fz_pixmap * fz_load_jxr(fz_context *ctx, unsigned char *data, size_t size) { struct info info = { 0 }; fz_pixmap *image = NULL; fz_var(image); jxr_read_image(ctx, data, size, &info, 0); image = fz_new_pixmap(ctx, info.cspace, info.width, info.height, NULL, 1); image->xres = info.xres; image->yres = info.yres; fz_try(ctx) { fz_unpack_tile(ctx, image, info.samples, fz_colorspace_n(ctx, info.cspace) + 1, 8, info.stride, 0); if (info.has_alpha && !info.has_premul) fz_premultiply_pixmap(ctx, image); } fz_always(ctx) { fz_free(ctx, info.samples); fz_drop_colorspace(ctx, info.cspace); } fz_catch(ctx) { fz_drop_pixmap(ctx, image); fz_rethrow(ctx); } return image; }
static fz_pixmap * fz_copy_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap) { fz_pixmap *pixmap; int y; pixmap = fz_new_pixmap(ctx, NULL, bitmap->width, bitmap->rows); pixmap->x = left; pixmap->y = top - bitmap->rows; if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { for (y = 0; y < pixmap->h; y++) { unsigned char *out = pixmap->samples + (unsigned int)(y * pixmap->w); unsigned char *in = bitmap->buffer + (unsigned int)((pixmap->h - y - 1) * bitmap->pitch); unsigned char bit = 0x80; int w = pixmap->w; while (w--) { *out++ = (*in & bit) ? 255 : 0; bit >>= 1; if (bit == 0) { bit = 0x80; in++; } } } } else { for (y = 0; y < pixmap->h; y++)
fz_pixmap * fz_new_pixmap_with_bbox(fz_context *ctx, fz_colorspace *colorspace, fz_bbox r) { fz_pixmap *pixmap; pixmap = fz_new_pixmap(ctx, colorspace, r.x1 - r.x0, r.y1 - r.y0); pixmap->x = r.x0; pixmap->y = r.y0; return pixmap; }
QImage Document::RenderPage (int num, double xRes, double yRes) { auto page = WrapPage (pdf_load_page (MuDoc_, num), MuDoc_); if (!page) return QImage (); #if MUPDF_VERSION < 0x0102 const auto& rect = pdf_bound_page (MuDoc_, page.get ()); #else fz_rect rect; pdf_bound_page (MuDoc_, page.get (), &rect); #endif auto px = fz_new_pixmap (MuCtx_, fz_device_bgr, xRes * (rect.x1 - rect.x0), yRes * (rect.y1 - rect.y0)); fz_clear_pixmap (MuCtx_, px); auto dev = fz_new_draw_device (MuCtx_, px); #if MUPDF_VERSION < 0x0102 pdf_run_page (MuDoc_, page.get (), dev, fz_scale (xRes, yRes), NULL); #else fz_matrix matrix; pdf_run_page (MuDoc_, page.get (), dev, fz_scale (&matrix, xRes, yRes), NULL); #endif fz_free_device (dev); const int pxWidth = fz_pixmap_width (MuCtx_, px); const int pxHeight = fz_pixmap_height (MuCtx_, px); auto samples = fz_pixmap_samples (MuCtx_, px); QImage temp (samples, pxWidth, pxHeight, QImage::Format_ARGB32); QImage img (QSize (pxWidth, pxHeight), QImage::Format_ARGB32); for (int y = 0; y < pxHeight; ++y) { auto target = reinterpret_cast<QRgb*> (img.scanLine (y)); const auto source = reinterpret_cast<QRgb*> (temp.scanLine (y)); std::memcpy (target, source, sizeof (source [0]) * pxWidth); } fz_drop_pixmap (MuCtx_, px); temp = QImage (QSize (pxWidth, pxHeight), QImage::Format_ARGB32); QPainter p; p.begin (&temp); p.fillRect (QRect (QPoint (0, 0), temp.size ()), Qt::white); p.drawImage (0, 0, img); p.end (); return temp; }
/* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1333 */ static fz_pixmap * decomp_image_banded(fz_context *ctx, fz_stream *stm, fz_image *image, int indexed, int l2factor, int native_l2factor) { fz_pixmap *tile = NULL, *part = NULL; int w = (image->w + (1 << l2factor) - 1) >> l2factor; int h = (image->h + (1 << l2factor) - 1) >> l2factor; int part_h, orig_h = image->h; int band = 1 << fz_maxi(8, l2factor); fz_var(tile); fz_var(part); fz_try(ctx) { fz_colorspace *cs = image->colorspace; if (indexed) cs = *(fz_colorspace **)cs->data; // cf. struct indexed in res_colorspace.c tile = fz_new_pixmap(ctx, cs, w, h); tile->interpolate = image->interpolate; tile->has_alpha = 0; /* SumatraPDF: allow optimizing non-alpha pixmaps */ /* decompress the image in bands of 256 lines */ for (part_h = h; part_h > 0; part_h -= band >> l2factor) { image->h = part_h > band >> l2factor ? band : ((orig_h - 1) % band) + 1; part = fz_decomp_image_from_stream(ctx, fz_keep_stream(stm), image, -1 - indexed, l2factor, native_l2factor); memcpy(tile->samples + (h - part_h) * tile->w * tile->n, part->samples, part->h * part->w * part->n); tile->has_alpha |= part->has_alpha; /* SumatraPDF: allow optimizing non-alpha pixmaps */ fz_drop_pixmap(ctx, part); part = NULL; } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ if (image->usecolorkey && image->mask) fz_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { image->h = orig_h; fz_close(stm); } fz_catch(ctx) { fz_drop_pixmap(ctx, part); fz_drop_pixmap(ctx, tile); fz_rethrow(ctx); } return tile; }
fz_pixmap * fz_load_jpeg(fz_context *ctx, unsigned char *rbuf, int rlen) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr err; struct jpeg_source_mgr src; unsigned char *row[1], *sp, *dp; fz_colorspace *colorspace; unsigned int x; int k; fz_pixmap *image = NULL; fz_var(image); fz_var(row); row[0] = NULL; fz_try(ctx) { cinfo.client_data = ctx; cinfo.err = jpeg_std_error(&err); err.error_exit = error_exit; jpeg_create_decompress(&cinfo); cinfo.src = &src; src.init_source = init_source; src.fill_input_buffer = fill_input_buffer; src.skip_input_data = skip_input_data; src.resync_to_restart = jpeg_resync_to_restart; src.term_source = term_source; src.next_input_byte = rbuf; src.bytes_in_buffer = rlen; jpeg_read_header(&cinfo, 1); jpeg_start_decompress(&cinfo); if (cinfo.output_components == 1) colorspace = fz_device_gray; else if (cinfo.output_components == 3) colorspace = fz_device_rgb; else if (cinfo.output_components == 4) colorspace = fz_device_cmyk; else fz_throw(ctx, "bad number of components in jpeg: %d", cinfo.output_components); image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height); if (cinfo.density_unit == 1) { image->xres = cinfo.X_density; image->yres = cinfo.Y_density; } else if (cinfo.density_unit == 2) { image->xres = cinfo.X_density * 254 / 100; image->yres = cinfo.Y_density * 254 / 100; } if (image->xres <= 0) image->xres = 72; if (image->yres <= 0) image->yres = 72; fz_clear_pixmap(ctx, image); row[0] = fz_malloc(ctx, cinfo.output_components * cinfo.output_width); dp = image->samples; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, row, 1); sp = row[0]; for (x = 0; x < cinfo.output_width; x++) { for (k = 0; k < cinfo.output_components; k++) *dp++ = *sp++; *dp++ = 255; } } } fz_always(ctx) { fz_free(ctx, row[0]); row[0] = NULL; fz_try(ctx) { /* Annoyingly, jpeg_finish_decompress can throw */ jpeg_finish_decompress(&cinfo); } fz_catch(ctx) { /* Ignore any errors here */ } jpeg_destroy_decompress(&cinfo); } fz_catch(ctx) { fz_drop_pixmap(ctx, image); fz_rethrow(ctx); } return image; }
static fz_pixmap * pam_binary_read_image(fz_context *ctx, struct info *pnm, unsigned char *p, unsigned char *e, int onlymeta) { fz_pixmap *img = NULL; int bitmap = 0; p = pam_binary_read_header(ctx, pnm, p, e); if (pnm->tupletype == NULL) switch (pnm->depth) { case 1: pnm->tupletype = fz_strdup(ctx, "BLACKANDWHITE"); break; case 2: pnm->tupletype = fz_strdup(ctx, "GRAYSCALE_ALPHA"); break; case 3: pnm->tupletype = fz_strdup(ctx, "RGB"); break; case 4: pnm->tupletype = fz_strdup(ctx, "CMYK"); break; case 5: pnm->tupletype = fz_strdup(ctx, "CMYK_ALPHA"); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "cannot guess tupletype based on depth in pnm image"); } if (!strcmp(pnm->tupletype, "BLACKANDWHITE")) { if (pnm->depth != 1) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for b/w pnm image"); if (pnm->maxval == 1) bitmap = 1; else if (pnm->maxval < 2 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for grayscale pnm image"); pnm->cs = fz_device_gray(ctx); } else if (!strcmp(pnm->tupletype, "GRAYSCALE")) { if (pnm->maxval < 2 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for grayscale pnm image"); if (pnm->depth != 1) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for grayscale pnm image"); pnm->cs = fz_device_gray(ctx); } else if (!strcmp(pnm->tupletype, "GRAYSCALE_ALPHA")) { if (pnm->maxval < 2 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for grayscale pnm image with alpha"); if (pnm->depth != 2) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for grayscale pnm image with alpha"); pnm->cs = fz_device_gray(ctx); pnm->alpha = 1; } else if (!strcmp(pnm->tupletype, "RGB")) { if (pnm->maxval < 1 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for rgb pnm image"); if (pnm->depth != 3) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for rgb pnm image"); pnm->cs = fz_device_rgb(ctx); } else if (!strcmp(pnm->tupletype, "RGB_ALPHA")) { if (pnm->maxval < 1 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for rgb pnm image with alpha"); if (pnm->depth != 4) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for rgb pnm image with alpha"); pnm->cs = fz_device_rgb(ctx); pnm->alpha = 1; } else if (!strcmp(pnm->tupletype, "CMYK")) { if (pnm->maxval < 1 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for cmyk pnm image"); if (pnm->depth != 4) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for cmyk pnm image"); pnm->cs = fz_device_cmyk(ctx); } else if (!strcmp(pnm->tupletype, "CMYK_ALPHA")) { if (pnm->maxval < 1 || pnm->maxval > 65535) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range for cmyk pnm image with alpha"); if (pnm->depth != 5) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of range for cmyk pnm image with alpha"); pnm->cs = fz_device_cmyk(ctx); pnm->alpha = 1; } else { fz_free(ctx, pnm->tupletype); fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported tupletype"); } fz_free(ctx, pnm->tupletype); if (!onlymeta) { unsigned char *dp; int x, y, k; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, pnm->alpha); dp = img->samples; if (bitmap) { if (e - p < pnm->height * pnm->width * img->n) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) { if (*p) *dp = 0x00; else *dp = 0xff; dp++; p++; } } else { if (pnm->maxval == 255) { if (e - p < pnm->height * pnm->width * img->n) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->n; k++) *dp++ = *p++; } else if (pnm->maxval < 256) { if (e - p < pnm->height * pnm->width * img->n) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { if (e - p < pnm->height * pnm->width * img->n * 2) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } } } return img; }
static fz_pixmap * pnm_binary_read_image(fz_context *ctx, struct info *pnm, unsigned char *p, unsigned char *e, int onlymeta, int bitmap) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 1); if (pnm->maxval < 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); } if (!onlymeta) { unsigned char *dp; int x, y, k; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, 0); dp = img->samples; if (bitmap) { for (y = 0; y < pnm->height; y++) { for (x = 0; x < pnm->width; x++) { if (*p & (1 << (7 - (x & 0x7)))) *dp = 0x00; else *dp = 0xff; dp++; if ((x & 0x7) == 7) p++; } if (pnm->width & 0x7) p++; } } else { if (pnm->maxval == 255) { for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) *dp++ = *p++; } else if (pnm->maxval < 256) { for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } } } return img; }
static fz_pixmap * pnm_ascii_read_image(fz_context *ctx, struct info *pnm, unsigned char *p, unsigned char *e, int onlymeta, int bitmap) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 0); if (pnm->maxval < 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); } if (!onlymeta) { unsigned char *dp; int x, y, k; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, 0); dp = img->samples; if (bitmap) { for (y = 0; y < pnm->height; y++) { for (x = 0; x < pnm->width; x++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); if (v == 0) *dp = 0x00; else *dp = 0xff; dp++; } } } else { for (y = 0; y < pnm->height; y++) for (x = 0; x < pnm->width; x++) for (k = 0; k < img->colorspace->n; k++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); v = fz_clampi(v, 0, pnm->maxval); *dp++ = map_color(ctx, v, pnm->maxval, 255); } } } return img; }
fz_pixmap * fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, int in_line, int indexed, int l2factor, int native_l2factor) { fz_pixmap *tile = NULL; int stride, len, i; unsigned char *samples = NULL; int f = 1<<native_l2factor; int w = (image->w + f-1) >> native_l2factor; int h = (image->h + f-1) >> native_l2factor; fz_var(tile); fz_var(samples); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->colorspace, w, h); tile->interpolate = image->interpolate; stride = (w * image->n * image->bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); len = fz_read(stm, samples, h * stride); if (len < 0) { fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ if (in_line) { unsigned char tbuf[512]; fz_try(ctx) { int tlen = fz_read(stm, tbuf, sizeof tbuf); if (tlen > 0) fz_warn(ctx, "ignoring garbage at end of image"); } fz_catch(ctx) { fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); fz_warn(ctx, "ignoring error at end of image"); } } /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(tile, samples, image->n, image->bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; if (image->usecolorkey) fz_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, image->decode, (1 << image->bpc) - 1); conv = fz_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(tile, image->decode); } } fz_always(ctx) { fz_close(stm); } fz_catch(ctx) { if (tile) fz_drop_pixmap(ctx, tile); fz_free(ctx, samples); fz_rethrow(ctx); } /* Now apply any extra subsampling required */ if (l2factor - native_l2factor > 0) { if (l2factor - native_l2factor > 8) l2factor = native_l2factor + 8; fz_subsample_pixmap(ctx, tile, l2factor - native_l2factor); } return tile; }
static fz_pixmap * fz_load_jxr_or_info(fz_context *ctx, unsigned char *data, int size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep) { #ifdef _WIN32 int info_only = wp && hp && xresp && yresp && cspacep; fz_pixmap *pix = NULL; IStream *stream = NULL; IWICImagingFactory *factory = NULL; IWICBitmapDecoder *decoder = NULL; IWICFormatConverter *converter = NULL; IWICBitmapFrameDecode *src_frame = NULL; IWICBitmapSource *src_bitmap = NULL; int codec_available = 0; LARGE_INTEGER zero = { 0 }; UINT width, height; double xres, yres; ULONG written; HRESULT hr; hr = CoInitialize(NULL); if (FAILED(hr)) fz_throw(ctx, "JPEG-XR codec is not available"); #define Check(hr) if (FAILED(hr)) goto CleanUp Check(CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_ALL, &IID_IWICImagingFactory, (void **)&factory)); Check(CreateStreamOnHGlobal(NULL, TRUE, &stream)); Check(IStream_Write(stream, data, (ULONG)size, &written)); Check(IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL)); Check(IWICImagingFactory_CreateDecoderFromStream(factory, stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder)); Check(IWICImagingFactory_CreateFormatConverter(factory, &converter)); Check(IWICBitmapDecoder_GetFrame(decoder, 0, &src_frame)); Check(IUnknown_QueryInterface(src_frame, &IID_IWICBitmapSource, &src_bitmap)); Check(IWICFormatConverter_Initialize(converter, src_bitmap, &GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom)); Check(IWICFormatConverter_GetSize(converter, &width, &height)); Check(IWICFormatConverter_GetResolution(converter, &xres, &yres)); #undef Check codec_available = 1; if (info_only) { *cspacep = fz_device_bgr; *wp = width; *hp = height; *xresp = (int)(xres + 0.5); *yresp = (int)(yres + 0.5); } else { fz_try(ctx) { pix = fz_new_pixmap(ctx, fz_device_bgr, width, height); } fz_catch(ctx) { pix = NULL; goto CleanUp; } hr = IWICFormatConverter_CopyPixels(converter, NULL, pix->w * pix->n, pix->w * pix->h * pix->n, pix->samples); if (FAILED(hr)) { fz_drop_pixmap(ctx, pix); pix = NULL; goto CleanUp; } pix->xres = (int)(xres + 0.5); pix->yres = (int)(yres + 0.5); } CleanUp: #define Release(unk) if (unk) IUnknown_Release(unk) Release(src_bitmap); Release(converter); Release(src_frame); Release(decoder); Release(factory); Release(stream); #undef Release CoUninitialize(); if (codec_available) { if (!pix && !info_only) fz_throw(ctx, "JPEG-XR codec failed to decode the image"); return pix; } #endif fz_throw(ctx, "JPEG-XR codec is not available"); return NULL; }
static fz_pixmap * decomp_image_from_stream(fz_context *ctx, fz_stream *stm, pdf_image *image, int in_line, int indexed, int l2factor, int native_l2factor, int cache) { fz_pixmap *tile = NULL; fz_pixmap *existing_tile; int stride, len, i; unsigned char *samples = NULL; int f = 1<<native_l2factor; int w = (image->base.w + f-1) >> native_l2factor; int h = (image->base.h + f-1) >> native_l2factor; pdf_image_key *key = NULL; fz_var(tile); fz_var(samples); fz_var(key); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.colorspace, w, h); tile->interpolate = image->interpolate; stride = (w * image->n * image->bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); len = fz_read(stm, samples, h * stride); if (len < 0) { fz_throw(ctx, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1980 */ if (in_line && 0) { unsigned char tbuf[512]; fz_try(ctx) { int tlen = fz_read(stm, tbuf, sizeof tbuf); if (tlen > 0) fz_warn(ctx, "ignoring garbage at end of image"); } fz_catch(ctx) { fz_warn(ctx, "ignoring error at end of image"); } } /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(tile, samples, image->n, image->bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; if (image->usecolorkey) pdf_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, image->decode, (1 << image->bpc) - 1); conv = pdf_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(tile, image->decode); } }
fz_pixmap * fz_load_jpeg(fz_context *ctx, unsigned char *rbuf, int rlen) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr_jmp err; struct jpeg_source_mgr src; unsigned char *row[1], *sp, *dp; fz_colorspace *colorspace; unsigned int x; int k; fz_pixmap *image = NULL; fz_var(image); if (setjmp(err.env)) { /* SumatraPDF: prevent memory leak */ if (image) { fz_drop_pixmap(ctx, image); image = NULL; jpeg_finish_decompress(&cinfo); } jpeg_destroy_decompress(&cinfo); fz_throw(ctx, "jpeg error: %s", err.msg); } cinfo.err = jpeg_std_error(&err.super); err.super.error_exit = error_exit; jpeg_create_decompress(&cinfo); cinfo.src = &src; src.init_source = init_source; src.fill_input_buffer = fill_input_buffer; src.skip_input_data = skip_input_data; src.resync_to_restart = jpeg_resync_to_restart; src.term_source = term_source; src.next_input_byte = rbuf; src.bytes_in_buffer = rlen; jpeg_read_header(&cinfo, 1); jpeg_start_decompress(&cinfo); if (cinfo.output_components == 1) colorspace = fz_device_gray; else if (cinfo.output_components == 3) colorspace = fz_device_rgb; else if (cinfo.output_components == 4) colorspace = fz_device_cmyk; else { /* SumatraPDF: prevent memory leak */ jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fz_throw(ctx, "bad number of components in jpeg: %d", cinfo.output_components); } fz_try(ctx) { image = fz_new_pixmap(ctx, colorspace, cinfo.output_width, cinfo.output_height); } fz_catch(ctx) { jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fz_throw(ctx, "out of memory"); } if (cinfo.density_unit == 1) { image->xres = cinfo.X_density; image->yres = cinfo.Y_density; } else if (cinfo.density_unit == 2) { image->xres = cinfo.X_density * 254 / 100; image->yres = cinfo.Y_density * 254 / 100; } /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1963 */ else if (cinfo.density_unit == 0) { extract_exif_resolution(rbuf, rlen, &image->xres, &image->yres); } if (image->xres <= 0) image->xres = 72; if (image->yres <= 0) image->yres = 72; fz_clear_pixmap(ctx, image); row[0] = fz_malloc(ctx, cinfo.output_components * cinfo.output_width); dp = image->samples; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, row, 1); sp = row[0]; for (x = 0; x < cinfo.output_width; x++) { for (k = 0; k < cinfo.output_components; k++) *dp++ = *sp++; *dp++ = 255; } } fz_free(ctx, row[0]); jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); image->has_alpha = 0; /* SumatraPDF: allow optimizing non-alpha pixmaps */ return image; }
static fz_image * pdf_load_image_imp(pdf_document *xref, pdf_obj *rdb, pdf_obj *dict, fz_stream *cstm, int forcemask) { fz_stream *stm = NULL; fz_image *image = NULL; pdf_obj *obj, *res; int w, h, bpc, n; int imagemask; int interpolate; int indexed; fz_image *mask = NULL; /* explicit mask/soft mask image */ int usecolorkey = 0; fz_colorspace *colorspace = NULL; float decode[FZ_MAX_COLORS * 2]; int colorkey[FZ_MAX_COLORS * 2]; int i; fz_context *ctx = xref->ctx; fz_var(stm); fz_var(mask); fz_var(image); fz_var(colorspace); fz_try(ctx) { /* special case for JPEG2000 images */ if (pdf_is_jpx_image(ctx, dict)) { image = pdf_load_jpx(xref, dict, forcemask); if (forcemask) { fz_pixmap *mask_pixmap; if (image->n != 2) { /* SumatraPDF: ignore invalid JPX softmasks */ fz_warn(ctx, "soft mask must be grayscale"); mask_pixmap = fz_new_pixmap(ctx, NULL, image->tile->w, image->tile->h); fz_clear_pixmap_with_value(ctx, mask_pixmap, 255); } else mask_pixmap = fz_alpha_from_gray(ctx, image->tile, 1); fz_drop_pixmap(ctx, image->tile); image->tile = mask_pixmap; } break; /* Out of fz_try */ } w = pdf_to_int(pdf_dict_getsa(dict, "Width", "W")); h = pdf_to_int(pdf_dict_getsa(dict, "Height", "H")); bpc = pdf_to_int(pdf_dict_getsa(dict, "BitsPerComponent", "BPC")); if (bpc == 0) bpc = 8; imagemask = pdf_to_bool(pdf_dict_getsa(dict, "ImageMask", "IM")); interpolate = pdf_to_bool(pdf_dict_getsa(dict, "Interpolate", "I")); indexed = 0; usecolorkey = 0; mask = NULL; if (imagemask) bpc = 1; if (w <= 0) fz_throw(ctx, "image width is zero (or less)"); if (h <= 0) fz_throw(ctx, "image height is zero (or less)"); if (bpc <= 0) fz_throw(ctx, "image depth is zero (or less)"); if (bpc > 16) fz_throw(ctx, "image depth is too large: %d", bpc); if (w > (1 << 16)) fz_throw(ctx, "image is too wide"); if (h > (1 << 16)) fz_throw(ctx, "image is too high"); obj = pdf_dict_getsa(dict, "ColorSpace", "CS"); if (obj && !imagemask && !forcemask) { /* colorspace resource lookup is only done for inline images */ if (pdf_is_name(obj)) { res = pdf_dict_get(pdf_dict_gets(rdb, "ColorSpace"), obj); if (res) obj = res; } colorspace = pdf_load_colorspace(xref, obj); if (!strcmp(colorspace->name, "Indexed")) indexed = 1; n = colorspace->n; } else { n = 1; } obj = pdf_dict_getsa(dict, "Decode", "D"); if (obj) { for (i = 0; i < n * 2; i++) decode[i] = pdf_to_real(pdf_array_get(obj, i)); } else { float maxval = indexed ? (1 << bpc) - 1 : 1; for (i = 0; i < n * 2; i++) decode[i] = i & 1 ? maxval : 0; } obj = pdf_dict_getsa(dict, "SMask", "Mask"); if (pdf_is_dict(obj)) { /* Not allowed for inline images or soft masks */ if (cstm) fz_warn(ctx, "Ignoring invalid inline image soft mask"); else if (forcemask) fz_warn(ctx, "Ignoring recursive image soft mask"); else mask = pdf_load_image_imp(xref, rdb, obj, NULL, 1); } else if (pdf_is_array(obj)) { usecolorkey = 1; for (i = 0; i < n * 2; i++) { if (!pdf_is_int(pdf_array_get(obj, i))) { fz_warn(ctx, "invalid value in color key mask"); usecolorkey = 0; } colorkey[i] = pdf_to_int(pdf_array_get(obj, i)); } } /* Now, do we load a ref, or do we load the actual thing? */ if (!cstm) { /* Just load the compressed image data now and we can * decode it on demand. */ int num = pdf_to_num(dict); int gen = pdf_to_gen(dict); fz_compressed_buffer *buffer = pdf_load_compressed_stream(xref, num, gen); image = fz_new_image(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, usecolorkey ? colorkey : NULL, buffer, mask); break; /* Out of fz_try */ } /* We need to decompress the image now */ if (cstm) { int stride = (w * n * bpc + 7) / 8; stm = pdf_open_inline_stream(xref, dict, stride * h, cstm, NULL); } else { stm = pdf_open_stream(xref, pdf_to_num(dict), pdf_to_gen(dict)); } image = fz_new_image(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, usecolorkey ? colorkey : NULL, NULL, mask); image->tile = fz_decomp_image_from_stream(ctx, stm, image, cstm != NULL, indexed, 0, 0); } fz_catch(ctx) { /* SumatraPDF: fix memory leak */ if (!image) fz_drop_colorspace(ctx, colorspace); else fz_drop_image(ctx, image); fz_rethrow(ctx); } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ fz_try(ctx) { obj = pdf_dict_getp(dict, "SMask/Matte"); if (pdf_is_array(obj) && image->mask) { assert(!image->usecolorkey); image->usecolorkey = 2; for (i = 0; i < n; i++) image->colorkey[i] = pdf_to_int(pdf_array_get(obj, i)); } } fz_catch(ctx) { fz_drop_image(ctx, image); fz_rethrow(ctx); } return image; }
static fz_pixmap * pam_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, const unsigned char **out) { fz_pixmap *img = NULL; int bitmap = 0; int minval = 1; int maxval = 65535; fz_var(img); p = pam_binary_read_header(ctx, pnm, p, e); if (pnm->tupletype == PAM_UNKNOWN) switch (pnm->depth) { case 1: pnm->tupletype = pnm->maxval == 1 ? PAM_BW : PAM_GRAY; break; case 2: pnm->tupletype = pnm->maxval == 1 ? PAM_BWA : PAM_GRAYA; break; case 3: pnm->tupletype = PAM_RGB; break; case 4: pnm->tupletype = PAM_CMYK; break; case 5: pnm->tupletype = PAM_CMYKA; break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "cannot guess tuple type based on depth in pnm image"); } if (pnm->tupletype == PAM_BW && pnm->maxval > 1) pnm->tupletype = PAM_GRAY; else if (pnm->tupletype == PAM_GRAY && pnm->maxval == 1) pnm->tupletype = PAM_BW; else if (pnm->tupletype == PAM_BWA && pnm->maxval > 1) pnm->tupletype = PAM_GRAYA; else if (pnm->tupletype == PAM_GRAYA && pnm->maxval == 1) pnm->tupletype = PAM_BWA; switch (pnm->tupletype) { case PAM_BWA: pnm->alpha = 1; /* fallthrough */ case PAM_BW: pnm->cs = fz_device_gray(ctx); maxval = 1; bitmap = 1; break; case PAM_GRAYA: pnm->alpha = 1; /* fallthrough */ case PAM_GRAY: pnm->cs = fz_device_gray(ctx); minval = 2; break; case PAM_RGBA: pnm->alpha = 1; /* fallthrough */ case PAM_RGB: pnm->cs = fz_device_rgb(ctx); break; case PAM_CMYKA: pnm->alpha = 1; /* fallthrough */ case PAM_CMYK: pnm->cs = fz_device_cmyk(ctx); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported tuple type"); } if (pnm->depth != fz_colorspace_n(ctx, pnm->cs) + pnm->alpha) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of tuple type range"); if (pnm->maxval < minval || pnm->maxval > maxval) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range"); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int packed; int w, h, n; w = pnm->width; h = pnm->height; n = fz_colorspace_n(ctx, pnm->cs) + pnm->alpha; /* some encoders incorrectly pack bits into bytes and invert the image */ packed = 0; if (pnm->maxval == 1) { const unsigned char *e_packed = p + w * h * n / 8; if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') e = e_packed; if (e - p < w * h * n) packed = 1; } if (packed && e - p < w * h * n / 8) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image"); if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2)) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); if (pnm->maxval == 255) p += n * w * h; else if (bitmap && packed) p += ((w + 7) / 8) * h; else if (bitmap) p += n * w * h; else if (pnm->maxval < 255) p += n * w * h; else p += 2 * n * w * h; } if (!onlymeta) { unsigned char *dp; int x, y, k, packed; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, pnm->alpha); fz_try(ctx) { dp = img->samples; w = img->w; h = img->h; n = img->n; /* some encoders incorrectly pack bits into bytes and invert the image */ packed = 0; if (pnm->maxval == 1) { const unsigned char *e_packed = p + w * h * n / 8; if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') e = e_packed; if (e - p < w * h * n) packed = 1; } if (packed && e - p < w * h * n / 8) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image"); if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2)) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); if (pnm->maxval == 255) memcpy(dp, p, w * h * n); else if (bitmap && packed) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) { for (k = 0; k < n; k++) { *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; if ((x & 0x7) == 7) p++; } if (w & 0x7) p++; } } else if (bitmap) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = *p++ ? 0xff : 0x00; } else if (pnm->maxval < 255) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } if (pnm->alpha) fz_premultiply_pixmap(ctx, img); } fz_catch(ctx) { fz_drop_pixmap(ctx, img); fz_rethrow(ctx); } }
static fz_pixmap * pnm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) { fz_pixmap *img = NULL; pnm->width = 0; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { pnm->height = 0; p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { pnm->height = 0; p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); pnm->maxval = 0; p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 1); } if (pnm->maxval <= 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int w = pnm->width; int h = pnm->height; int n = fz_colorspace_n(ctx, pnm->cs); if (pnm->maxval == 255) p += n * w * h; else if (bitmap) p += ((w + 7) / 8) * h; else if (pnm->maxval < 255) p += n * w * h; else p += 2 * n * w * h; } else { unsigned char *dp; int x, y, k; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); dp = img->samples; w = img->w; h = img->h; n = img->n; if (pnm->maxval == 255) { memcpy(dp, p, w * h * n); p += n * w * h; } else if (bitmap) { for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; if ((x & 0x7) == 7) p++; } if (w & 0x7) p++; } } else if (pnm->maxval < 255) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } } if (out) *out = p; return img; }
static fz_pixmap * pnm_ascii_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 0); } if (pnm->maxval <= 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int x, y, k; int w, h, n; w = pnm->width; h = pnm->height; n = fz_colorspace_n(ctx, pnm->cs); if (bitmap) { for (y = 0; y < h; y++) for (x = -1; x < w; x++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } } else { unsigned char *dp; int x, y, k; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); dp = img->samples; w = img->w; h = img->h; n = img->n; if (bitmap) { for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); *dp++ = v ? 0x00 : 0xff; } } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); v = fz_clampi(v, 0, pnm->maxval); *dp++ = map_color(ctx, v, pnm->maxval, 255); } } } if (out) *out = p; return img; }
fz_pixmap * fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_compressed_image *cimg, fz_irect *subarea, int indexed, int l2factor) { fz_image *image = &cimg->super; fz_pixmap *tile = NULL; size_t stride, len, i; unsigned char *samples = NULL; int f = 1<<l2factor; int w = image->w; int h = image->h; if (subarea) { int bpp = image->bpc * image->n; int mask; switch (bpp) { case 1: mask = 8*f; break; case 2: mask = 4*f; break; case 4: mask = 2*f; break; default: mask = (bpp & 7) == 0 ? f : 0; break; } if (mask != 0) { subarea->x0 &= ~(mask - 1); subarea->x1 = (subarea->x1 + mask - 1) & ~(mask - 1); } else { /* Awkward case - mask cannot be a power of 2. */ mask = bpp*f; switch (bpp) { case 3: case 5: case 7: case 9: case 11: case 13: case 15: default: mask *= 8; break; case 6: case 10: case 14: mask *= 4; break; case 12: mask *= 2; break; } subarea->x0 = (subarea->x0 / mask) * mask; subarea->x1 = ((subarea->x1 + mask - 1) / mask) * mask; } subarea->y0 &= ~(f - 1); if (subarea->x1 > image->w) subarea->x1 = image->w; subarea->y1 = (subarea->y1 + f - 1) & ~(f - 1); if (subarea->y1 > image->h) subarea->y1 = image->h; w = (subarea->x1 - subarea->x0); h = (subarea->y1 - subarea->y0); } w = (w + f - 1) >> l2factor; h = (h + f - 1) >> l2factor; fz_var(tile); fz_var(samples); fz_try(ctx) { int alpha = (image->colorspace == NULL); if (image->use_colorkey) alpha = 1; tile = fz_new_pixmap(ctx, image->colorspace, w, h, alpha); tile->interpolate = image->interpolate; stride = (w * image->n * image->bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); if (subarea) { int hh; unsigned char *s = samples; int stream_w = (image->w + f - 1)>>l2factor; size_t stream_stride = (stream_w * image->n * image->bpc + 7) / 8; int l_margin = subarea->x0 >> l2factor; int t_margin = subarea->y0 >> l2factor; int r_margin = (image->w + f - 1 - subarea->x1) >> l2factor; int b_margin = (image->h + f - 1 - subarea->y1) >> l2factor; int l_skip = (l_margin * image->n * image->bpc)/8; int r_skip = (r_margin * image->n * image->bpc + 7)/8; size_t t_skip = t_margin * stream_stride + l_skip; size_t b_skip = b_margin * stream_stride + r_skip; size_t l = fz_skip(ctx, stm, t_skip); len = 0; if (l == t_skip) { hh = h; do { l = fz_read(ctx, stm, s, stride); s += l; len += l; if (l < stride) break; if (--hh == 0) break; l = fz_skip(ctx, stm, r_skip + l_skip); if (l < (size_t)(r_skip + l_skip)) break; } while (1); (void)fz_skip(ctx, stm, r_skip + b_skip); } } else { len = fz_read(ctx, stm, samples, h * stride); } /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(ctx, tile, samples, image->n, image->bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; /* color keyed transparency */ if (image->use_colorkey && !image->mask) fz_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(ctx, tile, image->decode, (1 << image->bpc) - 1); conv = fz_expand_indexed_pixmap(ctx, tile, alpha); fz_drop_pixmap(ctx, tile); tile = conv; } else if (image->use_decode) { fz_decode_tile(ctx, tile, image->decode); } /* pre-blended matte color */ if (image->use_colorkey && image->mask) fz_unblend_masked_tile(ctx, tile, image); }
fz_pixmap * fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, int indexed, int l2factor, int native_l2factor) { fz_pixmap *tile = NULL; int stride, len, i; unsigned char *samples = NULL; int f = 1<<native_l2factor; int w = (image->w + f-1) >> native_l2factor; int h = (image->h + f-1) >> native_l2factor; /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1333 */ int is_banded = indexed < 0; fz_var(tile); fz_var(samples); /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1333 */ if (is_banded) indexed = -1 - indexed; else if (l2factor - native_l2factor > 0 && image->w > (1 << 8)) return decomp_image_banded(ctx, stm, image, indexed, l2factor, native_l2factor); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->colorspace, w, h); tile->interpolate = image->interpolate; stride = (w * image->n * image->bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); len = fz_read(stm, samples, h * stride); /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(tile, samples, image->n, image->bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; if (image->usecolorkey && !image->mask) fz_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, image->decode, (1 << image->bpc) - 1); conv = fz_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(tile, image->decode); } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ if (image->usecolorkey && image->mask && !is_banded) fz_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { fz_close(stm); } fz_catch(ctx) { if (tile) fz_drop_pixmap(ctx, tile); fz_free(ctx, samples); fz_rethrow(ctx); } /* Now apply any extra subsampling required */ if (l2factor - native_l2factor > 0) { if (l2factor - native_l2factor > 8) l2factor = native_l2factor + 8; fz_subsample_pixmap(ctx, tile, l2factor - native_l2factor); } return tile; }
fz_pixmap * fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs) { fz_pixmap *img; opj_event_mgr_t evtmgr; opj_dparameters_t params; opj_dinfo_t *info; opj_cio_t *cio; opj_image_t *jpx; fz_colorspace *colorspace; unsigned char *p; int format; int a, n, w, h, depth, sgnd; int x, y, k, v; if (size < 2) fz_throw(ctx, "not enough data to determine image format"); /* Check for SOC marker -- if found we have a bare J2K stream */ if (data[0] == 0xFF && data[1] == 0x4F) format = CODEC_J2K; else format = CODEC_JP2; memset(&evtmgr, 0, sizeof(evtmgr)); evtmgr.error_handler = fz_opj_error_callback; evtmgr.warning_handler = fz_opj_warning_callback; evtmgr.info_handler = fz_opj_info_callback; opj_set_default_decoder_parameters(¶ms); info = opj_create_decompress(format); opj_set_event_mgr((opj_common_ptr)info, &evtmgr, ctx); opj_setup_decoder(info, ¶ms); cio = opj_cio_open((opj_common_ptr)info, data, size); jpx = opj_decode(info, cio); opj_cio_close(cio); opj_destroy_decompress(info); if (!jpx) fz_throw(ctx, "opj_decode failed"); for (k = 1; k < jpx->numcomps; k++) { if (jpx->comps[k].w != jpx->comps[0].w) fz_throw(ctx, "image components have different width"); if (jpx->comps[k].h != jpx->comps[0].h) fz_throw(ctx, "image components have different height"); if (jpx->comps[k].prec != jpx->comps[0].prec) fz_throw(ctx, "image components have different precision"); } n = jpx->numcomps; w = jpx->comps[0].w; h = jpx->comps[0].h; depth = jpx->comps[0].prec; sgnd = jpx->comps[0].sgnd; if (jpx->color_space == CLRSPC_SRGB && n == 4) { n = 3; a = 1; } else if (jpx->color_space == CLRSPC_SYCC && n == 4) { n = 3; a = 1; } else if (n == 2) { n = 1; a = 1; } else if (n > 4) { n = 4; a = 1; } else { a = 0; } if (defcs) { if (defcs->n == n) { colorspace = defcs; } else { fz_warn(ctx, "jpx file and dict colorspaces do not match"); defcs = NULL; } } if (!defcs) { switch (n) { case 1: colorspace = fz_device_gray; break; case 3: colorspace = fz_device_rgb; break; case 4: colorspace = fz_device_cmyk; break; } } fz_try(ctx) { img = fz_new_pixmap(ctx, colorspace, w, h); } fz_catch(ctx) { opj_image_destroy(jpx); fz_throw(ctx, "out of memory"); } p = img->samples; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { for (k = 0; k < n + a; k++) { v = jpx->comps[k].data[y * w + x]; if (sgnd) v = v + (1 << (depth - 1)); if (depth > 8) v = v >> (depth - 8); *p++ = v; } if (!a) *p++ = 255; } } if (a) { if (n == 4) { fz_pixmap *tmp = fz_new_pixmap(ctx, fz_device_rgb, w, h); fz_convert_pixmap(ctx, img, tmp); fz_drop_pixmap(ctx, img); img = tmp; } fz_premultiply_pixmap(ctx, img); } opj_image_destroy(jpx); return img; }
static fz_pixmap * gif_read_image(fz_context *ctx, struct info *info, unsigned char *p, size_t total, int only_metadata) { fz_pixmap *pix; unsigned char *end = p + total; memset(info, 0x00, sizeof (*info)); /* Read header */ p = gif_read_header(ctx, info, p, end); /* Read logical screen descriptor */ p = gif_read_lsd(ctx, info, p, end); if (only_metadata) return NULL; pix = fz_new_pixmap(ctx, fz_device_rgb(ctx), info->width, info->height, 1); fz_try(ctx) { info->mask = fz_calloc(ctx, info->width * info->height, 1); /* Read optional global color table */ if (info->has_gct) { unsigned char *bp, *dp = pix->samples; unsigned int x, y, k; p = gif_read_gct(ctx, info, p, end); bp = &info->gct[info->gct_background * 3]; memset(info->mask, 0x01, info->width * info->height); for (y = 0; y < info->height; y++) for (x = 0; x < info->width; x++, dp += 4) { for (k = 0; k < 3; k++) dp[k] = bp[k]; dp[3] = 255; } } while (1) { /* Read block indicator */ if (end - p < 1) fz_throw(ctx, FZ_ERROR_GENERIC, "premature end of block indicator in gif image"); /* Read trailer */ if (p[0] == 0x3b) { break; } /* Read extension */ else if (p[0] == 0x21) { /* Read extension label */ if (end - p < 2) fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in extension label in gif image"); if (p[1] == 0x01 && info->gif89a) { /* Read plain text extension */ p = gif_read_pte(ctx, info, p, end); /* Graphic control extension applies only to the graphic rendering block following it */ info->transparent = 0; info->has_transparency = 0; } else if (p[1] == 0xf9 && info->gif89a) /* Read graphic control extension */ p = gif_read_gce(ctx, info, p, end); else if (p[1] == 0xfe && info->gif89a) /* Read comment extension */ p = gif_read_ce(ctx, info, p, end); else if (p[1] == 0xff && info->gif89a) /* Read application extension */ p = gif_read_ae(ctx, info, p, end); else fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported extension label %02x in gif image", p[1]); } /* Read image descriptor */ else if (p[0] == 0x2c) { p = gif_read_id(ctx, info, p, end); if (info->has_lct) /* Read local color table */ p = gif_read_lct(ctx, info, p, end); /* Read table based image data */ p = gif_read_tbid(ctx, info, pix->samples, p, end); /* Graphic control extension applies only to the graphic rendering block following it */ info->transparent = 0; info->has_transparency = 0; /* Image descriptor applies only to the table based image data following it */ info->image_left = info->image_top = 0; info->image_width = info->width; info->image_height = info->height; info->image_interlaced = 0; fz_free(ctx, info->lct); info->lct = NULL; info->has_lct = 0; } else fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported block indicator %02x in gif image", p[0]); } gif_mask_transparency(ctx, pix, info); fz_premultiply_pixmap(ctx, pix); } fz_always(ctx) { fz_free(ctx, info->mask); fz_free(ctx, info->lct); fz_free(ctx, info->gct); } fz_catch(ctx) { fz_drop_pixmap(ctx, pix); fz_rethrow(ctx); } return pix; }
fz_pixmap * fz_decomp_image_from_stream(fz_context *ctx, fz_stream *stm, fz_image *image, int indexed, int l2factor) { fz_pixmap *tile = NULL; int stride, len, i; unsigned char *samples = NULL; int f = 1<<l2factor; int w = (image->w + f-1) >> l2factor; int h = (image->h + f-1) >> l2factor; fz_var(tile); fz_var(samples); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->colorspace, w, h); tile->interpolate = image->interpolate; stride = (w * image->n * image->bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); len = fz_read(ctx, stm, samples, h * stride); /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(ctx, tile, samples, image->n, image->bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; /* color keyed transparency */ if (image->usecolorkey && !image->mask) fz_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(ctx, tile, image->decode, (1 << image->bpc) - 1); conv = fz_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(ctx, tile, image->decode); } /* pre-blended matte color */ if (image->usecolorkey && image->mask) fz_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { fz_drop_stream(ctx, stm); } fz_catch(ctx) { if (tile) fz_drop_pixmap(ctx, tile); fz_free(ctx, samples); fz_rethrow(ctx); } return tile; }
static fz_pixmap * decomp_image_banded(fz_context *ctx, fz_stream *stm, pdf_image *image, int indexed, int l2factor, int native_l2factor, int cache) { fz_pixmap *tile = NULL, *part = NULL; int w = (image->base.w + (1 << l2factor) - 1) >> l2factor; int h = (image->base.h + (1 << l2factor) - 1) >> l2factor; int part_h, orig_h = image->base.h; int band = 1 << fz_maxi(8, l2factor); fz_var(tile); fz_var(part); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.colorspace, w, h); tile->interpolate = image->interpolate; tile->has_alpha = 0; /* SumatraPDF: allow optimizing non-alpha pixmaps */ /* decompress the image in bands of 256 lines */ for (part_h = h; part_h > 0; part_h -= band >> l2factor) { image->base.h = part_h > band >> l2factor ? band : (orig_h - 1) % band + 1; part = decomp_image_from_stream(ctx, fz_keep_stream(stm), image, -1, indexed, l2factor, native_l2factor, 0); memcpy(tile->samples + (h - part_h) * tile->w * tile->n, part->samples, part->h * part->w * part->n); tile->has_alpha |= part->has_alpha; /* SumatraPDF: allow optimizing non-alpha pixmaps */ fz_drop_pixmap(ctx, part); part = NULL; } /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=693517 */ if (image->usecolorkey && image->base.mask) pdf_unblend_masked_tile(ctx, tile, image); } fz_always(ctx) { image->base.h = orig_h; fz_close(stm); } fz_catch(ctx) { fz_drop_pixmap(ctx, part); fz_drop_pixmap(ctx, tile); fz_rethrow(ctx); } if (cache) { pdf_image_key *key = NULL; fz_var(key); fz_try(ctx) { key = fz_malloc_struct(ctx, pdf_image_key); key->refs = 1; key->image = fz_keep_image(ctx, &image->base); key->l2factor = l2factor; fz_store_item(ctx, key, tile, fz_pixmap_size(ctx, tile), &pdf_image_store_type); } fz_always(ctx) { pdf_drop_image_key(ctx, key); } fz_catch(ctx) { } } return tile; }
static fz_pixmap * decomp_image_from_stream(fz_context *ctx, fz_stream *stm, pdf_image *image, int in_line, int indexed, int l2factor, int native_l2factor, int cache) { fz_pixmap *tile = NULL; fz_pixmap *existing_tile; int stride, len, i; unsigned char *samples = NULL; int f = 1<<native_l2factor; int w = (image->base.w + f-1) >> native_l2factor; int h = (image->base.h + f-1) >> native_l2factor; pdf_image_key *key = NULL; fz_var(tile); fz_var(samples); fz_var(key); fz_try(ctx) { tile = fz_new_pixmap(ctx, image->base.colorspace, w, h); tile->interpolate = image->interpolate; stride = (w * image->n * image->base.bpc + 7) / 8; samples = fz_malloc_array(ctx, h, stride); len = fz_read(stm, samples, h * stride); if (len < 0) { fz_throw(ctx, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ if (in_line) { unsigned char tbuf[512]; fz_try(ctx) { int tlen = fz_read(stm, tbuf, sizeof tbuf); if (tlen > 0) fz_warn(ctx, "ignoring garbage at end of image"); } fz_catch(ctx) { fz_warn(ctx, "ignoring error at end of image"); } } /* Pad truncated images */ if (len < stride * h) { fz_warn(ctx, "padding truncated image"); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (image->imagemask) { /* 0=opaque and 1=transparent so we need to invert */ unsigned char *p = samples; len = h * stride; for (i = 0; i < len; i++) p[i] = ~p[i]; } fz_unpack_tile(tile, samples, image->n, image->base.bpc, stride, indexed); fz_free(ctx, samples); samples = NULL; if (image->usecolorkey) pdf_mask_color_key(tile, image->n, image->colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, image->decode, (1 << image->base.bpc) - 1); conv = pdf_expand_indexed_pixmap(ctx, tile); fz_drop_pixmap(ctx, tile); tile = conv; } else { fz_decode_tile(tile, image->decode); } } fz_always(ctx) { fz_close(stm); } fz_catch(ctx) { if (tile) fz_drop_pixmap(ctx, tile); fz_free(ctx, samples); fz_rethrow(ctx); } /* Now apply any extra subsampling required */ if (l2factor - native_l2factor > 0) { if (l2factor - native_l2factor > 8) l2factor = native_l2factor + 8; fz_subsample_pixmap(ctx, tile, l2factor - native_l2factor); } if (!cache) return tile; /* Now we try to cache the pixmap. Any failure here will just result * in us not caching. */ fz_try(ctx) { key = fz_malloc_struct(ctx, pdf_image_key); key->refs = 1; key->image = fz_keep_image(ctx, &image->base); key->l2factor = l2factor; existing_tile = fz_store_item(ctx, key, tile, fz_pixmap_size(ctx, tile), &pdf_image_store_type); if (existing_tile) { /* We already have a tile. This must have been produced by a * racing thread. We'll throw away ours and use that one. */ fz_drop_pixmap(ctx, tile); tile = existing_tile; } } fz_always(ctx) { pdf_drop_image_key(ctx, key); } fz_catch(ctx) { /* Do nothing */ } return tile; }
fz_pixmap * fz_load_jpx(fz_context *ctx, unsigned char *data, int size, fz_colorspace *defcs, int indexed) { fz_pixmap *img; fz_colorspace *origcs; opj_dparameters_t params; opj_codec_t *codec; opj_image_t *jpx; opj_stream_t *stream; fz_colorspace *colorspace; unsigned char *p; OPJ_CODEC_FORMAT format; int a, n, w, h, depth, sgnd; int x, y, k, v; stream_block sb; if (size < 2) fz_throw(ctx, FZ_ERROR_GENERIC, "not enough data to determine image format"); /* Check for SOC marker -- if found we have a bare J2K stream */ if (data[0] == 0xFF && data[1] == 0x4F) format = OPJ_CODEC_J2K; else format = OPJ_CODEC_JP2; opj_set_default_decoder_parameters(¶ms); if (indexed) params.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; codec = opj_create_decompress(format); opj_set_info_handler(codec, fz_opj_info_callback, ctx); opj_set_warning_handler(codec, fz_opj_warning_callback, ctx); opj_set_error_handler(codec, fz_opj_error_callback, ctx); if (!opj_setup_decoder(codec, ¶ms)) { fz_throw(ctx, FZ_ERROR_GENERIC, "j2k decode failed"); } stream = opj_stream_default_create(OPJ_TRUE); sb.data = data; sb.pos = 0; sb.size = size; opj_stream_set_read_function(stream, fz_opj_stream_read); opj_stream_set_skip_function(stream, fz_opj_stream_skip); opj_stream_set_seek_function(stream, fz_opj_stream_seek); opj_stream_set_user_data(stream, &sb); /* Set the length to avoid an assert */ opj_stream_set_user_data_length(stream, size); if (!opj_read_header(stream, codec, &jpx)) { opj_stream_destroy(stream); opj_destroy_codec(codec); fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to read JPX header"); } if (!opj_decode(codec, stream, jpx)) { opj_stream_destroy(stream); opj_destroy_codec(codec); opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to decode JPX image"); } opj_stream_destroy(stream); opj_destroy_codec(codec); /* jpx should never be NULL here, but check anyway */ if (!jpx) fz_throw(ctx, FZ_ERROR_GENERIC, "opj_decode failed"); for (k = 1; k < (int)jpx->numcomps; k++) { if (!jpx->comps[k].data) { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "image components are missing data"); } if (jpx->comps[k].w != jpx->comps[0].w) { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different width"); } if (jpx->comps[k].h != jpx->comps[0].h) { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different height"); } if (jpx->comps[k].prec != jpx->comps[0].prec) { opj_image_destroy(jpx); fz_throw(ctx, FZ_ERROR_GENERIC, "image components have different precision"); } } n = jpx->numcomps; w = jpx->comps[0].w; h = jpx->comps[0].h; depth = jpx->comps[0].prec; sgnd = jpx->comps[0].sgnd; if (jpx->color_space == OPJ_CLRSPC_SRGB && n == 4) { n = 3; a = 1; } else if (jpx->color_space == OPJ_CLRSPC_SYCC && n == 4) { n = 3; a = 1; } else if (n == 2) { n = 1; a = 1; } else if (n > 4) { n = 4; a = 1; } else { a = 0; } origcs = defcs; if (defcs) { if (defcs->n == n) { colorspace = defcs; } else { fz_warn(ctx, "jpx file and dict colorspaces do not match"); defcs = NULL; } } if (!defcs) { switch (n) { case 1: colorspace = fz_device_gray(ctx); break; case 3: colorspace = fz_device_rgb(ctx); break; case 4: colorspace = fz_device_cmyk(ctx); break; } } fz_try(ctx) { img = fz_new_pixmap(ctx, colorspace, w, h); } fz_catch(ctx) { opj_image_destroy(jpx); fz_rethrow_message(ctx, "out of memory loading jpx"); } p = img->samples; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { for (k = 0; k < n + a; k++) { v = jpx->comps[k].data[y * w + x]; if (sgnd) v = v + (1 << (depth - 1)); if (depth > 8) v = v >> (depth - 8); *p++ = v; } if (!a) *p++ = 255; } } opj_image_destroy(jpx); if (a) { if (n == 4) { fz_pixmap *tmp = fz_new_pixmap(ctx, fz_device_rgb(ctx), w, h); fz_convert_pixmap(ctx, tmp, img); fz_drop_pixmap(ctx, img); img = tmp; } fz_premultiply_pixmap(ctx, img); } if (origcs != defcs) { fz_pixmap *tmp = fz_new_pixmap(ctx, origcs, w, h); fz_convert_pixmap(ctx, tmp, img); fz_drop_pixmap(ctx, img); img = tmp; } return img; }