diff options
author | Robin Watts <robin.watts@artifex.com> | 2012-01-03 12:07:11 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2012-01-03 17:36:52 +0000 |
commit | a842269cb43fcb5f1d0bdd594d8d0839a7dcbdf7 (patch) | |
tree | 0ff3885a8421e59cb5eadc3f65123ce01f37b2b5 /draw/draw_device.c | |
parent | 634fa34f946fb3d48736917102a1e5be583a3fc8 (diff) | |
download | mupdf-a842269cb43fcb5f1d0bdd594d8d0839a7dcbdf7.tar.xz |
Rework draw device stack handling.
As we create clips/transparency groups etc, we maintain a stack of
draw device states.
Here we change this from having a 'current' state, and storing changes
on the stack to always keeping the complete current state on the head
of the stack. This should make error cleanup easier, as well as being
conceptually easier to follow (for me at least).
Diffstat (limited to 'draw/draw_device.c')
-rw-r--r-- | draw/draw_device.c | 1058 |
1 files changed, 480 insertions, 578 deletions
diff --git a/draw/draw_device.c b/draw/draw_device.c index 5583c30e..d3695073 100644 --- a/draw/draw_device.c +++ b/draw/draw_device.c @@ -13,29 +13,15 @@ /* Enable the following to help debug group blending. */ #undef DUMP_GROUP_BLENDS -/* Note #1: At various points in this code (notably when clipping with non - * rectangular masks), we create a new (empty) destination pixmap. We then - * render this pixmap, then plot it back into the original destination - * through a mask. This works well for normal blending, but falls down for - * non-zero blending modes; effectively we are forcing ourselves to use an - * isolated group. - * - * The fix for this would be to copy the contents from the underlying dest - * into the newly created dest. This would enable us to use a non - * FZ_BLEND_ISOLATED blendmode. Unfortunately, tt would break tiling, as - * we could no longer render once and blend back multiple times. - */ - - typedef struct fz_draw_device_s fz_draw_device; enum { FZ_DRAWDEV_FLAGS_TYPE3 = 1, }; -typedef struct fz_draw_stack_s fz_draw_stack; +typedef struct fz_draw_state_s fz_draw_state; -struct fz_draw_stack_s { +struct fz_draw_state_s { fz_bbox scissor; fz_pixmap *dest; fz_pixmap *mask; @@ -53,17 +39,11 @@ struct fz_draw_device_s fz_glyph_cache *cache; fz_gel *gel; fz_context *ctx; - - fz_pixmap *dest; - fz_pixmap *shape; - fz_bbox scissor; - int flags; int top; - int blendmode; - fz_draw_stack *stack; + fz_draw_state *stack; int stack_max; - fz_draw_stack init_stack[STACK_SIZE]; + fz_draw_state init_stack[STACK_SIZE]; }; #ifdef DUMP_GROUP_BLENDS @@ -97,7 +77,7 @@ static void dump_spaces(int x, const char *s) static void fz_grow_stack(fz_draw_device *dev) { int max = dev->stack_max * 2; - fz_draw_stack *stack; + fz_draw_state *stack; if (dev->stack == &dev->init_stack[0]) { @@ -112,21 +92,38 @@ static void fz_grow_stack(fz_draw_device *dev) dev->stack_max = max; } -static void fz_knockout_begin(fz_draw_device *dev) +/* 'Push' the stack. Returns a pointer to the current state, with state[1] + * already having been initialised to contain the same thing. Simply + * change any contents of state[1] that you want to and continue. */ +static fz_draw_state * +push_stack(fz_draw_device *dev) +{ + fz_draw_state *state; + + if (dev->top == dev->stack_max) + fz_grow_stack(dev); + state = &dev->stack[dev->top]; + dev->top++; + memcpy(&state[1], state, sizeof(*state)); + return state; +} + +static fz_draw_state * +fz_knockout_begin(fz_draw_device *dev) { fz_bbox bbox; fz_pixmap *dest, *shape; - int isolated = dev->blendmode & FZ_BLEND_ISOLATED; + fz_draw_state *state = &dev->stack[dev->top]; + int isolated = state->blendmode & FZ_BLEND_ISOLATED; - if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0) - return; + if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0) + return state; - if (dev->top == dev->stack_max) - fz_grow_stack(dev); + state = push_stack(dev); - bbox = fz_bound_pixmap(dev->dest); - bbox = fz_intersect_bbox(bbox, dev->scissor); - dest = fz_new_pixmap_with_rect(dev->ctx, dev->dest->colorspace, bbox); + bbox = fz_bound_pixmap(state->dest); + bbox = fz_intersect_bbox(bbox, state->scissor); + dest = fz_new_pixmap_with_rect(dev->ctx, state->dest->colorspace, bbox); if (isolated) { @@ -134,98 +131,94 @@ static void fz_knockout_begin(fz_draw_device *dev) } else { - fz_pixmap *prev; - int i = dev->top; - do + /* 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; - while (!prev); - fz_copy_pixmap_rect(dest, prev, bbox); + if (prev != state->dest) + break; + } + if (prev) + fz_copy_pixmap_rect(dest, prev, bbox); + else + fz_clear_pixmap(dest); } - if (dev->blendmode == 0 && isolated) + 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 = dev->shape; + shape = state->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"); + dump_spaces(dev->top-1, "Knockout begin\n"); #endif - dev->top++; + state[1].scissor = bbox; + state[1].dest = dest; + state[1].shape = shape; + state[1].blendmode &= ~FZ_BLEND_MODEMASK; - dev->scissor = bbox; - dev->dest = dest; - dev->shape = shape; - dev->blendmode &= ~FZ_BLEND_MODEMASK; + return &state[1]; } static void fz_knockout_end(fz_draw_device *dev) { - fz_pixmap *group = dev->dest; - fz_pixmap *shape = dev->shape; + fz_draw_state *state; int blendmode; int isolated; + fz_context *ctx = dev->ctx; - if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0) + 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; - 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; + 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, group, "Blending "); - if (shape) - fz_dump_blend(dev->ctx, shape, "/"); - fz_dump_blend(dev->ctx, dev->dest, " onto "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - if (blendmode != 0) - printf(" (blend %d)", blendmode); - if (isolated != 0) - printf(" (isolated)"); - printf(" (knockout)"); + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "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) && (!shape)) - fz_paint_pixmap(dev->dest, group, 255); - else - fz_blend_pixmap(dev->dest, group, 255, blendmode, isolated, shape); + 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, group); - if (shape != dev->shape) - { - if (dev->shape) - { - fz_paint_pixmap(dev->shape, shape, 255); - } - fz_drop_pixmap(dev->ctx, 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, dev->dest, " to get "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - printf("\n"); + 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 - } } static void @@ -233,44 +226,45 @@ fz_draw_fill_path(fz_device *devp, fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; unsigned char colorbv[FZ_MAX_COLORS + 1]; float colorfv[FZ_MAX_COLORS]; fz_bbox bbox; int i; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; - fz_reset_gel(dev->gel, dev->scissor); + 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, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); if (fz_is_empty_rect(bbox)) return; - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); fz_convert_color(dev->ctx, colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; colorbv[i] = alpha * 255; - fz_scan_convert(dev->gel, even_odd, bbox, dev->dest, colorbv); - if (dev->shape) + fz_scan_convert(dev->gel, even_odd, bbox, state->dest, colorbv); + if (state->shape) { - fz_reset_gel(dev->gel, dev->scissor); + fz_reset_gel(dev->gel, state->scissor); fz_flatten_fill_path(dev->gel, path, ctm, flatness); fz_sort_gel(dev->gel); colorbv[0] = alpha * 255; - fz_scan_convert(dev->gel, even_odd, bbox, dev->shape, colorbv); + fz_scan_convert(dev->gel, even_odd, bbox, state->shape, colorbv); } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -279,7 +273,6 @@ fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, fz_ fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; float expansion = fz_matrix_expansion(ctm); float flatness = 0.3f / expansion; float linewidth = stroke->linewidth; @@ -287,11 +280,13 @@ fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, fz_ float colorfv[FZ_MAX_COLORS]; fz_bbox bbox; int i; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; - fz_reset_gel(dev->gel, dev->scissor); + fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else @@ -299,23 +294,23 @@ fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, fz_ fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); if (fz_is_empty_rect(bbox)) return; - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); fz_convert_color(dev->ctx, colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; colorbv[i] = alpha * 255; - fz_scan_convert(dev->gel, 0, bbox, dev->dest, colorbv); - if (dev->shape) + fz_scan_convert(dev->gel, 0, bbox, state->dest, colorbv); + if (state->shape) { - fz_reset_gel(dev->gel, dev->scissor); + fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else @@ -323,10 +318,10 @@ fz_draw_stroke_path(fz_device *devp, fz_path *path, fz_stroke_state *stroke, fz_ fz_sort_gel(dev->gel); colorbv[0] = 255; - fz_scan_convert(dev->gel, 0, bbox, dev->shape, colorbv); + fz_scan_convert(dev->gel, 0, bbox, state->shape, colorbv); } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -334,87 +329,65 @@ 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; - 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; + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; - if (dev->top == dev->stack_max) - fz_grow_stack(dev); - - fz_reset_gel(dev->gel, dev->scissor); + 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, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->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; + state[1].scissor = bbox; + state[1].mask = NULL; #ifdef DUMP_GROUP_BLENDS - dump_spaces(dev->top, "Clip (rectangular) begin\n"); + dump_spaces(dev->top-1, "Clip (rectangular) begin\n"); #endif - dev->top++; return; } - mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); - fz_clear_pixmap(mask); - dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ - fz_clear_pixmap(dest); - if (dev->shape) + state[1].mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(state[1].mask); + state[1].dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); + fz_clear_pixmap(state[1].dest); + if (state[1].shape) { - shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); - fz_clear_pixmap(shape); + state[1].shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(state[1].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; + 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, "Clip (non-rectangular) begin\n"); + dump_spaces(dev->top-1, "Clip (non-rectangular) 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; - 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); + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; if (linewidth * expansion < 0.1f) linewidth = 1 / expansion; - fz_reset_gel(dev->gel, dev->scissor); + fz_reset_gel(dev->gel, state->scissor); if (stroke->dash_len > 0) fz_flatten_dash_path(dev->gel, path, stroke, ctm, flatness, linewidth); else @@ -422,39 +395,28 @@ fz_draw_clip_stroke_path(fz_device *devp, fz_path *path, fz_rect *rect, fz_strok fz_sort_gel(dev->gel); bbox = fz_bound_gel(dev->gel); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); - mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); - fz_clear_pixmap(mask); - dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ - fz_clear_pixmap(dest); - if (dev->shape) + state[1].mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(state[1].mask); + state[1].dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); + fz_clear_pixmap(state[1].dest); + if (state->shape) { - shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); - fz_clear_pixmap(shape); + state[1].shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(state[1].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; + 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, "Clip (stroke) begin\n"); + dump_spaces(dev->top-1, "Clip (stroke) begin\n"); #endif - dev->top++; } static void @@ -498,16 +460,17 @@ fz_draw_fill_text(fz_device *devp, fz_text *text, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; unsigned char colorbv[FZ_MAX_COLORS + 1]; unsigned char shapebv; float colorfv[FZ_MAX_COLORS]; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); fz_convert_color(dev->ctx, colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) @@ -536,20 +499,20 @@ fz_draw_fill_text(fz_device *devp, fz_text *text, fz_matrix ctm, { if (glyph->n == 1) { - draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor); - if (dev->shape) - draw_glyph(&shapebv, dev->shape, glyph, x, y, dev->scissor); + draw_glyph(colorbv, state->dest, glyph, x, y, state->scissor); + if (state->shape) + draw_glyph(&shapebv, state->shape, glyph, x, y, state->scissor); } else { fz_matrix ctm = {glyph->w, 0.0, 0.0, -glyph->h, x + glyph->x, y + glyph->y + glyph->h}; - fz_paint_image(dev->dest, dev->scissor, dev->shape, glyph, ctm, alpha * 255); + fz_paint_image(state->dest, state->scissor, state->shape, glyph, ctm, alpha * 255); } fz_drop_pixmap(dev->ctx, glyph); } } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -558,15 +521,16 @@ fz_draw_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, fz_ fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; unsigned char colorbv[FZ_MAX_COLORS + 1]; float colorfv[FZ_MAX_COLORS]; fz_matrix tm, trm; fz_pixmap *glyph; int i, x, y, gid; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); fz_convert_color(dev->ctx, colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) @@ -592,14 +556,14 @@ fz_draw_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke, fz_ glyph = fz_render_stroked_glyph(dev->ctx, dev->cache, text->font, gid, trm, ctm, stroke); if (glyph) { - draw_glyph(colorbv, dev->dest, glyph, x, y, dev->scissor); - if (dev->shape) - draw_glyph(colorbv, dev->shape, glyph, x, y, dev->scissor); + draw_glyph(colorbv, state->dest, glyph, x, y, state->scissor); + if (state->shape) + draw_glyph(colorbv, state->shape, glyph, x, y, state->scissor); fz_drop_pixmap(dev->ctx, glyph); } } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -607,30 +571,31 @@ static void fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) { fz_draw_device *dev = devp->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; + 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 */ - if (dev->top == dev->stack_max) - fz_grow_stack(dev); + state = push_stack(dev); + model = state->dest->colorspace; 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); + bbox = fz_intersect_bbox(bbox, state->scissor); } else { /* be conservative about the size of the mask needed */ - bbox = dev->scissor; + bbox = state->scissor; } if (accumulate == 0 || accumulate == 1) @@ -638,9 +603,8 @@ fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ fz_clear_pixmap(dest); - if (dev->shape) + if (state->shape) { shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); fz_clear_pixmap(shape); @@ -648,26 +612,22 @@ fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) 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; + 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, "Clip (text) begin\n"); + dump_spaces(dev->top-1, "Clip (text) begin\n"); #endif - dev->top++; } else { - mask = dev->stack[dev->top-1].mask; + mask = state->mask; + dev->top--; } - if (!fz_is_empty_rect(bbox)) + if (!fz_is_empty_rect(bbox) && mask) { tm = text->trm; @@ -689,8 +649,8 @@ fz_draw_clip_text(fz_device *devp, fz_text *text, fz_matrix ctm, int accumulate) if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); - if (dev->shape) - draw_glyph(NULL, dev->shape, glyph, x, y, bbox); + if (state[1].shape) + draw_glyph(NULL, state[1].shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } } @@ -701,46 +661,37 @@ 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_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); + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; /* make the mask the exact size needed */ bbox = fz_round_rect(fz_bound_text(text, ctm)); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); mask = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); fz_clear_pixmap(mask); dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ fz_clear_pixmap(dest); - if (dev->shape) + if (state->shape) { shape = fz_new_pixmap_with_rect(dev->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; + shape = state->shape; + + state[1].blendmode |= FZ_BLEND_ISOLATED; + state[1].scissor = bbox; + state[1].dest = dest; + state[1].shape = shape; #ifdef DUMP_GROUP_BLENDS - dump_spaces(dev->top, "Clip (stroke text) begin\n"); + dump_spaces(dev->top-1, "Clip (stroke text) begin\n"); #endif - dev->top++; if (!fz_is_empty_rect(bbox)) { @@ -764,8 +715,8 @@ fz_draw_clip_stroke_text(fz_device *devp, fz_text *text, fz_stroke_state *stroke if (glyph) { draw_glyph(NULL, mask, glyph, x, y, bbox); - if (dev->shape) - draw_glyph(NULL, dev->shape, glyph, x, y, bbox); + if (shape) + draw_glyph(NULL, shape, glyph, x, y, bbox); fz_drop_pixmap(dev->ctx, glyph); } } @@ -781,18 +732,17 @@ static void fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; - fz_pixmap *dest = dev->dest; fz_rect bounds; fz_bbox bbox, scissor; + fz_pixmap *dest, *shape; float colorfv[FZ_MAX_COLORS]; unsigned char colorbv[FZ_MAX_COLORS + 1]; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; bounds = fz_bound_shade(shade, ctm); - bbox = fz_intersect_bbox(fz_round_rect(bounds), dev->scissor); - scissor = dev->scissor; - - // TODO: proper clip by shade->bbox + scissor = state->scissor; + bbox = fz_intersect_bbox(fz_round_rect(bounds), scissor); if (fz_is_empty_rect(bbox)) return; @@ -803,15 +753,20 @@ fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) return; } + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); + + dest = state->dest; + shape = state->shape; + if (alpha < 1) { - dest = fz_new_pixmap_with_rect(dev->ctx, dev->dest->colorspace, bbox); + dest = fz_new_pixmap_with_rect(dev->ctx, state->dest->colorspace, bbox); fz_clear_pixmap(dest); + shape = fz_new_pixmap_with_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(shape); } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); - if (shade->use_background) { unsigned char *s; @@ -831,11 +786,11 @@ fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) *s++ = colorbv[i]; } } - if (dev->shape) + if (shape) { for (y = scissor.y0; y < scissor.y1; y++) { - s = dev->shape->samples + (scissor.x0 - dev->shape->x) + (y - dev->shape->y) * dev->shape->w; + s = shape->samples + (scissor.x0 - shape->x) + (y - shape->y) * shape->w; for (x = scissor.x0; x < scissor.x1; x++) { *s++ = 255; @@ -845,16 +800,21 @@ fz_draw_fill_shade(fz_device *devp, fz_shade *shade, fz_matrix ctm, float alpha) } fz_paint_shade(dev->ctx, shade, ctm, dest, bbox); - if (dev->shape) - fz_clear_pixmap_rect_with_color(dev->shape, 255, bbox); + if (shape) + fz_clear_pixmap_rect_with_color(shape, 255, bbox); if (alpha < 1) { - fz_paint_pixmap(dev->dest, dest, alpha * 255); + fz_paint_pixmap(state->dest, dest, alpha * 255); fz_drop_pixmap(dev->ctx, dest); + if (shape) + { + fz_paint_pixmap(state->shape, shape, alpha * 255); + fz_drop_pixmap(dev->ctx, shape); + } } - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -909,12 +869,13 @@ static void fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; fz_pixmap *converted = NULL; fz_pixmap *scaled = NULL; 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_var(scaled); @@ -931,8 +892,8 @@ fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha /* convert images with fewer components (gray->rgb after scaling */ /* convert images with expensive colorspace transforms after scaling */ - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); after = 0; if (image->colorspace == fz_device_gray) @@ -952,7 +913,7 @@ fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha if (dx < image->w && dy < image->h) { int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); - scaled = fz_transform_pixmap(ctx, image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit); + scaled = fz_transform_pixmap(ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit); if (!scaled) { if (dx < 1) @@ -980,7 +941,7 @@ fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha } } - fz_paint_image(dev->dest, dev->scissor, dev->shape, image, ctm, alpha * 255); + fz_paint_image(state->dest, state->scissor, state->shape, image, ctm, alpha * 255); } fz_catch(ctx) { @@ -994,7 +955,7 @@ fz_draw_fill_image(fz_device *devp, fz_pixmap *image, fz_matrix ctm, float alpha if (converted) fz_drop_pixmap(ctx, converted); - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -1003,25 +964,26 @@ fz_draw_fill_image_mask(fz_device *devp, fz_pixmap *image, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; unsigned char colorbv[FZ_MAX_COLORS + 1]; float colorfv[FZ_MAX_COLORS]; fz_pixmap *scaled = NULL; int dx, dy; int i; + fz_draw_state *state = &dev->stack[dev->top]; + fz_colorspace *model = state->dest->colorspace; if (image->w == 0 || image->h == 0) return; - if (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + state = fz_knockout_begin(dev); 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 = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3); - scaled = fz_transform_pixmap(dev->ctx, image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit); + scaled = fz_transform_pixmap(dev->ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit); if (!scaled) { if (dx < 1) @@ -1039,12 +1001,12 @@ fz_draw_fill_image_mask(fz_device *devp, fz_pixmap *image, fz_matrix ctm, colorbv[i] = colorfv[i] * 255; colorbv[i] = alpha * 255; - fz_paint_image_with_color(dev->dest, dev->scissor, dev->shape, image, ctm, colorbv); + fz_paint_image_with_color(state->dest, state->scissor, state->shape, image, ctm, colorbv); if (scaled) fz_drop_pixmap(dev->ctx, scaled); - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); } @@ -1052,7 +1014,6 @@ static void fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_matrix ctm) { fz_draw_device *dev = devp->user; - fz_colorspace *model = dev->dest->colorspace; fz_context *ctx = dev->ctx; fz_bbox bbox; fz_pixmap *mask = NULL; @@ -1060,31 +1021,29 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_pixmap *shape = NULL; fz_pixmap *scaled = NULL; int dx, dy; + fz_draw_state *state = push_stack(dev); + fz_colorspace *model = state->dest->colorspace; fz_var(mask); fz_var(dest); fz_var(shape); - 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++; +#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_round_rect(fz_transform_rect(ctm, fz_unit_rect)); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); if (rect) bbox = fz_intersect_bbox(bbox, fz_round_rect(*rect)); @@ -1093,22 +1052,19 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_try(ctx) { dest = fz_new_pixmap_with_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ fz_clear_pixmap(dest); - if (dev->shape) + if (state->shape) { shape = fz_new_pixmap_with_rect(dev->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(dev->ctx, image, &ctm, dev->dest->x, dev->dest->y, dx, dy, gridfit); + scaled = fz_transform_pixmap(dev->ctx, image, &ctm, state->dest->x, state->dest->y, dx, dy, gridfit); if (!scaled) { if (dx < 1) @@ -1129,81 +1085,67 @@ fz_draw_clip_image_mask(fz_device *devp, fz_pixmap *image, fz_rect *rect, fz_mat fz_rethrow(ctx); } - fz_paint_image(mask, bbox, dev->shape, image, ctm, 255); + fz_paint_image(mask, bbox, state->shape, image, ctm, 255); if (scaled) fz_drop_pixmap(dev->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++; + 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_pop_clip(fz_device *devp) { fz_draw_device *dev = devp->user; - fz_pixmap *mask, *dest, *shape; - if (dev->top > 0) + fz_context *ctx = dev->ctx; + fz_draw_state *state; + + 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); + fz_warn(ctx, "Unexpectd 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, dev->dest, "Clipping "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - fz_dump_blend(dev->ctx, dest, " onto "); - if (shape) - fz_dump_blend(dev->ctx, shape, "/"); - fz_dump_blend(dev->ctx, mask, " with "); + 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(dest, dev->dest, mask); - if (shape) - { - assert(shape != dev->shape); - fz_paint_pixmap_with_mask(shape, dev->shape, mask); - fz_drop_pixmap(dev->ctx, dev->shape); - dev->shape = shape; - } - fz_drop_pixmap(dev->ctx, mask); - fz_drop_pixmap(dev->ctx, dev->dest); - dev->dest = dest; + 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, dev->dest, " to get "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - printf("\n"); + 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 - { + } + else + { #ifdef DUMP_GROUP_BLENDS - dump_spaces(dev->top, "Clip End\n"); + dump_spaces(dev->top, "Clip End\n"); #endif - assert(!dest); - assert(shape == dev->shape); - } } } @@ -1212,16 +1154,14 @@ fz_draw_begin_mask(fz_device *devp, fz_rect rect, int luminosity, fz_colorspace { fz_draw_device *dev = devp->user; fz_pixmap *dest; - fz_pixmap *shape = dev->shape; fz_bbox bbox; - - if (dev->top == dev->stack_max) - fz_grow_stack(dev); + fz_draw_state *state = push_stack(dev); + fz_pixmap *shape = state->shape; bbox = fz_round_rect(rect); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_rect(dev->ctx, fz_device_gray, bbox); - if (dev->shape) + if (state->shape) { /* FIXME: If we ever want to support AIS true, then we * probably want to create a shape pixmap here, using: @@ -1249,97 +1189,80 @@ fz_draw_begin_mask(fz_device *devp, fz_rect rect, int luminosity, fz_colorspace 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"); + dump_spaces(dev->top-1, "Mask begin\n"); #endif - dev->top++; - - dev->scissor = bbox; - dev->dest = dest; - dev->shape = shape; + state[1].scissor = bbox; + state[1].dest = dest; + state[1].shape = shape; + state[1].luminosity = luminosity; } static void fz_draw_end_mask(fz_device *devp) { fz_draw_device *dev = devp->user; - fz_pixmap *mask = dev->dest; - fz_pixmap *maskshape = dev->shape; fz_pixmap *temp, *dest; fz_bbox bbox; int luminosity; + fz_context *ctx = dev->ctx; + fz_draw_state *state; - if (dev->top == dev->stack_max) - fz_grow_stack(dev); - - if (dev->top > 0) + 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(dev->ctx, mask, luminosity); - fz_drop_pixmap(dev->ctx, mask); - fz_drop_pixmap(dev->ctx, maskshape); - - /* create new dest scratch buffer */ - bbox = fz_bound_pixmap(temp); - dest = fz_new_pixmap_with_rect(dev->ctx, dev->dest->colorspace, bbox); - /* FIXME: See note #1 */ - fz_clear_pixmap(dest); + fz_warn(ctx, "Unexpected draw_end_mask"); + return; + } + state = &dev->stack[dev->top-1]; + /* pop soft mask buffer */ + luminosity = state[1].luminosity; - /* 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(dev->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"); + dump_spaces(dev->top-1, "Mask -> Clip\n"); #endif - dev->top++; + /* convert to alpha mask */ + temp = fz_alpha_from_gray(dev->ctx, state[1].dest, luminosity); + fz_drop_pixmap(dev->ctx, state[1].mask); + state[1].mask = NULL; + if (state[1].shape != state[0].shape) + fz_drop_pixmap(dev->ctx, state[1].shape); + state[1].shape = NULL; + + /* create new dest scratch buffer */ + bbox = fz_bound_pixmap(temp); + dest = fz_new_pixmap_with_rect(dev->ctx, state->dest->colorspace, bbox); + fz_clear_pixmap(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_rect(dev->ctx, NULL, bbox); + fz_clear_pixmap(state[1].shape); } + state[1].scissor = bbox; } 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_colorspace *model = dev->dest->colorspace; 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 (dev->top == dev->stack_max) - { - fz_warn(ctx, "assert: too many buffers on stack"); - return; - } - - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); + state = push_stack(dev); bbox = fz_round_rect(rect); - bbox = fz_intersect_bbox(bbox, dev->scissor); + bbox = fz_intersect_bbox(bbox, state->scissor); dest = fz_new_pixmap_with_rect(ctx, model, bbox); #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED @@ -1353,14 +1276,14 @@ fz_draw_begin_group(fz_device *devp, fz_rect rect, int isolated, int knockout, i } else { - fz_copy_pixmap_rect(dest, dev->dest, bbox); + fz_copy_pixmap_rect(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 = dev->shape; + shape = state[0].shape; } else { @@ -1376,83 +1299,74 @@ fz_draw_begin_group(fz_device *devp, fz_rect rect, int isolated, int knockout, i } } - 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; + state[1].alpha = alpha; #ifdef DUMP_GROUP_BLENDS - dump_spaces(dev->top, "Group Begin\n"); + dump_spaces(dev->top-1, "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); + 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_end_group(fz_device *devp) { fz_draw_device *dev = devp->user; - fz_pixmap *group = dev->dest; - fz_pixmap *shape = dev->shape; int blendmode; int isolated; float alpha; + fz_context *ctx = dev->ctx; + fz_draw_state *state; - if (dev->top > 0) + if (dev->top == 0) { - dev->top--; - alpha = dev->stack[dev->top].alpha; - 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; + 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, group, "Blending "); - if (shape) - fz_dump_blend(dev->ctx, shape, "/"); - fz_dump_blend(dev->ctx, dev->dest, " onto "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - if (alpha != 1.0f) - printf(" (alpha %g)", alpha); - if (blendmode != 0) - printf(" (blend %d)", blendmode); - if (isolated != 0) - printf(" (isolated)"); - if (blendmode & FZ_BLEND_KNOCKOUT) - printf(" (knockout)"); + dump_spaces(dev->top, ""); + fz_dump_blend(dev->ctx, state[1].dest, "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) && (!shape)) - fz_paint_pixmap(dev->dest, group, alpha * 255); - else - fz_blend_pixmap(dev->dest, group, alpha * 255, blendmode, isolated, shape); + 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, group); - if (shape != dev->shape) - { - if (dev->shape) - { - fz_paint_pixmap(dev->shape, shape, alpha * 255); - } - fz_drop_pixmap(dev->ctx, 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, dev->dest, " to get "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - printf("\n"); + 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 (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state[0].blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(dev); } @@ -1460,30 +1374,29 @@ 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_colorspace *model = dev->dest->colorspace; fz_pixmap *dest = NULL; - fz_pixmap *shape = dev->shape; + 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 (dev->top == dev->stack_max) - fz_grow_stack(dev); - - if (dev->blendmode & FZ_BLEND_KNOCKOUT) + if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_begin(dev); + state = push_stack(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. */ - 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); + 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_rect(dev->ctx, model, bbox); - /* FIXME: See note #1 */ fz_clear_pixmap(dest); + shape = state[0].shape; if (shape) { fz_var(shape); @@ -1498,102 +1411,96 @@ fz_draw_begin_tile(fz_device *devp, fz_rect area, fz_rect view, float xstep, flo fz_rethrow(ctx); } } - 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; + 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, "Tile begin\n"); + dump_spaces(dev->top-1, "Tile begin\n"); #endif - dev->top++; - dev->scissor = bbox; - dev->dest = dest; - dev->shape = shape; + state[1].scissor = bbox; + state[1].dest = dest; + state[1].shape = shape; } static void fz_draw_end_tile(fz_device *devp) { fz_draw_device *dev = devp->user; - fz_pixmap *tile = dev->dest; - fz_pixmap *tileshape = dev->shape; 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) + if (dev->top == 0) { - dev->top--; - 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->shape = dev->stack[dev->top].shape; - 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; - if (tileshape) - { - shapectm = ctm; - shapectm.e = tileshape->x; - shapectm.f = tileshape->y; - } + 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, tile, "Tiling "); - if (tileshape) - fz_dump_blend(dev->ctx, tileshape, "/"); - fz_dump_blend(dev->ctx, dev->dest, " onto "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); + 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 (y = y0; y < y1; y++) + { + for (x = x0; x < x1; x++) { - 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), ctm); - tile->x = ttm.e; - tile->y = ttm.f; - fz_paint_pixmap_with_rect(dev->dest, tile, 255, dev->scissor); - if (tileshape) - { - ttm = fz_concat(fz_translate(x * xstep, y * ystep), shapectm); - tileshape->x = ttm.e; - tileshape->y = ttm.f; - fz_paint_pixmap_with_rect(dev->shape, tileshape, 255, dev->scissor); - } + 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, tile); - fz_drop_pixmap(dev->ctx, tileshape); + 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, dev->dest, " to get "); - if (dev->shape) - fz_dump_blend(dev->ctx, dev->shape, "/"); - printf("\n"); + 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 (dev->blendmode & FZ_BLEND_KNOCKOUT) - fz_knockout_begin(dev); + if (state->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -1604,26 +1511,20 @@ fz_draw_free_user(fz_device *devp) /* pop and free the stacks */ if (dev->top > 0) { + fz_draw_state *state = &dev->stack[--dev->top]; fz_warn(ctx, "items left on stack in draw device: %d", dev->top); - dev->top--; - if (dev->dest != dev->stack[dev->top].dest) - fz_drop_pixmap(ctx, dev->dest); - if (dev->shape != dev->stack[dev->top].shape) - fz_drop_pixmap(ctx, dev->shape); - while(dev->top > 0) + + do { - fz_pixmap *mask = dev->stack[dev->top].mask; - fz_pixmap *dest = dev->stack[dev->top].dest; - fz_pixmap *shape = dev->stack[dev->top].shape; - - dev->top--; - if (mask != dev->stack[dev->top].mask) - fz_drop_pixmap(ctx, mask); - if (dest != dev->stack[dev->top].dest) - fz_drop_pixmap(ctx, dest); - if (shape != dev->stack[dev->top].shape) - fz_drop_pixmap(ctx, shape); + if (state[1].mask != state[0].mask) + fz_drop_pixmap(ctx, state[1].mask); + if (state[1].dest != state[0].dest) + fz_drop_pixmap(ctx, state[1].dest); + if (state[1].shape != state[0].shape) + fz_drop_pixmap(ctx, state[1].shape); + state--; } + while(--dev->top > 0); fz_drop_pixmap(ctx, dev->stack[0].mask); fz_drop_pixmap(ctx, dev->stack[0].dest); fz_drop_pixmap(ctx, dev->stack[0].shape); @@ -1639,26 +1540,27 @@ fz_new_draw_device(fz_context *ctx, fz_glyph_cache *cache, fz_pixmap *dest) { fz_device *dev = NULL; fz_draw_device *ddev = fz_malloc_struct(ctx, fz_draw_device); - ddev->gel = NULL; + + memset(ddev, 0, sizeof(*ddev)); fz_var(dev); fz_try(ctx) { ddev->cache = cache; ddev->gel = fz_new_gel(ctx); - ddev->dest = dest; - ddev->shape = NULL; - ddev->top = 0; - ddev->blendmode = 0; ddev->flags = 0; ddev->ctx = ctx; + ddev->top = 0; ddev->stack = &ddev->init_stack[0]; ddev->stack_max = STACK_SIZE; - - ddev->scissor.x0 = dest->x; - ddev->scissor.y0 = dest->y; - ddev->scissor.x1 = dest->x + dest->w; - ddev->scissor.y1 = dest->y + dest->h; + ddev->stack[0].dest = dest; + ddev->stack[0].shape = NULL; + ddev->stack[0].mask = NULL; + ddev->stack[0].blendmode = 0; + ddev->stack[0].scissor.x0 = dest->x; + ddev->stack[0].scissor.y0 = dest->y; + ddev->stack[0].scissor.x1 = dest->x + dest->w; + ddev->stack[0].scissor.y1 = dest->y + dest->h; dev = fz_new_device(ctx, ddev); } |