static void saveimage(int num) { fz_image *image = NULL; fz_pixmap *pix = NULL; pdf_obj *ref; char buf[32]; ref = pdf_new_indirect(ctx, doc, num, 0); fz_var(image); fz_var(pix); fz_try(ctx) { /* TODO: detect DCTD and save as jpeg */ image = pdf_load_image(ctx, doc, ref); pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); snprintf(buf, sizeof(buf), "img-%04d", num); writepixmap(ctx, pix, buf, dorgb); } fz_always(ctx) { fz_drop_image(ctx, image); fz_drop_pixmap(ctx, pix); pdf_drop_obj(ctx, ref); } fz_catch(ctx) fz_rethrow(ctx); }
static void res_image_get_md5(fz_context *ctx, fz_image *image, unsigned char *digest) { fz_pixmap *pixmap; fz_md5 state; pixmap = fz_get_pixmap_from_image(ctx, image, 0, 0); fz_md5_init(&state); fz_md5_update(&state, pixmap->samples, pixmap->w * pixmap->h * pixmap->n); fz_md5_final(&state, digest); fz_drop_pixmap(ctx, pixmap); }
static void fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; while (dev->resolved == 0) /* So we can break out */ { fz_compressed_buffer *buffer; if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace)) break; if ((dev->options & FZ_TEST_OPT_IMAGES) == 0) { /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ if (*dev->is_color == 0) *dev->is_color = 1; dev->resolved = 1; if (dev->passthrough == NULL) fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } buffer = fz_compressed_image_buffer(ctx, image); if (buffer && image->bpc == 8) { fz_stream *stream = fz_open_compressed_buffer(ctx, buffer); fz_try(ctx) fz_test_fill_compressed_8bpc_image(ctx, dev, image, stream, color_params); fz_always(ctx) fz_drop_stream(ctx, stream); fz_catch(ctx) fz_rethrow(ctx); } else { fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); if (pix == NULL) /* Should never happen really, but... */ break; fz_try(ctx) fz_test_fill_other_image(ctx, dev, pix, color_params); fz_always(ctx) fz_drop_pixmap(ctx, pix); fz_catch(ctx) fz_rethrow(ctx); } break; } if (dev->passthrough) fz_fill_image(ctx, dev->passthrough, image, ctm, alpha, color_params); }
static void fz_unblend_masked_tile(fz_context *ctx, fz_pixmap *tile, fz_image *image) { fz_pixmap *mask = fz_get_pixmap_from_image(ctx, image->mask, NULL, NULL, NULL, NULL); unsigned char *s = mask->samples; unsigned char *d = tile->samples; int n = tile->n; int k; int sstride = mask->stride - mask->w * mask->n; int dstride = tile->stride - tile->w * tile->n; int h = mask->h; if (tile->w != mask->w || tile->h != mask->h) { fz_warn(ctx, "mask must be of same size as image for /Matte"); fz_drop_pixmap(ctx, mask); return; } if (mask->w != 0) { while (h--) { int w = mask->w; do { if (*s == 0) for (k = 0; k < image->n; k++) d[k] = image->colorkey[k]; else for (k = 0; k < image->n; k++) d[k] = fz_clampi(image->colorkey[k] + (d[k] - image->colorkey[k]) * 255 / *s, 0, 255); s++; d += n; } while (--w); s += sstride; d += dstride; } } fz_drop_pixmap(ctx, mask); }
static void res_image_get_md5(fz_context *ctx, fz_image *image, unsigned char *digest) { fz_pixmap *pixmap; fz_md5 state; int h; unsigned char *d; pixmap = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); fz_md5_init(&state); d = pixmap->samples; h = pixmap->h; while (h--) { fz_md5_update(&state, d, pixmap->w * pixmap->n); d += pixmap->stride; } fz_md5_final(&state, digest); fz_drop_pixmap(ctx, pixmap); }
static void fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params) { fz_test_device *dev = (fz_test_device*)dev_; while (dev->resolved == 0) /* So we can break out */ { fz_pixmap *pix; unsigned int count, i, k, h, sa, ss; unsigned char *s; fz_compressed_buffer *buffer; if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace)) break; if ((dev->options & FZ_TEST_OPT_IMAGES) == 0) { /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ if (*dev->is_color == 0) *dev->is_color = 1; dev->resolved = 1; if (dev->passthrough == NULL) fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } buffer = fz_compressed_image_buffer(ctx, image); if (buffer && image->bpc == 8) { fz_stream *stream = fz_open_compressed_buffer(ctx, buffer); count = (unsigned int)image->w * (unsigned int)image->h; if (image->colorspace == fz_device_rgb(ctx)) { int threshold_u8 = dev->threshold * 255; for (i = 0; i < count; i++) { int r = fz_read_byte(ctx, stream); int g = fz_read_byte(ctx, stream); int b = fz_read_byte(ctx, stream); if (is_rgb_color_u8(threshold_u8, r, g, b)) { *dev->is_color = 1; dev->resolved = 1; fz_drop_stream(ctx, stream); if (dev->passthrough == NULL) fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } } } else { fz_color_converter cc; unsigned int n = (unsigned int)image->n; fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), image->colorspace, color_params); fz_try(ctx) { for (i = 0; i < count; i++) { float cs[FZ_MAX_COLORS]; float ds[FZ_MAX_COLORS]; for (k = 0; k < n; k++) cs[k] = fz_read_byte(ctx, stream) / 255.0f; cc.convert(ctx, &cc, ds, cs); if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) { *dev->is_color = 1; dev->resolved = 1; if (dev->passthrough == NULL) { fz_drop_stream(ctx, stream); fz_fin_cached_color_converter(ctx, &cc); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); } break; } } } fz_always(ctx) fz_fin_cached_color_converter(ctx, &cc); fz_catch(ctx) fz_rethrow(ctx); } fz_drop_stream(ctx, stream); break; } pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); if (pix == NULL) /* Should never happen really, but... */ break; count = pix->w; h = pix->h; s = pix->samples; sa = pix->alpha; ss = pix->stride - pix->w * pix->n; if (pix->colorspace == fz_device_rgb(ctx)) { int threshold_u8 = dev->threshold * 255; while (h--) { for (i = 0; i < count; i++) { if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) { *dev->is_color = 1; dev->resolved = 1; if (dev->passthrough == NULL) { fz_drop_pixmap(ctx, pix); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); } break; } s += 3 + sa; } s += ss; } } else { fz_color_converter cc; unsigned int n = (unsigned int)pix->n-1; fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), pix->colorspace, color_params); fz_try(ctx) { while (h--) { for (i = 0; i < count; i++) { float cs[FZ_MAX_COLORS]; float ds[FZ_MAX_COLORS]; for (k = 0; k < n; k++) cs[k] = (*s++) / 255.0f; if (sa && *s++ == 0) continue; cc.convert(ctx, &cc, ds, cs); if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) { *dev->is_color = 1; dev->resolved = 1; if (dev->passthrough == NULL) { fz_fin_cached_color_converter(ctx, &cc); fz_drop_pixmap(ctx, pix); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); } break; } } s += ss; } } fz_always(ctx) fz_fin_cached_color_converter(ctx, &cc); fz_catch(ctx) fz_rethrow(ctx); } fz_drop_pixmap(ctx, pix); break; }
static void fz_test_fill_image(fz_context *ctx, fz_device *dev, const fz_image *image, const fz_matrix *ctm, float alpha) { fz_test_device *t = (fz_test_device*)dev; fz_pixmap *pix; unsigned int count, i, k; unsigned char *s; if (*t->is_color || !image->colorspace || image->colorspace == fz_device_gray(ctx)) return; if (image->buffer && image->bpc == 8) { fz_stream *stream = fz_open_compressed_buffer(ctx, image->buffer); count = (unsigned int)image->w * (unsigned int)image->h; if (image->colorspace == fz_device_rgb(ctx)) { int threshold_u8 = t->threshold * 255; for (i = 0; i < count; i++) { int r = fz_read_byte(ctx, stream); int g = fz_read_byte(ctx, stream); int b = fz_read_byte(ctx, stream); if (is_rgb_color_u8(threshold_u8, r, g, b)) { *t->is_color = 1; dev->hints |= FZ_IGNORE_IMAGE; fz_drop_stream(ctx, stream); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } } } else { fz_color_converter cc; unsigned int n = (unsigned int)image->n; fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), image->colorspace); for (i = 0; i < count; i++) { float cs[FZ_MAX_COLORS]; float ds[FZ_MAX_COLORS]; for (k = 0; k < n; k++) cs[k] = fz_read_byte(ctx, stream) / 255.0f; cc.convert(ctx, &cc, ds, cs); if (is_rgb_color(t->threshold, ds[0], ds[1], ds[2])) { *t->is_color = 1; dev->hints |= FZ_IGNORE_IMAGE; break; } } fz_fin_cached_color_converter(ctx, &cc); } fz_drop_stream(ctx, stream); return; } pix = fz_get_pixmap_from_image(ctx, image, 0, 0); if (pix == NULL) /* Should never happen really, but... */ return; count = (unsigned int)pix->w * (unsigned int)pix->h; s = pix->samples; if (pix->colorspace == fz_device_rgb(ctx)) { int threshold_u8 = t->threshold * 255; for (i = 0; i < count; i++) { if (s[3] != 0 && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) { *t->is_color = 1; dev->hints |= FZ_IGNORE_IMAGE; fz_drop_pixmap(ctx, pix); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } s += 4; } } else { fz_color_converter cc; unsigned int n = (unsigned int)pix->n-1; fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), pix->colorspace); for (i = 0; i < count; i++) { float cs[FZ_MAX_COLORS]; float ds[FZ_MAX_COLORS]; for (k = 0; k < n; k++) cs[k] = (*s++) / 255.0f; if (*s++ == 0) continue; cc.convert(ctx, &cc, ds, cs); if (is_rgb_color(t->threshold, ds[0], ds[1], ds[2])) { *t->is_color = 1; dev->hints |= FZ_IGNORE_IMAGE; fz_drop_pixmap(ctx, pix); fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); break; } } fz_fin_cached_color_converter(ctx, &cc); } fz_drop_pixmap(ctx, pix); }
cairo_surface_t* pdf_page_image_get_cairo(zathura_page_t* page, void* data, zathura_image_t* image, zathura_error_t* error) { mupdf_page_t* mupdf_page = data; if (page == NULL || mupdf_page == NULL || image == NULL || image->data == NULL) { if (error != NULL) { *error = ZATHURA_ERROR_INVALID_ARGUMENTS; } goto error_ret; } fz_image* mupdf_image = (fz_image*) image->data; fz_pixmap* pixmap = NULL; cairo_surface_t* surface = NULL; pixmap = fz_get_pixmap_from_image(mupdf_page->ctx, mupdf_image, NULL, NULL, 0, 0); if (pixmap == NULL) { goto error_free; } surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, mupdf_image->w, mupdf_image->h); if (surface == NULL) { goto error_free; } unsigned char* surface_data = cairo_image_surface_get_data(surface); int rowstride = cairo_image_surface_get_stride(surface); unsigned char* s = fz_pixmap_samples(mupdf_page->ctx, pixmap); unsigned int n = fz_pixmap_components(mupdf_page->ctx, pixmap); const int height = fz_pixmap_height(mupdf_page->ctx, pixmap); const int width = fz_pixmap_width(mupdf_page->ctx, pixmap); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { guchar* p = surface_data + y * rowstride + x * 4; // RGB if (n == 4) { p[0] = s[2]; p[1] = s[1]; p[2] = s[0]; // Gray-scale or mask } else { p[0] = s[0]; p[1] = s[0]; p[2] = s[0]; } s += n; } } fz_drop_pixmap(mupdf_page->ctx, pixmap); return surface; error_free: if (pixmap != NULL) { fz_drop_pixmap(mupdf_page->ctx, pixmap); } if (surface != NULL) { cairo_surface_destroy(surface); } error_ret: return NULL; }
pdf_obj * pdf_add_image(fz_context *ctx, pdf_document *doc, fz_image *image) { fz_pixmap *pixmap = NULL; pdf_obj *imobj = NULL; pdf_obj *dp; fz_buffer *buffer = NULL; pdf_obj *imref = NULL; fz_compressed_buffer *cbuffer; unsigned char digest[16]; int i, n; /* If we can maintain compression, do so */ cbuffer = fz_compressed_image_buffer(ctx, image); fz_var(pixmap); fz_var(buffer); fz_var(imobj); fz_var(imref); /* Check if the same image already exists in this doc. */ imref = pdf_find_image_resource(ctx, doc, image, digest); if (imref) return imref; imobj = pdf_add_new_dict(ctx, doc, 3); fz_try(ctx) { dp = pdf_dict_put_dict(ctx, imobj, PDF_NAME(DecodeParms), 3); pdf_dict_put(ctx, imobj, PDF_NAME(Type), PDF_NAME(XObject)); pdf_dict_put(ctx, imobj, PDF_NAME(Subtype), PDF_NAME(Image)); if (cbuffer) { fz_compression_params *cp = &cbuffer->params; switch (cp ? cp->type : FZ_IMAGE_UNKNOWN) { default: goto raw_or_unknown_compression; case FZ_IMAGE_JPEG: if (cp->u.jpeg.color_transform != -1) pdf_dict_put_int(ctx, dp, PDF_NAME(ColorTransform), cp->u.jpeg.color_transform); pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(DCTDecode)); break; case FZ_IMAGE_JPX: if (cp->u.jpx.smask_in_data) pdf_dict_put_int(ctx, dp, PDF_NAME(SMaskInData), cp->u.jpx.smask_in_data); pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(JPXDecode)); break; case FZ_IMAGE_FAX: if (cp->u.fax.columns) pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.fax.columns); if (cp->u.fax.rows) pdf_dict_put_int(ctx, dp, PDF_NAME(Rows), cp->u.fax.rows); if (cp->u.fax.k) pdf_dict_put_int(ctx, dp, PDF_NAME(K), cp->u.fax.k); if (cp->u.fax.end_of_line) pdf_dict_put_bool(ctx, dp, PDF_NAME(EndOfLine), cp->u.fax.end_of_line); if (cp->u.fax.encoded_byte_align) pdf_dict_put_bool(ctx, dp, PDF_NAME(EncodedByteAlign), cp->u.fax.encoded_byte_align); if (cp->u.fax.end_of_block) pdf_dict_put_bool(ctx, dp, PDF_NAME(EndOfBlock), cp->u.fax.end_of_block); if (cp->u.fax.black_is_1) pdf_dict_put_bool(ctx, dp, PDF_NAME(BlackIs1), cp->u.fax.black_is_1); if (cp->u.fax.damaged_rows_before_error) pdf_dict_put_int(ctx, dp, PDF_NAME(DamagedRowsBeforeError), cp->u.fax.damaged_rows_before_error); pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(CCITTFaxDecode)); break; case FZ_IMAGE_FLATE: if (cp->u.flate.columns) pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.flate.columns); if (cp->u.flate.colors) pdf_dict_put_int(ctx, dp, PDF_NAME(Colors), cp->u.flate.colors); if (cp->u.flate.predictor) pdf_dict_put_int(ctx, dp, PDF_NAME(Predictor), cp->u.flate.predictor); if (cp->u.flate.bpc) pdf_dict_put_int(ctx, dp, PDF_NAME(BitsPerComponent), cp->u.flate.bpc); pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(FlateDecode)); pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), image->bpc); break; case FZ_IMAGE_LZW: if (cp->u.lzw.columns) pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.lzw.columns); if (cp->u.lzw.colors) pdf_dict_put_int(ctx, dp, PDF_NAME(Colors), cp->u.lzw.colors); if (cp->u.lzw.predictor) pdf_dict_put_int(ctx, dp, PDF_NAME(Predictor), cp->u.lzw.predictor); if (cp->u.lzw.early_change) pdf_dict_put_int(ctx, dp, PDF_NAME(EarlyChange), cp->u.lzw.early_change); if (cp->u.lzw.bpc) pdf_dict_put_int(ctx, dp, PDF_NAME(BitsPerComponent), cp->u.lzw.bpc); pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(LZWDecode)); break; case FZ_IMAGE_RLD: pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(RunLengthDecode)); break; } if (!pdf_dict_len(ctx, dp)) pdf_dict_del(ctx, imobj, PDF_NAME(DecodeParms)); buffer = fz_keep_buffer(ctx, cbuffer->buffer); if (image->use_decode) { pdf_obj *ary = pdf_dict_put_array(ctx, imobj, PDF_NAME(Decode), image->n * 2); for (i = 0; i < image->n * 2; ++i) pdf_array_push_real(ctx, ary, image->decode[i]); } } else { unsigned int size; int h; unsigned char *d, *s; raw_or_unknown_compression: /* Currently, set to maintain resolution; should we consider * subsampling here according to desired output res? */ pixmap = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL); n = pixmap->n - pixmap->alpha - pixmap->s; /* number of colorants */ if (n == 0) n = 1; /* treat pixmaps with only alpha or spots as grayscale */ size = image->w * n; h = image->h; s = pixmap->samples; d = fz_malloc(ctx, size * h); buffer = fz_new_buffer_from_data(ctx, d, size * h); if (n == pixmap->n) { /* If we use all channels, we can copy the data as is. */ while (h--) { memcpy(d, s, size); d += size; s += pixmap->stride; } } else { /* Need to remove the alpha and spot planes. */ /* TODO: extract alpha plane to a soft mask. */ /* TODO: convert spots to colors. */ int line_skip = pixmap->stride - pixmap->w * pixmap->n; int skip = pixmap->n - n; while (h--) { int w = pixmap->w; while (w--) { int k; for (k = 0; k < n; ++k) *d++ = *s++; s += skip; } s += line_skip; } } } pdf_dict_put_int(ctx, imobj, PDF_NAME(Width), pixmap ? pixmap->w : image->w); pdf_dict_put_int(ctx, imobj, PDF_NAME(Height), pixmap ? pixmap->h : image->h); if (image->imagemask) { pdf_dict_put_bool(ctx, imobj, PDF_NAME(ImageMask), 1); } else { fz_colorspace *cs; pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), image->bpc); cs = pixmap ? pixmap->colorspace : image->colorspace; switch (fz_colorspace_type(ctx, cs)) { case FZ_COLORSPACE_INDEXED: { fz_colorspace *basecs; unsigned char *lookup = NULL; int high = 0; int basen; pdf_obj *arr; lookup = fz_indexed_colorspace_palette(ctx, cs, &high); basecs = fz_colorspace_base(ctx, cs); basen = fz_colorspace_n(ctx, basecs); arr = pdf_dict_put_array(ctx, imobj, PDF_NAME(ColorSpace), 4); pdf_array_push(ctx, arr, PDF_NAME(Indexed)); switch (fz_colorspace_type(ctx, basecs)) { case FZ_COLORSPACE_GRAY: pdf_array_push(ctx, arr, PDF_NAME(DeviceGray)); break; case FZ_COLORSPACE_RGB: pdf_array_push(ctx, arr, PDF_NAME(DeviceRGB)); break; case FZ_COLORSPACE_CMYK: pdf_array_push(ctx, arr, PDF_NAME(DeviceCMYK)); break; default: // TODO: convert to RGB! fz_throw(ctx, FZ_ERROR_GENERIC, "only indexed Gray, RGB, and CMYK colorspaces supported"); break; } pdf_array_push_int(ctx, arr, high); pdf_array_push_string(ctx, arr, (char *) lookup, basen * (high + 1)); } break; case FZ_COLORSPACE_NONE: case FZ_COLORSPACE_GRAY: pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceGray)); break; case FZ_COLORSPACE_RGB: pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceRGB)); break; case FZ_COLORSPACE_CMYK: pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceCMYK)); break; default: // TODO: convert to RGB! fz_throw(ctx, FZ_ERROR_GENERIC, "only Gray, RGB, and CMYK colorspaces supported"); break; } } if (image->mask) { pdf_dict_put_drop(ctx, imobj, PDF_NAME(SMask), pdf_add_image(ctx, doc, image->mask)); } pdf_update_stream(ctx, doc, imobj, buffer, 1); /* Add ref to our image resource hash table. */ imref = pdf_insert_image_resource(ctx, doc, digest, imobj); } fz_always(ctx) { fz_drop_pixmap(ctx, pixmap); fz_drop_buffer(ctx, buffer); pdf_drop_obj(ctx, imobj); } fz_catch(ctx) fz_rethrow(ctx); return imref; }