static void fz_draw_begin_tile(fz_context *ctx, void *user, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; fz_pixmap *dest; fz_bbox bbox; /* area, view, xstep, ystep are in pattern space */ /* ctm maps from pattern space to device space */ if (dev->top == dev->stack_max) fz_grow_stack(dev); if (dev->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(ctx, dev); bbox = fz_round_rect(fz_transform_rect(ctm, view)); /* We should never have a bbox that entirely covers our destination. * If we do, then the check for only 1 tile being visible above has * failed. */ /* SumatraPDF: assertion intentionally disabled assert(bbox.x0 > dev->dest->x || bbox.x1 < dev->dest->x + dev->dest->w || bbox.y0 > dev->dest->y || bbox.y1 < dev->dest->y + dev->dest->h); /* cf. http://bugs.ghostscript.com/show_bug.cgi?id=692418 */ dest = fz_new_pixmap_with_limit(ctx, model, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0); if (dest) { dest->x = bbox.x0; dest->y = bbox.y0; } else { bbox.x1 = bbox.x0; bbox.y1 = bbox.y0; dest = fz_new_pixmap_with_rect(ctx, model, bbox); } /* FIXME: See note #1 */ fz_clear_pixmap(dest); dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; /* FIXME: See note #1 */ dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED; dev->stack[dev->top].xstep = xstep; dev->stack[dev->top].ystep = ystep; dev->stack[dev->top].area = area; dev->stack[dev->top].ctm = ctm; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Tile begin\n"); #endif dev->top++; dev->scissor = bbox; dev->dest = dest; }
static fz_error pdf_load_image_imp(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask) { fz_stream *stm; fz_pixmap *tile; fz_obj *obj, *res; fz_error error; int w, h, bpc, n; int imagemask; int interpolate; int indexed; fz_colorspace *colorspace; fz_pixmap *mask; /* explicit mask/softmask image */ int usecolorkey; int colorkey[FZ_MAX_COLORS * 2]; float decode[FZ_MAX_COLORS * 2]; int stride; unsigned char *samples; int i, len; /* special case for JPEG2000 images */ if (pdf_is_jpx_image(dict)) { tile = NULL; error = pdf_load_jpx_image(&tile, xref, dict); if (error) return fz_rethrow(error, "cannot load jpx image"); if (forcemask) { if (tile->n != 2) { fz_drop_pixmap(tile); return fz_throw("softmask must be grayscale"); } mask = fz_alpha_from_gray(tile, 1); fz_drop_pixmap(tile); *imgp = mask; return fz_okay; } *imgp = tile; return fz_okay; } w = fz_to_int(fz_dict_getsa(dict, "Width", "W")); h = fz_to_int(fz_dict_getsa(dict, "Height", "H")); bpc = fz_to_int(fz_dict_getsa(dict, "BitsPerComponent", "BPC")); imagemask = fz_to_bool(fz_dict_getsa(dict, "ImageMask", "IM")); interpolate = fz_to_bool(fz_dict_getsa(dict, "Interpolate", "I")); indexed = 0; usecolorkey = 0; colorspace = NULL; mask = NULL; if (imagemask) bpc = 1; if (w == 0) return fz_throw("image width is zero"); if (h == 0) return fz_throw("image height is zero"); if (bpc == 0) return fz_throw("image depth is zero"); if (w > (1 << 16)) return fz_throw("image is too wide"); if (h > (1 << 16)) return fz_throw("image is too high"); obj = fz_dict_getsa(dict, "ColorSpace", "CS"); if (obj && !imagemask && !forcemask) { /* colorspace resource lookup is only done for inline images */ if (fz_is_name(obj)) { res = fz_dict_get(fz_dict_gets(rdb, "ColorSpace"), obj); if (res) obj = res; } error = pdf_load_colorspace(&colorspace, xref, obj); if (error) return fz_rethrow(error, "cannot load image colorspace"); if (!strcmp(colorspace->name, "Indexed")) indexed = 1; n = colorspace->n; } else { n = 1; } obj = fz_dict_getsa(dict, "Decode", "D"); if (obj) { for (i = 0; i < n * 2; i++) decode[i] = fz_to_real(fz_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 = fz_dict_getsa(dict, "SMask", "Mask"); if (fz_is_dict(obj)) { /* Not allowed for inline images */ if (!cstm) { error = pdf_load_image_imp(&mask, xref, rdb, obj, NULL, 1); if (error) { if (colorspace) fz_drop_colorspace(colorspace); return fz_rethrow(error, "cannot load image mask/softmask"); } } } else if (fz_is_array(obj)) { usecolorkey = 1; for (i = 0; i < n * 2; i++) colorkey[i] = fz_to_int(fz_array_get(obj, i)); } /* Allocate now, to fail early if we run out of memory */ tile = fz_new_pixmap_with_limit(colorspace, w, h); if (!tile) { if (colorspace) fz_drop_colorspace(colorspace); if (mask) fz_drop_pixmap(mask); return fz_throw("out of memory"); } if (colorspace) fz_drop_colorspace(colorspace); tile->mask = mask; tile->interpolate = interpolate; stride = (w * n * bpc + 7) / 8; if (cstm) { stm = pdf_open_inline_stream(cstm, xref, dict, stride * h); } else { error = pdf_open_stream(&stm, xref, fz_to_num(dict), fz_to_gen(dict)); if (error) { fz_drop_pixmap(tile); return fz_rethrow(error, "cannot open image data stream (%d 0 R)", fz_to_num(dict)); } } samples = fz_calloc(h, stride); len = fz_read(stm, samples, h * stride); if (len < 0) { fz_close(stm); fz_free(samples); fz_drop_pixmap(tile); return fz_rethrow(len, "cannot read image data"); } /* Make sure we read the EOF marker (for inline images only) */ if (cstm) { unsigned char tbuf[512]; int tlen = fz_read(stm, tbuf, sizeof tbuf); if (tlen < 0) fz_catch(tlen, "ignoring error at end of image"); if (tlen > 0) fz_warn("ignoring garbage at end of image"); } fz_close(stm); /* Pad truncated images */ if (len < stride * h) { fz_warn("padding truncated image (%d 0 R)", fz_to_num(dict)); memset(samples + len, 0, stride * h - len); } /* Invert 1-bit image masks */ if (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, n, bpc, stride, indexed); fz_free(samples); if (usecolorkey) pdf_mask_color_key(tile, n, colorkey); if (indexed) { fz_pixmap *conv; fz_decode_indexed_tile(tile, decode, (1 << bpc) - 1); conv = pdf_expand_indexed_pixmap(tile); fz_drop_pixmap(tile); tile = conv; } else { fz_decode_tile(tile, decode); } *imgp = tile; return fz_okay; }
fz_error fz_load_jpx_image(fz_pixmap **imgp, 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) return fz_throw("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, stderr); 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) return fz_throw("opj_decode failed"); for (k = 1; k < jpx->numcomps; k++) { if (jpx->comps[k].w != jpx->comps[0].w) return fz_throw("image components have different width"); if (jpx->comps[k].h != jpx->comps[0].h) return fz_throw("image components have different height"); if (jpx->comps[k].prec != jpx->comps[0].prec) return fz_throw("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("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; } } img = fz_new_pixmap_with_limit(colorspace, w, h); if (!img) { opj_image_destroy(jpx); return fz_throw("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(fz_device_rgb, w, h); fz_convert_pixmap(img, tmp); fz_drop_pixmap(img); img = tmp; } fz_premultiply_pixmap(img); } opj_image_destroy(jpx); *imgp = img; return fz_okay; }