void fz_copy_pixmap_rect(fz_context *ctx, fz_pixmap *dest, fz_pixmap *src, fz_bbox r) { const unsigned char *srcp; unsigned char *destp; int y, w, destspan, srcspan; r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, dest)); r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, src)); w = r.x1 - r.x0; y = r.y1 - r.y0; if (w <= 0 || y <= 0) return; w *= src->n; srcspan = src->w * src->n; srcp = src->samples + srcspan * (r.y0 - src->y) + src->n * (r.x0 - src->x); destspan = dest->w * dest->n; destp = dest->samples + destspan * (r.y0 - dest->y) + dest->n * (r.x0 - dest->x); do { memcpy(destp, srcp, w); srcp += srcspan; destp += destspan; } while (--y); }
static void writepixmap(fz_context *ctx, fz_pixmap *pix, char *file, int rgb) { char buf[1024]; fz_pixmap *converted = NULL; if (!pix) return; if (rgb && pix->colorspace && pix->colorspace != fz_device_rgb(ctx)) { fz_irect bbox; converted = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), fz_pixmap_bbox(ctx, pix, &bbox), pix->alpha); fz_convert_pixmap(ctx, converted, pix); pix = converted; } if (pix->n <= 4) { snprintf(buf, sizeof(buf), "%s.png", file); printf("extracting image %s\n", buf); fz_save_pixmap_as_png(ctx, pix, buf); } else { snprintf(buf, sizeof(buf), "%s.pam", file); printf("extracting image %s\n", buf); fz_save_pixmap_as_pam(ctx, pix, buf); } fz_drop_pixmap(ctx, converted); }
static fz_draw_state * fz_knockout_begin(fz_draw_device *dev) { fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *dest, *shape; fz_draw_state *state = &dev->stack[dev->top]; int isolated = state->blendmode & FZ_BLEND_ISOLATED; if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0) return state; state = push_stack(dev); bbox = fz_pixmap_bbox(dev->ctx, state->dest); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); if (isolated) { fz_clear_pixmap(ctx, dest); } else { /* Find the last but one destination to copy */ int i = dev->top-1; /* i = the one on entry (i.e. the last one) */ fz_pixmap *prev = state->dest; while (i > 0) { prev = dev->stack[--i].dest; if (prev != state->dest) break; } if (prev) fz_copy_pixmap_rect(ctx, dest, prev, bbox); else fz_clear_pixmap(ctx, dest); } if (state->blendmode == 0 && isolated) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = state->shape; } else { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Knockout begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].blendmode &= ~FZ_BLEND_MODEMASK; return &state[1]; }
void fz_clear_pixmap_rect_with_value(fz_context *ctx, fz_pixmap *dest, int value, fz_bbox r) { unsigned char *destp; int x, y, w, k, destspan; r = fz_intersect_bbox(r, fz_pixmap_bbox(ctx, dest)); w = r.x1 - r.x0; y = r.y1 - r.y0; if (w <= 0 || y <= 0) return; destspan = dest->w * dest->n; destp = dest->samples + destspan * (r.y0 - dest->y) + dest->n * (r.x0 - dest->x); if (value == 255) do { memset(destp, 255, w * dest->n); destp += destspan; } while (--y); else do { unsigned char *s = destp; for (x = 0; x < w; x++) { for (k = 0; k < dest->n - 1; k++) *s++ = value; *s++ = 255; } destp += destspan; } while (--y); }
void fz_write_pixmap(fz_context *ctx, fz_pixmap *img, char *file, int rgb) { char name[1024]; fz_pixmap *converted = NULL; if (!img) return; if (rgb && img->colorspace && img->colorspace != fz_device_rgb) { fz_irect bbox; converted = fz_new_pixmap_with_bbox(ctx, fz_device_rgb, fz_pixmap_bbox(ctx, img, &bbox)); fz_convert_pixmap(ctx, converted, img); img = converted; } if (img->n <= 4) { sprintf(name, "%s.png", file); printf("extracting image %s\n", name); fz_write_png(ctx, img, name, 0); } else { sprintf(name, "%s.pam", file); printf("extracting image %s\n", name); fz_write_pam(ctx, img, name, 0); } fz_drop_pixmap(ctx, converted); }
static void fz_draw_end_mask(fz_device *devp) { fz_draw_device *dev = devp->user; fz_pixmap *temp, *dest; fz_bbox bbox; int luminosity; fz_context *ctx = dev->ctx; fz_draw_state *state; if (dev->top == 0) { fz_warn(ctx, "Unexpected draw_end_mask"); return; } state = &dev->stack[dev->top-1]; /* pop soft mask buffer */ luminosity = state[1].luminosity; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Mask -> Clip\n"); #endif /* convert to alpha mask */ temp = fz_alpha_from_gray(dev->ctx, state[1].dest, luminosity); if (state[1].dest != state[0].dest) fz_drop_pixmap(dev->ctx, state[1].dest); state[1].dest = NULL; if (state[1].shape != state[0].shape) fz_drop_pixmap(dev->ctx, state[1].shape); state[1].shape = NULL; if (state[1].mask != state[0].mask) fz_drop_pixmap(dev->ctx, state[1].mask); state[1].mask = NULL; /* create new dest scratch buffer */ bbox = fz_pixmap_bbox(ctx, temp); dest = fz_new_pixmap_with_bbox(dev->ctx, state->dest->colorspace, bbox); fz_clear_pixmap(dev->ctx, dest); /* push soft mask as clip mask */ state[1].mask = temp; state[1].dest = dest; state[1].blendmode |= FZ_BLEND_ISOLATED; /* If we have a shape, then it'll need to be masked with the * clip mask when we pop. So create a new shape now. */ if (state[0].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } state[1].scissor = bbox; }
fz_pixmap * pdf_expand_indexed_pixmap(fz_context *ctx, fz_pixmap *src) { struct indexed *idx; fz_pixmap *dst; unsigned char *s, *d; int y, x, k, n, high; unsigned char *lookup; fz_irect bbox; assert(src->colorspace->to_rgb == indexed_to_rgb); assert(src->n == 2); idx = src->colorspace->data; high = idx->high; lookup = idx->lookup; n = idx->base->n; dst = fz_new_pixmap_with_bbox(ctx, idx->base, fz_pixmap_bbox(ctx, src, &bbox)); s = src->samples; d = dst->samples; for (y = 0; y < src->h; y++) { for (x = 0; x < src->w; x++) { int v = *s++; int a = *s++; v = fz_mini(v, high); for (k = 0; k < n; k++) *d++ = fz_mul255(lookup[v * n + k], a); *d++ = a; } } dst->interpolate = src->interpolate; return dst; }
fz_pixmap * fz_alpha_from_gray(fz_context *ctx, fz_pixmap *gray, int luminosity) { fz_pixmap *alpha; unsigned char *sp, *dp; int len; assert(gray->n == 2); alpha = fz_new_pixmap_with_bbox(ctx, NULL, fz_pixmap_bbox(ctx, gray)); dp = alpha->samples; sp = gray->samples; if (!luminosity) sp ++; len = gray->w * gray->h; while (len--) { *dp++ = sp[0]; sp += 2; } return alpha; }
static void fz_draw_fill_image(fz_device *devp, fz_image *image, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; fz_pixmap *converted = NULL; fz_pixmap *scaled = NULL; fz_pixmap *pixmap; fz_pixmap *orig_pixmap; int after; int dx, dy; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_pixmap_bbox(ctx, state->dest); clip = fz_intersect_bbox(clip, state->scissor); fz_var(scaled); if (!model) { fz_warn(dev->ctx, "cannot render image directly to an alpha mask"); return; } if (image->w == 0 || image->h == 0) return; dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); pixmap = fz_image_to_pixmap(ctx, image, dx, dy); orig_pixmap = pixmap; /* convert images with more components (cmyk->rgb) before scaling */ /* convert images with fewer components (gray->rgb after scaling */ /* convert images with expensive colorspace transforms after scaling */ fz_try(ctx) { if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(dev); after = 0; if (pixmap->colorspace == fz_device_gray) after = 1; if (pixmap->colorspace != model && !after) { converted = fz_new_pixmap_with_bbox(ctx, model, fz_pixmap_bbox(ctx, pixmap)); fz_convert_pixmap(ctx, converted, pixmap); pixmap = converted; } if (dx < pixmap->w && dy < pixmap->h) { int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) pixmap = scaled; } if (pixmap->colorspace != model) { if ((pixmap->colorspace == fz_device_gray && model == fz_device_rgb) || (pixmap->colorspace == fz_device_gray && model == fz_device_bgr)) { /* We have special case rendering code for gray -> rgb/bgr */ } else { converted = fz_new_pixmap_with_bbox(ctx, model, fz_pixmap_bbox(ctx, pixmap)); fz_convert_pixmap(ctx, converted, pixmap); pixmap = converted; } } fz_paint_image(state->dest, state->scissor, state->shape, pixmap, ctm, alpha * 255); if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } fz_always(ctx) { fz_drop_pixmap(ctx, scaled); fz_drop_pixmap(ctx, converted); fz_drop_pixmap(ctx, orig_pixmap); } fz_catch(ctx) { fz_rethrow(ctx); } }
static void fz_draw_clip_image_mask(fz_device *devp, fz_image *image, fz_rect *rect, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask = NULL; fz_pixmap *dest = NULL; fz_pixmap *shape = NULL; fz_pixmap *scaled = NULL; fz_pixmap *pixmap; fz_pixmap *orig_pixmap; int dx, dy; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_pixmap_bbox(ctx, state->dest); clip = fz_intersect_bbox(clip, state->scissor); fz_var(mask); fz_var(dest); fz_var(shape); if (image->w == 0 || image->h == 0) { #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n"); #endif state[1].scissor = fz_empty_bbox; state[1].mask = NULL; return; } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (image mask) begin\n"); #endif bbox = fz_bbox_covering_rect(fz_transform_rect(ctm, fz_unit_rect)); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); pixmap = fz_image_to_pixmap(ctx, image, dx, dy); orig_pixmap = pixmap; fz_try(ctx) { mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, mask); dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, dest); if (state->shape) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } if (dx < pixmap->w && dy < pixmap->h) { int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(dev->ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) pixmap = scaled; } fz_paint_image(mask, bbox, state->shape, pixmap, ctm, 255); } fz_always(ctx) { fz_drop_pixmap(ctx, scaled); fz_drop_pixmap(ctx, orig_pixmap); } fz_catch(ctx) { fz_drop_pixmap(ctx, shape); fz_drop_pixmap(ctx, dest); fz_drop_pixmap(ctx, mask); fz_rethrow(ctx); } state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].mask = mask; }
static void fz_draw_fill_image_mask(fz_device *devp, fz_image *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; unsigned char colorbv[FZ_MAX_COLORS + 1]; float colorfv[FZ_MAX_COLORS]; fz_pixmap *scaled = NULL; fz_pixmap *pixmap; fz_pixmap *orig_pixmap; int dx, dy; int i; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; fz_bbox clip = fz_pixmap_bbox(ctx, state->dest); clip = fz_intersect_bbox(clip, state->scissor); if (image->w == 0 || image->h == 0) return; dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); pixmap = fz_image_to_pixmap(ctx, image, dx, dy); orig_pixmap = pixmap; fz_try(ctx) { if (state->blendmode & FZ_BLEND_KNOCKOUT) state = fz_knockout_begin(dev); if (dx < pixmap->w && dy < pixmap->h) { int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(dev->ctx, pixmap, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip); if (!scaled) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(dev->ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL); } if (scaled) pixmap = scaled; } fz_convert_color(dev->ctx, model, colorfv, colorspace, color); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; colorbv[i] = alpha * 255; fz_paint_image_with_color(state->dest, state->scissor, state->shape, pixmap, ctm, colorbv); if (scaled) fz_drop_pixmap(dev->ctx, scaled); if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } fz_always(ctx) { fz_drop_pixmap(dev->ctx, orig_pixmap); } fz_catch(ctx) { fz_rethrow(ctx); } }
static fz_image * pdf_load_image_imp(pdf_document *doc, 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 stride; int i; fz_context *ctx = doc->ctx; fz_compressed_buffer *buffer; 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(doc, dict, forcemask); if (forcemask) { fz_pixmap *mask_pixmap; if (image->n != 2) { fz_pixmap *gray; fz_irect bbox; fz_warn(ctx, "soft mask should be grayscale"); gray = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), fz_pixmap_bbox(ctx, image->tile, &bbox)); /* SumatraPDF: ignore invalid JPX softmasks */ fz_clear_pixmap_with_value(ctx, gray, 255); fz_drop_pixmap(ctx, image->tile); image->tile = gray; } 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; if (imagemask) bpc = 1; if (w <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width is zero (or less)"); if (h <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height is zero (or less)"); if (bpc <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image depth is zero (or less)"); if (bpc > 16) fz_throw(ctx, FZ_ERROR_GENERIC, "image depth is too large: %d", bpc); if (w > (1 << 16)) fz_throw(ctx, FZ_ERROR_GENERIC, "image is too wide"); if (h > (1 << 16)) fz_throw(ctx, FZ_ERROR_GENERIC, "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(doc, obj); indexed = fz_colorspace_is_indexed(colorspace); 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(doc, rdb, obj, NULL, 1); obj = pdf_dict_gets(obj, "Matte"); if (pdf_is_array(obj)) { usecolorkey = 1; for (i = 0; i < n; i++) colorkey[i] = pdf_to_real(pdf_array_get(obj, i)) * 255; } } } 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)); } } /* Do we load from a ref, or do we load an inline stream? */ if (cstm == NULL) { /* 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); buffer = pdf_load_compressed_stream(doc, num, gen); image = fz_new_image(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, usecolorkey ? colorkey : NULL, buffer, mask); } else { /* Inline stream */ stride = (w * n * bpc + 7) / 8; image = fz_new_image(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, usecolorkey ? colorkey : NULL, NULL, mask); pdf_load_compressed_inline_image(doc, dict, stride * h, cstm, indexed, image); } } fz_catch(ctx) { fz_drop_colorspace(ctx, colorspace); fz_drop_image(ctx, mask); fz_drop_image(ctx, image); fz_rethrow(ctx); } return image; }