summaryrefslogtreecommitdiff
path: root/draw/draw_device.c
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2011-08-04 12:22:16 +0100
committerRobin Watts <Robin.Watts@artifex.com>2011-08-04 12:22:16 +0100
commitb2a56e3f513626476abd55cede7efbda9104b25e (patch)
tree3183867e11da4df69a1f35c9043cd80e09a32f51 /draw/draw_device.c
parent346f414afadf87f65227c2c66eb6afbadc49fa78 (diff)
downloadmupdf-b2a56e3f513626476abd55cede7efbda9104b25e.tar.xz
Fix part of bug 692377: "hivemind" PDF regression
Comment #3 on bug 692377 notes that the "Hivemind" pdf (from bug 692203) has regressed; this is due to the new shape code not working quite right with mask groups. Fixed here. It also showed up a bug in the shape code with filled paths at non full alpha. Also fixed. In tracking this down, I've extended the DUMP_GROUP_BLENDS debugging code to give more readable output. Also committed here. This still leaves a regression in l16.pdf which the bug was originally reopened about, but that's a much more minor one.
Diffstat (limited to 'draw/draw_device.c')
-rw-r--r--draw/draw_device.c154
1 files changed, 145 insertions, 9 deletions
diff --git a/draw/draw_device.c b/draw/draw_device.c
index aadef905..f3ced47d 100644
--- a/draw/draw_device.c
+++ b/draw/draw_device.c
@@ -9,6 +9,20 @@
/* 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 {
@@ -58,6 +72,16 @@ static void fz_dump_blend(fz_pixmap *pix, const char *s)
fz_write_png(pix, name, (pix->n > 1));
}
+
+static void dump_spaces(int x, const char *s)
+{
+ int i;
+
+ for (i=0; i < x; i++)
+ printf(" ");
+ printf("%s", s);
+}
+
#endif
static void fz_knockout_begin(void *user)
@@ -109,6 +133,9 @@ static void fz_knockout_begin(void *user)
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");
+#endif
dev->top++;
dev->scissor = bbox;
@@ -144,6 +171,20 @@ static void fz_knockout_end(void *user)
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
@@ -158,6 +199,12 @@ static void fz_knockout_end(void *user)
}
fz_drop_pixmap(shape);
}
+#ifdef DUMP_GROUP_BLENDS
+ fz_dump_blend(dev->dest, " to get ");
+ if (dev->shape)
+ fz_dump_blend(dev->shape, "/");
+ printf("\n");
+#endif
}
}
@@ -199,7 +246,7 @@ fz_draw_fill_path(void *user, fz_path *path, int even_odd, fz_matrix ctm,
fz_flatten_fill_path(dev->gel, path, ctm, flatness);
fz_sort_gel(dev->gel);
- colorbv[0] = 255;
+ colorbv[0] = alpha * 255;
fz_scan_convert(dev->gel, even_odd, bbox, dev->shape, colorbv);
}
@@ -297,6 +344,9 @@ fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_mat
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;
}
@@ -304,6 +354,7 @@ fz_draw_clip_path(void *user, fz_path *path, fz_rect *rect, int even_odd, fz_mat
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
+ /* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
@@ -319,10 +370,14 @@ 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;
+ /* 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++;
}
@@ -361,6 +416,7 @@ fz_draw_clip_stroke_path(void *user, fz_path *path, fz_rect *rect, fz_stroke_sta
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
+ /* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
@@ -377,10 +433,14 @@ 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;
+ /* 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++;
}
@@ -568,6 +628,7 @@ fz_draw_clip_text(void *user, fz_text *text, fz_matrix ctm, int accumulate)
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
+ /* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
@@ -581,10 +642,14 @@ 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;
+ /* 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
@@ -646,6 +711,7 @@ fz_draw_clip_stroke_text(void *user, fz_text *text, fz_stroke_state *stroke, fz_
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
+ /* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
@@ -659,10 +725,14 @@ 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;
+ /* 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))
@@ -964,6 +1034,10 @@ fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix c
return;
}
+#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;
@@ -983,6 +1057,7 @@ fz_draw_clip_image_mask(void *user, fz_pixmap *image, fz_rect *rect, fz_matrix c
mask = fz_new_pixmap_with_rect(NULL, bbox);
fz_clear_pixmap(mask);
dest = fz_new_pixmap_with_rect(model, bbox);
+ /* FIXME: See note #1 */
fz_clear_pixmap(dest);
if (dev->shape)
{
@@ -1018,7 +1093,8 @@ 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;
+ /* FIXME: See note #1 */
+ dev->stack[dev->top].blendmode = dev->blendmode | FZ_BLEND_ISOLATED;
dev->scissor = bbox;
dev->dest = dest;
dev->shape = shape;
@@ -1047,6 +1123,16 @@ fz_draw_pop_clip(void *user)
{
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)
{
@@ -1058,9 +1144,18 @@ fz_draw_pop_clip(void *user)
fz_drop_pixmap(mask);
fz_drop_pixmap(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);
}
@@ -1072,6 +1167,7 @@ fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colo
{
fz_draw_device *dev = user;
fz_pixmap *dest;
+ fz_pixmap *shape = dev->shape;
fz_bbox bbox;
if (dev->top == STACK_SIZE)
@@ -1083,6 +1179,16 @@ fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colo
bbox = fz_round_rect(rect);
bbox = fz_intersect_bbox(bbox, dev->scissor);
dest = fz_new_pixmap_with_rect(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)
{
@@ -1091,19 +1197,29 @@ fz_draw_begin_mask(void *user, fz_rect rect, int luminosity, fz_colorspace *colo
colorspace = fz_device_gray;
fz_convert_color(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
@@ -1111,6 +1227,7 @@ fz_draw_end_mask(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;
@@ -1128,21 +1245,25 @@ fz_draw_end_mask(void *user)
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(mask, luminosity);
fz_drop_pixmap(mask);
+ fz_drop_pixmap(maskshape);
/* create new dest scratch buffer */
bbox = fz_bound_pixmap(temp);
dest = fz_new_pixmap_with_rect(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;
- dev->stack[dev->top].blendmode = dev->blendmode;
+ /* 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)
@@ -1153,6 +1274,9 @@ fz_draw_end_mask(void *user)
}
dev->scissor = bbox;
dev->dest = dest;
+#ifdef DUMP_GROUP_BLENDS
+ dump_spaces(dev->top, "Mask -> Clip\n");
+#endif
dev->top++;
}
}
@@ -1204,6 +1328,9 @@ fz_draw_begin_group(void *user, fz_rect rect, int isolated, int knockout, int bl
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;
@@ -1234,6 +1361,7 @@ fz_draw_end_group(void *user)
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, "/");
@@ -1246,7 +1374,7 @@ fz_draw_end_group(void *user)
printf(" (blend %d)", blendmode);
if (isolated != 0)
printf(" (isolated)");
- if (blendmode != 0)
+ if (blendmode & FZ_BLEND_KNOCKOUT)
printf(" (knockout)");
#endif
if ((blendmode == 0) && (shape == NULL))
@@ -1297,16 +1425,21 @@ fz_draw_begin_tile(void *user, fz_rect area, fz_rect view, float xstep, float ys
bbox = fz_round_rect(fz_transform_rect(ctm, view));
dest = fz_new_pixmap_with_rect(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;
- dev->stack[dev->top].blendmode = dev->blendmode;
+ /* 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;
@@ -1326,6 +1459,9 @@ fz_draw_end_tile(void *user)
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;