diff options
author | Robin Watts <Robin.Watts@artifex.com> | 2011-07-14 19:07:14 +0100 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2011-07-14 19:41:35 +0100 |
commit | 9aba12aaa87f1da7b9047d1a86ad0d02298193ff (patch) | |
tree | ae4eccccaee8a3b101a64842938416b89a3fb967 /draw/draw_device.c | |
parent | 6913145eefde0e5c4cde9573d8d2cb2a35fd605d (diff) | |
download | mupdf-9aba12aaa87f1da7b9047d1a86ad0d02298193ff.tar.xz |
Add Knockout group support.
While in a knockout group, we create a new group around every single
object drawn. These groups are either isolated or non-isolated as
required (but non-isolated ones seed their background from two
backgrounds up the stack, not one).
Diffstat (limited to 'draw/draw_device.c')
-rw-r--r-- | draw/draw_device.c | 172 |
1 files changed, 166 insertions, 6 deletions
diff --git a/draw/draw_device.c b/draw/draw_device.c index 67b766ca..46a3fa2b 100644 --- a/draw/draw_device.c +++ b/draw/draw_device.c @@ -18,6 +18,7 @@ struct fz_draw_device_s fz_bbox scissor; int top; + int blendmode; struct { fz_bbox scissor; fz_pixmap *dest; @@ -32,6 +33,97 @@ struct fz_draw_device_s } stack[STACK_SIZE]; }; +static void fz_knockout_begin(void *user) +{ + fz_draw_device *dev = user; + fz_bbox bbox; + fz_pixmap *dest, *shape; + + if ((dev->blendmode & FZ_BLEND_KNOCKOUT) == 0) + return; + + if (dev->top == STACK_SIZE) + { + fz_warn("assert: too many buffers on stack"); + return; + } + + bbox = fz_bound_pixmap(dev->dest); + bbox = fz_intersect_bbox(bbox, dev->scissor); + dest = fz_new_pixmap_with_rect(dev->dest->colorspace, bbox); + + if (dev->blendmode & FZ_BLEND_ISOLATED) + { + fz_clear_pixmap(dest); + shape = dev->shape; + } + else + { + fz_pixmap *prev; + int i = dev->top; + do { + prev = dev->stack[--i].dest; + } while (prev == NULL); + shape = fz_new_pixmap_with_rect(NULL, bbox); + fz_clear_pixmap(shape); + fz_copy_pixmap_rect(dest, prev, bbox); + } + 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; + dev->top++; + + dev->scissor = bbox; + dev->dest = dest; + dev->shape = shape; + dev->blendmode &= ~FZ_BLEND_MODEMASK; +} + +static void fz_knockout_end(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 == STACK_SIZE) + { + fz_warn("assert: too many buffers on stack"); + return; + } + + 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; + + 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(group); + if (shape != dev->shape) + { + if (dev->shape) + { + fz_paint_pixmap(dev->shape, shape, 255); + } + fz_drop_pixmap(shape); + } + } +} + static void fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm, fz_colorspace *colorspace, float *color, float alpha) @@ -55,6 +147,9 @@ fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm, if (fz_is_empty_rect(bbox)) return; + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + fz_convert_color(colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; @@ -70,6 +165,9 @@ fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm, colorbv[0] = 255; fz_scan_convert(dev->gel, even_odd, bbox, dev->shape, colorbv); } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -102,6 +200,9 @@ fz_draw_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matri if (fz_is_empty_rect(bbox)) return; + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + fz_convert_color(colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; @@ -120,6 +221,9 @@ fz_draw_stroke_path(void *user, fz_path *path, fz_stroke_state *stroke, fz_matri colorbv[0] = 255; fz_scan_convert(dev->gel, 0, bbox, dev->shape, colorbv); } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -154,6 +258,7 @@ fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_mat 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; dev->top++; return; @@ -177,6 +282,7 @@ fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_mat dev->stack[dev->top].mask = mask; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; @@ -234,6 +340,7 @@ fz_draw_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_sta dev->stack[dev->top].mask = mask; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; @@ -289,6 +396,9 @@ fz_draw_fill_text(void *user, fz_text *text, fz_matrix ctm, fz_pixmap *glyph; int i, x, y, gid; + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + fz_convert_color(colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; @@ -320,6 +430,9 @@ fz_draw_fill_text(void *user, fz_text *text, fz_matrix ctm, fz_drop_pixmap(glyph); } } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -334,6 +447,9 @@ fz_draw_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matri fz_pixmap *glyph; int i, x, y, gid; + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + fz_convert_color(colorspace, color, model, colorfv); for (i = 0; i < model->n; i++) colorbv[i] = colorfv[i] * 255; @@ -364,6 +480,9 @@ fz_draw_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_matri fz_drop_pixmap(glyph); } } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -417,6 +536,7 @@ fz_draw_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate) dev->stack[dev->top].mask = mask; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; @@ -494,6 +614,7 @@ fz_draw_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_ dev->stack[dev->top].mask = mask; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; @@ -566,6 +687,9 @@ fz_draw_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha) fz_clear_pixmap(dest); } + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + if (shade->use_background) { unsigned char *s; @@ -607,6 +731,9 @@ fz_draw_fill_shade(void *user, fz_shade *shade, fz_matrix ctm, float alpha) fz_paint_pixmap(dev->dest, dest, alpha * 255); fz_drop_pixmap(dest); } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static fz_pixmap * @@ -670,6 +797,9 @@ fz_draw_fill_image(void *user, 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); + after = 0; if (image->colorspace == fz_device_gray) after = 1; @@ -719,6 +849,9 @@ fz_draw_fill_image(void *user, fz_pixmap *image, fz_matrix ctm, float alpha) fz_drop_pixmap(scaled); if (converted) fz_drop_pixmap(converted); + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -736,6 +869,9 @@ fz_draw_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm, if (image->w == 0 || image->h == 0) return; + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + 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) @@ -762,6 +898,9 @@ fz_draw_fill_image_mask(void *user, fz_pixmap *image, fz_matrix ctm, if (scaled) fz_drop_pixmap(scaled); + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); } static void @@ -785,6 +924,7 @@ fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix c 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; @@ -833,6 +973,7 @@ fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix c dev->stack[dev->top].mask = mask; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->scissor = bbox; dev->dest = dest; dev->shape = shape; @@ -851,6 +992,7 @@ fz_draw_pop_clip(void *user) 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 @@ -912,7 +1054,7 @@ fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colo dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].luminosity = luminosity; dev->stack[dev->top].shape = dev->shape; - /* FIXME: More shape stuff? */ + dev->stack[dev->top].blendmode = dev->blendmode; dev->top++; dev->scissor = bbox; @@ -955,6 +1097,7 @@ fz_draw_end_mask(void *user) dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].mask = temp; dev->stack[dev->top].dest = dev->dest; + dev->stack[dev->top].blendmode = dev->blendmode; /* 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) @@ -983,6 +1126,9 @@ fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int bl return; } + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + bbox = fz_round_rect(rect); bbox = fz_intersect_bbox(bbox, dev->scissor); dest = fz_new_pixmap_with_rect(model, bbox); @@ -1000,7 +1146,7 @@ fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int bl } dev->stack[dev->top].alpha = alpha; - dev->stack[dev->top].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); + 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; @@ -1009,6 +1155,7 @@ fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int bl dev->scissor = bbox; dev->dest = dest; dev->shape = shape; + dev->blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0); } static void @@ -1018,16 +1165,16 @@ fz_draw_end_group(void *user) fz_pixmap *group = dev->dest; fz_pixmap *shape = dev->shape; int blendmode; - int isolated, knockout; + int isolated; float alpha; if (dev->top > 0) { dev->top--; alpha = dev->stack[dev->top].alpha; - blendmode = dev->stack[dev->top].blendmode & FZ_BLEND_MODEMASK; - isolated = dev->stack[dev->top].blendmode & FZ_BLEND_ISOLATED; - knockout = dev->stack[dev->top].blendmode & FZ_BLEND_KNOCKOUT; + 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; @@ -1047,6 +1194,9 @@ fz_draw_end_group(void *user) fz_drop_pixmap(shape); } } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_end(dev); } static void @@ -1066,6 +1216,9 @@ fz_draw_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ys return; } + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); + bbox = fz_round_rect(fz_transform_rect(ctm, view)); dest = fz_new_pixmap_with_rect(model, bbox); fz_clear_pixmap(dest); @@ -1073,6 +1226,7 @@ fz_draw_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ys dev->stack[dev->top].scissor = dev->scissor; dev->stack[dev->top].dest = dev->dest; dev->stack[dev->top].shape = dev->shape; + dev->stack[dev->top].blendmode = dev->blendmode; dev->stack[dev->top].xstep = xstep; dev->stack[dev->top].ystep = ystep; dev->stack[dev->top].area = area; @@ -1102,6 +1256,8 @@ fz_draw_end_tile(void *user) 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); @@ -1124,6 +1280,9 @@ fz_draw_end_tile(void *user) fz_drop_pixmap(tile); } + + if (dev->blendmode & FZ_BLEND_KNOCKOUT) + fz_knockout_begin(dev); } static void @@ -1147,6 +1306,7 @@ fz_new_draw_device(fz_glyph_cache *cache, fz_pixmap *dest) ddev->dest = dest; ddev->shape = NULL; ddev->top = 0; + ddev->blendmode = 0; ddev->scissor.x0 = dest->x; ddev->scissor.y0 = dest->y; |