static void fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_bbox bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; fz_context *ctx = dev->ctx; fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); state = push_stack(dev); model = state->dest->colorspace; bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(rect)); /* SumatraPDF: try to match rendering with and without display list */ if (fz_is_infinite_rect(rect)) bbox = fz_intersect_bbox(bbox, fz_bbox_from_rect(fz_bound_path(ctx, path, NULL, ctm))); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { state[1].scissor = bbox; state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif return; } fz_try(ctx) { state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state[1].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); #endif } fz_catch(ctx) { emergency_pop_stack(dev, state); } }
static void fz_draw_pop_clip(fz_context *ctx, void *user) { fz_draw_device *dev = user; fz_pixmap *mask, *dest, *shape; if (dev->top > 0) { dev->top--; dev->scissor = dev->stack[dev->top].scissor; mask = dev->stack[dev->top].mask; dest = dev->stack[dev->top].dest; shape = dev->stack[dev->top].shape; dev->blendmode = dev->stack[dev->top].blendmode; /* We can get here with mask == NULL if the clipping actually * resolved to a rectangle earlier. In this case, we will * have a dest, and the shape will be unchanged. */ if (mask) { assert(dest); #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(dev->dest, "Clipping "); if (dev->shape) fz_dump_blend(dev->shape, "/"); fz_dump_blend(dest, " onto "); if (shape) fz_dump_blend(shape, "/"); fz_dump_blend(mask, " with "); #endif fz_paint_pixmap_with_mask(dest, dev->dest, mask); if (shape != NULL) { assert(shape != dev->shape); fz_paint_pixmap_with_mask(shape, dev->shape, mask); fz_drop_pixmap(ctx, dev->shape); dev->shape = shape; } fz_drop_pixmap(ctx, mask); fz_drop_pixmap(ctx, dev->dest); dev->dest = dest; #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->dest, " to get "); if (dev->shape) fz_dump_blend(dev->shape, "/"); printf("\n"); #endif } else { #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip End\n"); #endif assert(dest == NULL); assert(shape == dev->shape); } } }
static void fz_draw_pop_clip(fz_device *devp) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_draw_state *state; if (dev->top == 0) { fz_warn(ctx, "Unexpected pop clip"); return; } state = &dev->stack[--dev->top]; /* We can get here with state[1].mask == NULL if the clipping actually * resolved to a rectangle earlier. */ if (state[1].mask) { #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(dev->ctx, state[1].dest, "Clipping "); if (state[1].shape) fz_dump_blend(dev->ctx, state[1].shape, "/"); fz_dump_blend(dev->ctx, state[0].dest, " onto "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); fz_dump_blend(dev->ctx, state[1].mask, " with "); #endif fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask); if (state[0].shape != state[1].shape) { fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask); fz_drop_pixmap(dev->ctx, state[1].shape); } fz_drop_pixmap(dev->ctx, state[1].mask); fz_drop_pixmap(dev->ctx, state[1].dest); #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->ctx, state[0].dest, " to get "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); printf("\n"); #endif } else { #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip end\n"); #endif } }
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]; }
static void fz_knockout_end(fz_context *ctx, void *user) { fz_draw_device *dev = user; fz_pixmap *group = dev->dest; fz_pixmap *shape = dev->shape; int blendmode; int isolated; if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0) return; if (dev->top == dev->stack_max) fz_grow_stack(dev); if (dev->top > 0) { dev->top--; blendmode = dev->blendmode & FZ_BLEND_MODEMASK; isolated = dev->blendmode & FZ_BLEND_ISOLATED; dev->blendmode = dev->stack[dev->top].blendmode; dev->shape = dev->stack[dev->top].shape; dev->dest = dev->stack[dev->top].dest; dev->scissor = dev->stack[dev->top].scissor; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(group, "Blending "); if (shape) fz_dump_blend(shape, "/"); fz_dump_blend(dev->dest, " onto "); if (dev->shape) fz_dump_blend(dev->shape, "/"); if (blendmode != 0) printf(" (blend %d)", blendmode); if (isolated != 0) printf(" (isolated)"); printf(" (knockout)"); #endif if ((blendmode == 0) && (shape == NULL)) fz_paint_pixmap(dev->dest, group, 255); else fz_blend_pixmap(dev->dest, group, 255, blendmode, isolated, shape); fz_drop_pixmap(ctx, group); if (shape != dev->shape) { if (dev->shape) { fz_paint_pixmap(dev->shape, shape, 255); } fz_drop_pixmap(ctx, shape); } #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->dest, " to get "); if (dev->shape) fz_dump_blend(dev->shape, "/"); printf("\n"); #endif } }
static void fz_draw_clip_path(fz_device *devp, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { state[1].scissor = bbox; state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif return; } state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state[1].shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } fz_scan_convert(dev->gel, even_odd, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n"); #endif }
static void fz_draw_end_group(fz_device *devp) { fz_draw_device *dev = devp->user; int blendmode; int isolated; float alpha; fz_context *ctx = dev->ctx; fz_draw_state *state; if (dev->top == 0) { fz_warn(ctx, "Unexpected end_group"); return; } state = &dev->stack[--dev->top]; alpha = state[1].alpha; blendmode = state[1].blendmode & FZ_BLEND_MODEMASK; isolated = state[1].blendmode & FZ_BLEND_ISOLATED; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(dev->ctx, state[1].dest, "Group end: blending "); if (state[1].shape) fz_dump_blend(dev->ctx, state[1].shape, "/"); fz_dump_blend(dev->ctx, state[0].dest, " onto "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); if (alpha != 1.0f) printf(" (alpha %g)", alpha); if (blendmode != 0) printf(" (blend %d)", blendmode); if (isolated != 0) printf(" (isolated)"); if (state[1].blendmode & FZ_BLEND_KNOCKOUT) printf(" (knockout)"); #endif if ((blendmode == 0) && (state[0].shape == state[1].shape)) fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255); else fz_blend_pixmap(state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].shape); fz_drop_pixmap(dev->ctx, state[1].dest); if (state[0].shape != state[1].shape) { if (state[0].shape) fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255); fz_drop_pixmap(dev->ctx, state[1].shape); } #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->ctx, state[0].dest, " to get "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); printf("\n"); #endif if (state[0].blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); }
static void fz_draw_clip_stroke_path(fz_context *ctx, void *user, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; fz_pixmap *mask, *dest, *shape; fz_bbox bbox; if (dev->top == dev->stack_max) fz_grow_stack(dev); if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; fz_reset_gel(dev->gel, dev->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(ctx, dev->gel, path, stroke, ctm, flatness, linewidth); else fz_flatten_stroke_path(ctx, dev->gel, path, stroke, ctm, flatness, linewidth); fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, dev->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); mask = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(ctx, model, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); if (dev->shape) { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } else shape = NULL; if (!fz_is_empty_rect(bbox)) fz_scan_convert(dev->gel, 0, bbox, mask, NULL); dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = mask; 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->scissor = bbox; dev->dest = dest; dev->shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (stroke) begin\n"); #endif dev->top++; }
static void fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, fz_rect *rect, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; fz_bbox bbox; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model; fz_context *ctx = dev->ctx; if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else fz_flatten_stroke_path(dev->gel, path, stroke, ctm, flatness, linewidth); fz_sort_gel(dev->gel); state = push_stack(dev); model = state->dest->colorspace; bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(*rect)); /* SumatraPDF: try to match rendering with and without display list */ else bbox = fz_intersect_bbox(bbox, fz_bbox_covering_rect(fz_bound_path(ctx, path, stroke, ctm))); fz_try(ctx) { state[1].mask = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].mask); state[1].dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(dev->ctx, state[1].dest); if (state->shape) { state[1].shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, state[1].shape); } if (!fz_is_empty_rect(bbox)) fz_scan_convert(dev->gel, 0, bbox, state[1].mask, NULL); state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke) begin\n"); #endif } fz_catch(ctx) { emergency_pop_stack(dev, state); } }
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 void fz_draw_begin_tile(fz_device *devp, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_pixmap *dest = NULL; fz_pixmap *shape; fz_bbox bbox; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; /* area, view, xstep, ystep are in pattern space */ /* ctm maps from pattern space to device space */ if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_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. Actually, this *can* fail due to the round_rect, at extreme * resolutions, so disable this assert. * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w || * bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h); */ dest = fz_new_pixmap_with_bbox(dev->ctx, model, bbox); fz_clear_pixmap(ctx, dest); shape = state[0].shape; if (shape) { fz_var(shape); fz_try(ctx) { shape = fz_new_pixmap_with_bbox(dev->ctx, NULL, bbox); fz_clear_pixmap(ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].xstep = xstep; state[1].ystep = ystep; state[1].area = area; state[1].ctm = ctm; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Tile begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; }
static void fz_knockout_begin(fz_context *ctx, void *user) { fz_draw_device *dev = user; fz_bbox bbox; fz_pixmap *dest, *shape; int isolated = dev->blendmode & FZ_BLEND_ISOLATED; if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0) return; if (dev->top == dev->stack_max) fz_grow_stack(dev); bbox = fz_bound_pixmap(dev->dest); bbox = fz_intersect_bbox(bbox, dev->scissor); dest = fz_new_pixmap_with_rect(ctx, dev->dest->colorspace, bbox); if (isolated) { fz_clear_pixmap(dest); } else { fz_pixmap *prev; int i = dev->top; do prev = dev->stack[--i].dest; while (prev == NULL); fz_copy_pixmap_rect(dest, prev, bbox); } if (dev->blendmode == 0 && isolated || 1 /* SumatraPDF: disable crashy shape code */) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = dev->shape; } else { shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); fz_clear_pixmap(shape); } dev->stack[dev->top].blendmode = dev->blendmode; dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Knockout begin\n"); #endif dev->top++; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; dev->blendmode &= ~FZ_BLEND_MODEMASK; }
static void fz_draw_begin_mask(fz_context *ctx, void *user, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv) { fz_draw_device *dev = user; fz_pixmap *dest; fz_pixmap *shape = dev->shape; fz_bbox bbox; if (dev->top == dev->stack_max) fz_grow_stack(dev); bbox = fz_round_rect(rect); bbox = fz_intersect_bbox(bbox, dev->scissor); dest = fz_new_pixmap_with_rect(ctx, fz_device_gray, bbox); if (dev->shape) { /* FIXME: If we ever want to support AIS true, then we * probably want to create a shape pixmap here, using: * shape = fz_new_pixmap_with_rect(NULL, bbox); * then, in the end_mask code, we create the mask from this * rather than dest. */ shape = NULL; } if (luminosity) { float bc; if (!colorspace) colorspace = fz_device_gray; fz_convert_color(ctx, colorspace, colorfv, fz_device_gray, &bc); fz_clear_pixmap_with_color(dest, bc * 255); if (shape) fz_clear_pixmap_with_color(shape, 255); } else { fz_clear_pixmap(dest); if (shape) fz_clear_pixmap(shape); } dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].luminosity = luminosity; dev->stack[dev->top].shape = dev->shape; dev->stack[dev->top].blendmode = dev->blendmode; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Mask begin\n"); #endif dev->top++; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; }
static void fz_draw_end_mask(fz_context *ctx, void *user) { fz_draw_device *dev = user; fz_pixmap *mask = dev->dest; fz_pixmap *maskshape = dev->shape; fz_pixmap *temp, *dest; fz_bbox bbox; int luminosity; if (dev->top == dev->stack_max) fz_grow_stack(dev); if (dev->top > 0) { /* pop soft mask buffer */ dev->top--; luminosity = dev->stack[dev->top].luminosity; dev->scissor = dev->stack[dev->top].scissor; dev->dest = dev->stack[dev->top].dest; dev->shape = dev->stack[dev->top].shape; /* convert to alpha mask */ temp = fz_alpha_from_gray(ctx, mask, luminosity); fz_drop_pixmap(ctx, mask); fz_drop_pixmap(ctx, maskshape); /* create new dest scratch buffer */ bbox = fz_bound_pixmap(temp); dest = fz_new_pixmap_with_rect(ctx, dev->dest->colorspace, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); /* push soft mask as clip mask */ dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = temp; dev->stack[dev->top].dest = dev->dest; /* FIXME: See note #1 */ dev->stack[dev->top].blendmode = dev->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 (dev->shape) { dev->stack[dev->top].shape = dev->shape; dev->shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(dev->shape); } dev->scissor = bbox; dev->dest = dest; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Mask -> Clip\n"); #endif dev->top++; } }
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; }
static void fz_knockout_end(fz_draw_device *dev) { fz_draw_state *state; int blendmode; int isolated; fz_context *ctx = dev->ctx; if (dev->top == 0) { fz_warn(ctx, "unexpected knockout end"); return; } state = &dev->stack[--dev->top]; if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0) return; blendmode = state->blendmode & FZ_BLEND_MODEMASK; isolated = state->blendmode & FZ_BLEND_ISOLATED; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(dev->ctx, state[1].dest, "Knockout end: blending "); if (state[1].shape) fz_dump_blend(dev->ctx, state[1].shape, "/"); fz_dump_blend(dev->ctx, state[0].dest, " onto "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); if (blendmode != 0) printf(" (blend %d)", blendmode); if (isolated != 0) printf(" (isolated)"); printf(" (knockout)"); #endif if ((blendmode == 0) && (state[0].shape == state[1].shape)) fz_paint_pixmap(state[0].dest, state[1].dest, 255); else fz_blend_pixmap(state[0].dest, state[1].dest, 255, blendmode, isolated, state[1].shape); fz_drop_pixmap(dev->ctx, state[1].dest); if (state[0].shape != state[1].shape) { if (state[0].shape) fz_paint_pixmap(state[0].shape, state[1].shape, 255); fz_drop_pixmap(dev->ctx, state[1].shape); } #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->ctx, state[0].dest, " to get "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); printf("\n"); #endif }
void Cxif_key::dump(ostream& os, bool show_ratio, int depth, Cvirtual_binary* t) const { bool own_t = !t; if (own_t) { t = new Cvirtual_binary; os << "K\\"; dump_ratio(os, t); os << ':' << endl; depth++; } for (auto& i : m_keys) { dump_spaces(os, depth << 1); os << 'K' << nh(8, i.first); int cb_s = i.second.get_size(); if (show_ratio && cb_s > 64) i.second.dump_ratio(os, t); os << ':' << endl; i.second.dump(os, show_ratio, depth + 1, t); } for (auto& i : m_values) { dump_spaces(os, depth << 1); os << 'V' << nh(8, i.first); int cb_s = i.second.get_size(); if (show_ratio && cb_s > 64) { unsigned long cb_z = t->size(); if (Z_OK == compress(t->data_edit(), &cb_z, i.second.get_data(), cb_s)) os << " (" << cb_z << '/' << cb_s << ')'; } os << ": "; i.second.dump(os, depth + 1); } if (own_t) delete t; }
static void fz_draw_end_tile(fz_context *ctx, void *user) { fz_draw_device *dev = user; fz_pixmap *tile = dev->dest; float xstep, ystep; fz_matrix ctm, ttm; fz_rect area; int x0, y0, x1, y1, x, y; if (dev->top > 0) { dev->top--; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Tile end\n"); #endif xstep = dev->stack[dev->top].xstep; ystep = dev->stack[dev->top].ystep; area = dev->stack[dev->top].area; ctm = dev->stack[dev->top].ctm; dev->scissor = dev->stack[dev->top].scissor; dev->dest = dev->stack[dev->top].dest; dev->blendmode = dev->stack[dev->top].blendmode; x0 = floorf(area.x0 / xstep); y0 = floorf(area.y0 / ystep); x1 = ceilf(area.x1 / xstep); y1 = ceilf(area.y1 / ystep); ctm.e = tile->x; ctm.f = tile->y; for (y = y0; y < y1; y++) { for (x = x0; x < x1; x++) { ttm = fz_concat(fz_translate(x * xstep, y * ystep), ctm); tile->x = ttm.e; tile->y = ttm.f; fz_paint_pixmap_with_rect(dev->dest, tile, 255, dev->scissor); } } fz_drop_pixmap(ctx, tile); } if (dev->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(ctx, dev); }
static void fz_draw_begin_mask(fz_device *devp, fz_rect rect, int luminosity, fz_colorspace *colorspace, float *colorfv) { fz_draw_device *dev = devp->user; fz_pixmap *dest; fz_bbox bbox; fz_draw_state *state = push_stack(dev); fz_pixmap *shape = state->shape; bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(dev->ctx, fz_device_gray, bbox); if (state->shape) { /* FIXME: If we ever want to support AIS true, then we * probably want to create a shape pixmap here, using: * shape = fz_new_pixmap_with_bbox(NULL, bbox); * then, in the end_mask code, we create the mask from this * rather than dest. */ shape = NULL; } if (luminosity) { float bc; if (!colorspace) colorspace = fz_device_gray; fz_convert_color(dev->ctx, fz_device_gray, &bc, colorspace, colorfv); fz_clear_pixmap_with_value(dev->ctx, dest, bc * 255); if (shape) fz_clear_pixmap_with_value(dev->ctx, shape, 255); } else { fz_clear_pixmap(dev->ctx, dest); if (shape) fz_clear_pixmap(dev->ctx, shape); } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Mask begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].luminosity = luminosity; }
static void fz_draw_clip_path(fz_context *ctx, void *user, fz_path *path, fz_rect *rect, int even_odd, fz_matrix ctm) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; fz_pixmap *mask, *dest, *shape; fz_bbox bbox; if (dev->top == dev->stack_max) fz_grow_stack(dev); fz_reset_gel(dev->gel, dev->scissor); fz_flatten_fill_path(ctx, dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); bbox = fz_intersect_bbox(bbox, dev->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); if (fz_is_empty_rect(bbox) || fz_is_rect_gel(dev->gel)) { dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = NULL; dev->stack[dev->top].dest = NULL; dev->stack[dev->top].shape = dev->shape; dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (rectangular) begin\n"); #endif dev->top++; return; } mask = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(ctx, model, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); if (dev->shape) { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } else shape = NULL; fz_scan_convert(dev->gel, even_odd, bbox, mask, NULL); dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = mask; 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->scissor = bbox; dev->dest = dest; dev->shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (non-rectangular) begin\n"); #endif dev->top++; }
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_clip_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state = push_stack(dev); fz_colorspace *model = state->dest->colorspace; /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); 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); } else shape = state->shape; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].mask = mask; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (stroke text) begin\n"); #endif if (!fz_is_empty_rect(bbox)) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_stroked_glyph(dev->ctx, text->font, gid, trm, ctm, stroke, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (shape) draw_glyph(NULL, shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_warn(dev->ctx, "cannot draw glyph for clipping (unimplemented case)"); // TODO: outline/non-cached case } } } }
static void fz_draw_begin_group(fz_context *ctx, void *user, fz_rect rect, int isolated, int knockout, int blendmode, float alpha) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; fz_bbox bbox; fz_pixmap *dest, *shape; if (dev->top == dev->stack_max) { fz_warn(ctx, "assert: too many buffers on stack"); return; } if (dev->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(ctx, dev); bbox = fz_round_rect(rect); bbox = fz_intersect_bbox(bbox, dev->scissor); dest = fz_new_pixmap_with_rect(ctx, model, bbox); #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED knockout = 0; isolated = 1; #endif if (isolated) { fz_clear_pixmap(dest); } else { fz_copy_pixmap_rect(dest, dev->dest, bbox); } if (blendmode == 0 && alpha == 1.0 && isolated || 1 /* SumatraPDF: disable crashy shape code */) { /* We can render direct to any existing shape plane. If there * isn't one, we don't need to make one. */ shape = dev->shape; } else { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } dev->stack[dev->top].alpha = alpha; dev->stack[dev->top].blendmode = dev->blendmode; dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Group Begin\n"); #endif dev->top++; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; dev->blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); }
static void fz_draw_end_tile(fz_device *devp) { fz_draw_device *dev = devp->user; float xstep, ystep; fz_matrix ctm, ttm, shapectm; fz_rect area; int x0, y0, x1, y1, x, y; fz_context *ctx = dev->ctx; fz_draw_state *state; if (dev->top == 0) { fz_warn(ctx, "Unexpected end_tile"); return; } state = &dev->stack[--dev->top]; xstep = state[1].xstep; ystep = state[1].ystep; area = state[1].area; ctm = state[1].ctm; x0 = floorf(area.x0 / xstep); y0 = floorf(area.y0 / ystep); x1 = ceilf(area.x1 / xstep); y1 = ceilf(area.y1 / ystep); ctm.e = state[1].dest->x; ctm.f = state[1].dest->y; if (state[1].shape) { shapectm = ctm; shapectm.e = state[1].shape->x; shapectm.f = state[1].shape->y; } #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, ""); fz_dump_blend(dev->ctx, state[1].dest, "Tiling "); if (state[1].shape) fz_dump_blend(dev->ctx, state[1].shape, "/"); fz_dump_blend(dev->ctx, state[0].dest, " onto "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); #endif for (y = y0; y < y1; y++) { for (x = x0; x < x1; x++) { ttm = fz_concat(fz_translate(x * xstep, y * ystep), ctm); state[1].dest->x = ttm.e; state[1].dest->y = ttm.f; fz_paint_pixmap_with_rect(state[0].dest, state[1].dest, 255, state[0].scissor); if (state[1].shape) { ttm = fz_concat(fz_translate(x * xstep, y * ystep), shapectm); state[1].shape->x = ttm.e; state[1].shape->y = ttm.f; fz_paint_pixmap_with_rect(state[0].shape, state[1].shape, 255, state[0].scissor); } } } fz_drop_pixmap(dev->ctx, state[1].dest); fz_drop_pixmap(dev->ctx, state[1].shape); #ifdef DUMP_GROUP_BLENDS fz_dump_blend(dev->ctx, state[0].dest, " to get "); if (state[0].shape) fz_dump_blend(dev->ctx, state[0].shape, "/"); printf("\n"); #endif if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); }
static void fz_draw_clip_image_mask(fz_context *ctx, void *user, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_pixmap *scaled = NULL; int dx, dy; if (dev->top == dev->stack_max) fz_grow_stack(dev); #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (image mask) begin\n"); #endif if (image->w == 0 || image->h == 0) { dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = NULL; dev->stack[dev->top].dest = NULL; dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = fz_empty_bbox; dev->top++; return; } bbox = fz_round_rect(fz_transform_rect(ctm, fz_unit_rect)); bbox = fz_intersect_bbox(bbox, dev->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); mask = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(ctx, model, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); if (dev->shape) { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } else shape = NULL; dx = sqrtf(ctm.a * ctm.a + ctm.b * ctm.b); dy = sqrtf(ctm.c * ctm.c + ctm.d * ctm.d); if (dx < image->w && dy < image->h) { int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); scaled = fz_transform_pixmap(ctx, image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit); if (scaled == NULL) { if (dx < 1) dx = 1; if (dy < 1) dy = 1; scaled = fz_scale_pixmap(ctx, image, image->x, image->y, dx, dy); } if (scaled != NULL) image = scaled; } fz_paint_image(mask, bbox, dev->shape, image, ctm, 255); if (scaled) fz_drop_pixmap(ctx, scaled); dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = mask; 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->scissor = bbox; dev->dest = dest; dev->shape = shape; dev->top++; }
static void fz_draw_clip_text(fz_context *ctx, void *user, fz_text *text, fz_matrix ctm, int accumulate) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; /* If accumulate == 0 then this text object is guaranteed complete */ /* If accumulate == 1 then this text object is the first (or only) in a sequence */ /* If accumulate == 2 then this text object is a continuation */ if (dev->top == dev->stack_max) fz_grow_stack(dev); if (accumulate == 0) { /* make the mask the exact size needed */ bbox = fz_round_rect(fz_bound_text(text, ctm)); bbox = fz_intersect_bbox(bbox, dev->scissor); } else { /* be conservative about the size of the mask needed */ bbox = dev->scissor; } if (accumulate == 0 || accumulate == 1) { mask = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(ctx, model, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); if (dev->shape) { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } else shape = NULL; dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = mask; 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->scissor = bbox; dev->dest = dest; dev->shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (text) begin\n"); #endif dev->top++; } else { mask = dev->stack[dev->top-1].mask; } if (!fz_is_empty_rect(bbox)) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_glyph(ctx, dev->cache, text->font, gid, trm, model); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (dev->shape) draw_glyph(NULL, dev->shape, glyph, x, y, bbox); fz_drop_pixmap(ctx, glyph); } } } }
static void fz_draw_begin_group(fz_device *devp, fz_rect rect, int isolated, int knockout, int blendmode, float alpha) { fz_draw_device *dev = devp->user; fz_bbox bbox; fz_pixmap *dest, *shape; fz_context *ctx = dev->ctx; fz_draw_state *state = &dev->stack[dev->top]; fz_colorspace *model = state->dest->colorspace; if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); state = push_stack(dev); bbox = fz_bbox_covering_rect(rect); bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_bbox(ctx, model, bbox); #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED knockout = 0; isolated = 1; #endif if (isolated) { fz_clear_pixmap(dev->ctx, dest); } else { fz_copy_pixmap_rect(dev->ctx, dest, state[0].dest, bbox); } if (blendmode == 0 && alpha == 1.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[0].shape; } else { fz_try(ctx) { shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox); fz_clear_pixmap(dev->ctx, shape); } fz_catch(ctx) { fz_drop_pixmap(ctx, dest); fz_rethrow(ctx); } } state[1].alpha = alpha; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Group begin\n"); #endif state[1].scissor = bbox; state[1].dest = dest; state[1].shape = shape; state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); }
static void fz_draw_clip_stroke_text(fz_context *ctx, void *user, fz_text *text, fz_stroke_state *stroke, fz_matrix ctm) { fz_draw_device *dev = user; fz_colorspace *model = dev->dest->colorspace; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; if (dev->top == dev->stack_max) fz_grow_stack(dev); /* make the mask the exact size needed */ bbox = fz_round_rect(fz_bound_text(text, ctm)); bbox = fz_intersect_bbox(bbox, dev->scissor); mask = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(ctx, model, bbox); /* FIXME: See note #1 */ fz_clear_pixmap(dest); if (dev->shape) { shape = fz_new_pixmap_with_rect(ctx, NULL, bbox); fz_clear_pixmap(shape); } else shape = dev->shape; dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = mask; 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->scissor = bbox; dev->dest = dest; dev->shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top, "Clip (stroke text) begin\n"); #endif dev->top++; if (!fz_is_empty_rect(bbox)) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_stroked_glyph(ctx, dev->cache, text->font, gid, trm, ctm, stroke); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (dev->shape) draw_glyph(NULL, dev->shape, glyph, x, y, bbox); fz_drop_pixmap(ctx, glyph); } } } }
static void fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) { fz_draw_device *dev = devp->user; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask, *dest, *shape; fz_matrix tm, trm, trunc_trm; fz_pixmap *glyph; int i, x, y, gid; fz_draw_state *state; fz_colorspace *model; /* If accumulate == 0 then this text object is guaranteed complete */ /* If accumulate == 1 then this text object is the first (or only) in a sequence */ /* If accumulate == 2 then this text object is a continuation */ state = push_stack(dev); model = state->dest->colorspace; if (accumulate == 0) { /* make the mask the exact size needed */ bbox = fz_bbox_covering_rect(fz_bound_text(dev->ctx, text, ctm)); bbox = fz_intersect_bbox(bbox, state->scissor); } else { /* be conservative about the size of the mask needed */ bbox = state->scissor; } if (accumulate == 0 || accumulate == 1) { 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); } else shape = NULL; state[1].blendmode |= FZ_BLEND_ISOLATED; state[1].scissor = bbox; state[1].dest = dest; state[1].mask = mask; state[1].shape = shape; #ifdef DUMP_GROUP_BLENDS dump_spaces(dev->top-1, "Clip (text) begin\n"); #endif } else { mask = state->mask; dev->top--; } if (!fz_is_empty_rect(bbox) && mask) { tm = text->trm; for (i = 0; i < text->len; i++) { gid = text->items[i].gid; if (gid < 0) continue; tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); x = floorf(trm.e); y = floorf(trm.f); trunc_trm = trm; trunc_trm.e = QUANT(trm.e - floorf(trm.e), HSUBPIX); trunc_trm.f = QUANT(trm.f - floorf(trm.f), VSUBPIX); glyph = fz_render_glyph(dev->ctx, text->font, gid, trunc_trm, model, bbox); if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); if (state[1].shape) draw_glyph(NULL, state[1].shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } else { fz_path *path = fz_outline_glyph(dev->ctx, text->font, gid, trm); if (path) { fz_pixmap *old_dest; float white = 1; state = &dev->stack[dev->top]; old_dest = state[0].dest; state[0].dest = state[0].mask; state[0].mask = NULL; fz_try(ctx) { fz_draw_fill_path(devp, path, 0, fz_identity, fz_device_gray, &white, 1); } fz_always(ctx) { state[0].mask = state[0].dest; state[0].dest = old_dest; fz_free_path(dev->ctx, path); } fz_catch(ctx) { fz_rethrow(ctx); } } else { fz_warn(dev->ctx, "cannot render glyph for clipping"); } } }