summaryrefslogtreecommitdiff
path: root/source/fitz/draw-device.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-09-01 16:54:57 +0100
committerRobin Watts <robin.watts@artifex.com>2017-10-24 15:16:36 +0100
commitc7c407b57a6e1aa5b1a11034ec7cb308cf4ad3c4 (patch)
tree5e3d13a07dcc007de2474027433644ecae3e68e7 /source/fitz/draw-device.c
parentb940c2fda5de510ea814ce63c903bcffb99b92db (diff)
downloadmupdf-c7c407b57a6e1aa5b1a11034ec7cb308cf4ad3c4.tar.xz
Add group_alpha plane to draw device and painters.
Diffstat (limited to 'source/fitz/draw-device.c')
-rw-r--r--source/fitz/draw-device.c311
1 files changed, 253 insertions, 58 deletions
diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c
index 27229108..ff6097fb 100644
--- a/source/fitz/draw-device.c
+++ b/source/fitz/draw-device.c
@@ -31,6 +31,7 @@ struct fz_draw_state_s {
fz_pixmap *dest;
fz_pixmap *mask;
fz_pixmap *shape;
+ fz_pixmap *group_alpha;
int blendmode;
int id, encache;
float alpha;
@@ -198,6 +199,8 @@ static void emergency_pop_stack(fz_context *ctx, fz_draw_device *dev, fz_draw_st
fz_drop_pixmap(ctx, state[1].dest);
if (state[1].shape != state[0].shape)
fz_drop_pixmap(ctx, state[1].shape);
+ if (state[1].group_alpha != state[0].group_alpha)
+ fz_drop_pixmap(ctx, state[1].group_alpha);
dev->top--;
STACK_POPPED("emergency");
fz_rethrow(ctx);
@@ -242,13 +245,14 @@ fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
fz_clear_pixmap(ctx, dest);
}
+ /* Knockout groups (and only knockout groups) rely on shape */
shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, shape);
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top-1, "");
fz_dump_blend(ctx, "Knockout begin: background is ", dest);
if (shape)
- fz_dump_blend(ctx, "/", shape);
+ fz_dump_blend(ctx, "/S=", shape);
printf("\n");
#endif
state[1].scissor = bbox;
@@ -282,20 +286,22 @@ static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Knockout end: blending ", state[1].dest);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
fz_dump_blend(ctx, " onto ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
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(ctx, state[0].dest, state[1].dest, 255, blendmode, isolated, state[1].shape);
+ assert(state[1].shape);
+ fz_blend_pixmap(ctx, state[0].dest, state[1].dest, 255, blendmode, 0, state[1].shape);
/* The following test should not be required, but just occasionally
* errors can cause the stack to get out of sync, and this saves our
@@ -308,10 +314,18 @@ static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
fz_paint_pixmap(state[0].shape, state[1].shape, 255);
fz_drop_pixmap(ctx, state[1].shape);
}
+ if (state[0].group_alpha != state[1].group_alpha)
+ {
+ if (state[0].group_alpha)
+ fz_paint_pixmap(state[0].group_alpha, state[1].group_alpha, 255);
+ fz_drop_pixmap(ctx, state[1].group_alpha);
+ }
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, " to get ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
printf("\n");
#endif
}
@@ -557,9 +571,17 @@ fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve
if (!rast->fns.reusable)
fz_flatten_fill_path(ctx, rast, path, &ctm, flatness, &bbox, NULL);
- colorbv[0] = alpha * 255;
+ colorbv[0] = 255;
fz_convert_rasterizer(ctx, rast, even_odd, state->shape, colorbv, 0);
}
+ if (state->group_alpha)
+ {
+ if (!rast->fns.reusable)
+ fz_flatten_fill_path(ctx, rast, path, &ctm, flatness, &bbox, NULL);
+
+ colorbv[0] = alpha * 255;
+ fz_convert_rasterizer(ctx, rast, even_odd, state->group_alpha, colorbv, 0);
+ }
if (state->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(ctx, dev);
@@ -607,7 +629,9 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Before stroke ", state->dest);
if (state->shape)
- fz_dump_blend(ctx, "/", state->shape);
+ fz_dump_blend(ctx, "/S=", state->shape);
+ if (state->group_alpha)
+ fz_dump_blend(ctx, "/GA=", state->group_alpha);
printf("\n");
#endif
fz_convert_rasterizer(ctx, rast, 0, state->dest, colorbv, eop);
@@ -619,11 +643,21 @@ fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const
colorbv[0] = 255;
fz_convert_rasterizer(ctx, rast, 0, state->shape, colorbv, 0);
}
+ if (state->group_alpha)
+ {
+ if (!rast->fns.reusable)
+ (void)fz_flatten_stroke_path(ctx, rast, path, stroke, &ctm, flatness, linewidth, &bbox, NULL);
+
+ colorbv[0] = 255 * alpha;
+ fz_convert_rasterizer(ctx, rast, 0, state->group_alpha, colorbv, 0);
+ }
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "After stroke ", state->dest);
if (state->shape)
- fz_dump_blend(ctx, "/", state->shape);
+ fz_dump_blend(ctx, "/S=", state->shape);
+ if (state->group_alpha)
+ fz_dump_blend(ctx, "/GA=", state->group_alpha);
printf("\n");
#endif
@@ -685,6 +719,11 @@ fz_draw_clip_path(fz_context *ctx, fz_device *devp, const fz_path *path, int eve
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, state[1].shape);
}
+ if (state[1].group_alpha)
+ {
+ state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, state[1].group_alpha);
+ }
fz_convert_rasterizer(ctx, rast, even_odd, state[1].mask, NULL, 0);
@@ -767,6 +806,11 @@ fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path,
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, state[1].shape);
}
+ if (state->group_alpha)
+ {
+ state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, state[1].group_alpha);
+ }
fz_convert_rasterizer(ctx, rast, 0, state[1].mask, NULL, 0);
@@ -860,7 +904,7 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
fz_draw_state *state = &dev->stack[dev->top];
fz_colorspace *model = state->dest->colorspace;
unsigned char colorbv[FZ_MAX_COLORS + 1];
- unsigned char shapebv;
+ unsigned char shapebv, shapebva;
fz_text_span *span;
int i;
fz_colorspace *colorspace = NULL;
@@ -885,6 +929,7 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
shapebv = 255;
+ shapebva = 255 * alpha;
for (span = text->head; span; span = span->next)
{
@@ -915,13 +960,15 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
if (state->shape)
draw_glyph(&shapebv, state->shape, glyph, x, y, &state->scissor, 0);
+ if (state->group_alpha)
+ draw_glyph(&shapebva, state->group_alpha, glyph, x, y, &state->scissor, 0);
}
else
{
fz_matrix mat;
mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
mat.e = x + pixmap->x; mat.f = y + pixmap->y;
- fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
+ fz_paint_image(state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, &mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
}
fz_drop_glyph(ctx, glyph);
}
@@ -953,6 +1000,8 @@ fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const
fz_matrix ctm = concat(in_ctm, &dev->transform);
fz_draw_state *state = &dev->stack[dev->top];
unsigned char colorbv[FZ_MAX_COLORS + 1];
+ unsigned char solid = 255;
+ unsigned char alpha_byte = alpha * 255;
fz_text_span *span;
int i;
fz_colorspace *colorspace = NULL;
@@ -996,7 +1045,9 @@ fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const
int y = (int)trm.f;
draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
if (state->shape)
- draw_glyph(colorbv, state->shape, glyph, x, y, &state->scissor, 0);
+ draw_glyph(&solid, state->shape, glyph, x, y, &state->scissor, 0);
+ if (state->group_alpha)
+ draw_glyph(&alpha_byte, state->group_alpha, glyph, x, y, &state->scissor, 0);
fz_drop_glyph(ctx, glyph);
}
else
@@ -1025,7 +1076,7 @@ fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
fz_draw_device *dev = (fz_draw_device*)devp;
fz_matrix ctm = concat(in_ctm, &dev->transform);
fz_irect bbox;
- fz_pixmap *mask, *dest, *shape;
+ fz_pixmap *mask, *dest, *shape, *group_alpha;
fz_matrix tm, trm;
fz_glyph *glyph;
int i, gid;
@@ -1073,12 +1124,20 @@ fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
}
else
shape = NULL;
+ if (state->group_alpha)
+ {
+ group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, group_alpha);
+ }
+ else
+ group_alpha = NULL;
state[1].blendmode |= FZ_BLEND_ISOLATED;
state[1].scissor = bbox;
state[1].dest = dest;
state[1].mask = mask;
state[1].shape = shape;
+ state[1].group_alpha = group_alpha;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top-1, "Clip (text) begin\n");
#endif
@@ -1107,6 +1166,8 @@ fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, const f
draw_glyph(NULL, mask, glyph, x, y, &bbox, 0);
if (state[1].shape)
draw_glyph(NULL, state[1].shape, glyph, x, y, &bbox, 0);
+ if (state[1].group_alpha)
+ draw_glyph(NULL, state[1].group_alpha, glyph, x, y, &bbox, 0);
fz_drop_glyph(ctx, glyph);
}
else
@@ -1157,7 +1218,7 @@ fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text,
fz_draw_device *dev = (fz_draw_device*)devp;
fz_matrix ctm = concat(in_ctm, &dev->transform);
fz_irect bbox;
- fz_pixmap *mask, *dest, *shape;
+ fz_pixmap *mask, *dest, *shape, *group_alpha;
fz_matrix tm, trm;
fz_glyph *glyph;
int i, gid;
@@ -1202,6 +1263,13 @@ fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text,
}
else
shape = state->shape;
+ if (state->group_alpha)
+ {
+ state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, group_alpha);
+ }
+ else
+ group_alpha = NULL;
state[1].blendmode |= FZ_BLEND_ISOLATED;
state[1].scissor = bbox;
@@ -1233,6 +1301,8 @@ fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text,
draw_glyph(NULL, mask, glyph, x, y, &bbox, 0);
if (shape)
draw_glyph(NULL, shape, glyph, x, y, &bbox, 0);
+ if (group_alpha)
+ draw_glyph(NULL, group_alpha, glyph, x, y, &bbox, 0);
fz_drop_glyph(ctx, glyph);
}
else
@@ -1289,8 +1359,9 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
fz_matrix ctm = concat(in_ctm, &dev->transform);
fz_rect bounds;
fz_irect bbox, scissor;
- fz_pixmap *dest, *shape;
+ fz_pixmap *dest, *shape, *group_alpha;
unsigned char colorbv[FZ_MAX_COLORS + 1];
+ unsigned char alpha_byte = 255 * alpha;
fz_draw_state *state = &dev->stack[dev->top];
fz_overprint op = { { 0 } };
fz_overprint *eop;
@@ -1314,6 +1385,7 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
dest = state->dest;
shape = state->shape;
+ group_alpha = state->group_alpha;
if (alpha < 1)
{
@@ -1327,6 +1399,11 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, shape);
}
+ if (group_alpha)
+ {
+ group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, group_alpha);
+ }
}
if (shade->use_background)
@@ -1383,6 +1460,17 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
}
}
}
+ if (group_alpha)
+ {
+ for (y = scissor.y0; y < scissor.y1; y++)
+ {
+ s = group_alpha->samples + (unsigned int)((y - group_alpha->y) * group_alpha->stride + (scissor.x0 - group_alpha->x));
+ for (x = scissor.x0; x < scissor.x1; x++)
+ {
+ *s++ = alpha_byte;
+ }
+ }
+ }
}
if (color_params->op)
@@ -1395,12 +1483,16 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
fz_paint_shade(ctx, shade, colorspace, &ctm, dest, color_params, &bbox, eop);
if (shape)
fz_clear_pixmap_rect_with_value(ctx, shape, 255, &bbox);
+ if (group_alpha)
+ fz_clear_pixmap_rect_with_value(ctx, group_alpha, 255, &bbox);
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Shade ", dest);
if (shape)
- fz_dump_blend(ctx, "/", shape);
+ fz_dump_blend(ctx, "/S=", shape);
+ if (group_alpha)
+ fz_dump_blend(ctx, "/GA=", group_alpha);
printf("\n");
#endif
@@ -1411,9 +1503,14 @@ fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, const fz_m
fz_drop_pixmap(ctx, dest);
if (shape)
{
- fz_paint_pixmap(state->shape, shape, alpha * 255);
+ fz_paint_pixmap(state->shape, shape, 255);
fz_drop_pixmap(ctx, shape);
}
+ if (group_alpha)
+ {
+ fz_paint_pixmap(state->group_alpha, group_alpha, alpha * 255);
+ fz_drop_pixmap(ctx, group_alpha);
+ }
}
if (state->blendmode & FZ_BLEND_KNOCKOUT)
@@ -1650,7 +1747,7 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m
pixmap = convert_pixmap_for_painting(ctx, pixmap, model, src_cs, state->dest, color_params, dev, &eop);
}
- fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
+ fz_paint_image(state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, &local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
if (state->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(ctx, dev);
@@ -1759,7 +1856,7 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
- fz_paint_image_with_color(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
+ fz_paint_image_with_color(state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, &local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
if (state->blendmode & FZ_BLEND_KNOCKOUT)
fz_knockout_end(ctx, dev);
@@ -1837,6 +1934,11 @@ fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, state[1].shape);
}
+ if (state[0].group_alpha)
+ {
+ state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, state[1].group_alpha);
+ }
state[1].blendmode |= FZ_BLEND_ISOLATED;
state[1].scissor = bbox;
@@ -1864,13 +1966,17 @@ fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const
fz_dump_blend(ctx, "Creating imagemask: plotting ", pixmap);
fz_dump_blend(ctx, " onto ", state[1].mask);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
#endif
- fz_paint_image(state[1].mask, &bbox, state[1].shape, pixmap, &local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
+ fz_paint_image(state[1].mask, &bbox, state[1].shape, state[1].group_alpha, pixmap, &local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, " to get ", state[1].mask);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
printf("\n");
#endif
}
@@ -1903,10 +2009,14 @@ fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Clipping ", state[1].dest);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
fz_dump_blend(ctx, " onto ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
fz_dump_blend(ctx, " with ", state[1].mask);
#endif
fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask);
@@ -1915,6 +2025,11 @@ fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask);
fz_drop_pixmap(ctx, state[1].shape);
}
+ if (state[0].group_alpha != state[1].group_alpha)
+ {
+ fz_paint_pixmap_with_mask(state[0].group_alpha, state[1].group_alpha, state[1].mask);
+ fz_drop_pixmap(ctx, state[1].group_alpha);
+ }
/* The following tests should not be required, but just occasionally
* errors can cause the stack to get out of sync, and this might save
* our bacon. */
@@ -1925,7 +2040,9 @@ fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, " to get ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
printf("\n");
#endif
}
@@ -1945,6 +2062,7 @@ fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int lu
fz_irect bbox;
fz_draw_state *state = push_stack(ctx, dev);
fz_pixmap *shape = state->shape;
+ fz_pixmap *group_alpha = state->group_alpha;
fz_rect trect = *rect;
fz_colorspace *colorspace = NULL;
@@ -1984,6 +2102,10 @@ fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int lu
*/
state[1].shape = shape = NULL;
}
+ if (state->group_alpha)
+ {
+ state[1].group_alpha = group_alpha = NULL;
+ }
if (luminosity)
{
@@ -1994,12 +2116,16 @@ fz_draw_begin_mask(fz_context *ctx, fz_device *devp, const fz_rect *rect, int lu
fz_clear_pixmap_with_value(ctx, dest, bc * 255);
if (shape)
fz_clear_pixmap_with_value(ctx, shape, 255);
+ if (group_alpha)
+ fz_clear_pixmap_with_value(ctx, group_alpha, 255);
}
else
{
fz_clear_pixmap(ctx, dest);
if (shape)
fz_clear_pixmap(ctx, shape);
+ if (group_alpha)
+ fz_clear_pixmap(ctx, group_alpha);
}
#ifdef DUMP_GROUP_BLENDS
@@ -2033,7 +2159,9 @@ fz_draw_end_mask(fz_context *ctx, fz_device *devp)
dump_spaces(dev->top-1, "Mask -> Clip: ");
fz_dump_blend(ctx, "Mask ", state[1].dest);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
#endif
fz_try(ctx)
{
@@ -2048,6 +2176,9 @@ fz_draw_end_mask(fz_context *ctx, fz_device *devp)
if (state[1].shape != state[0].shape)
fz_drop_pixmap(ctx, state[1].shape);
state[1].shape = NULL;
+ if (state[1].group_alpha != state[0].group_alpha)
+ fz_drop_pixmap(ctx, state[1].group_alpha);
+ state[1].group_alpha = NULL;
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, "-> Clip ", temp);
@@ -2069,6 +2200,11 @@ fz_draw_end_mask(fz_context *ctx, fz_device *devp)
state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, state[1].shape);
}
+ if (state[0].group_alpha)
+ {
+ state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, state[1].group_alpha);
+ }
state[1].scissor = bbox;
}
fz_catch(ctx)
@@ -2113,18 +2249,13 @@ fz_draw_begin_group(fz_context *ctx, fz_device *devp, const fz_rect *rect, fz_co
if (isolated)
{
fz_clear_pixmap(ctx, dest);
+ state[1].group_alpha = NULL;
}
else
{
fz_copy_pixmap_rect(ctx, dest, state[0].dest, &bbox, dev->default_cs);
- }
-
- if (isolated)
- state[1].shape = NULL;
- else
- {
- state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
- fz_clear_pixmap(ctx, state[1].shape);
+ state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, state[1].group_alpha);
}
state[1].alpha = alpha;
@@ -2135,18 +2266,20 @@ fz_draw_begin_group(fz_context *ctx, fz_device *devp, const fz_rect *rect, fz_co
char atext[80];
char btext[80];
if (alpha != 1)
- sprintf(atext, " (alpha=%g)", alpha);
+ sprintf(atext, " (alpha %g)", alpha);
else
atext[0] = 0;
if (blendmode != 0)
- sprintf(btext, " (blendmode=%d)", blendmode);
+ sprintf(btext, " (blend %d)", blendmode);
else
btext[0] = 0;
sprintf(text, "Group begin%s%s%s%s: background is ", isolated ? " (isolated)" : "", knockout ? " (knockout)" : "", atext, btext);
fz_dump_blend(ctx, text, state[1].dest);
}
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
printf("\n");
#endif
@@ -2183,10 +2316,14 @@ fz_draw_end_group(fz_context *ctx, fz_device *devp)
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Group end: blending ", state[1].dest);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
fz_dump_blend(ctx, " onto ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
if (alpha != 1.0f)
printf(" (alpha %g)", alpha);
if (blendmode != 0)
@@ -2203,20 +2340,33 @@ fz_draw_end_group(fz_context *ctx, fz_device *devp)
state[1].dest = converted;
}
- if ((blendmode == 0) && (state[0].shape == state[1].shape))
+ if ((blendmode == 0) && (state[0].shape == state[1].shape) && (state[0].group_alpha == state[1].group_alpha))
fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255);
else
- fz_blend_pixmap(ctx, state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].shape);
+ fz_blend_pixmap(ctx, state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].group_alpha);
- assert(state[0].shape == NULL || state[0].shape != state[1].shape);
- if (state[0].shape && state[0].shape != state[1].shape)
+ if (state[0].shape != state[1].shape)
{
- if (state[1].shape)
- fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255);
+ if (state[0].shape)
+ {
+ if (state[1].shape)
+ fz_paint_pixmap(state[0].shape, state[1].shape, 255);
+ else
+ fz_paint_pixmap_alpha(state[0].shape, state[1].dest, 255);
+ }
+ fz_drop_pixmap(ctx, state[1].shape);
+ }
+ assert(state[0].group_alpha == NULL || state[0].group_alpha != state[1].group_alpha);
+ if (state[0].group_alpha && state[0].group_alpha != state[1].group_alpha)
+ {
+ /* The 'D' on page 7 of Altona_Technical_v20_x4.pdf goes wrong if this
+ * is 255 * alpha, as an alpha effectively gets applied twice. */
+ if (state[1].group_alpha)
+ fz_paint_pixmap(state[0].group_alpha, state[1].group_alpha, 255);
else
- fz_paint_pixmap_alpha(state[0].shape, state[1].dest, alpha * 255);
+ fz_paint_pixmap_alpha(state[0].group_alpha, state[1].dest, 255);
}
- fz_drop_pixmap(ctx, state[1].shape);
+ fz_drop_pixmap(ctx, state[1].group_alpha);
/* The following test should not be required, but just occasionally
* errors can cause the stack to get out of sync, and this might save
* our bacon. */
@@ -2226,7 +2376,9 @@ fz_draw_end_group(fz_context *ctx, fz_device *devp)
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, " to get ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
printf("\n");
#endif
@@ -2247,6 +2399,7 @@ typedef struct
fz_storable storable;
fz_pixmap *dest;
fz_pixmap *shape;
+ fz_pixmap *group_alpha;
} tile_record;
static int
@@ -2318,6 +2471,7 @@ fz_drop_tile_record_imp(fz_context *ctx, fz_storable *storable)
tile_record *tr = (tile_record *)storable;
fz_drop_pixmap(ctx, tr->dest);
fz_drop_pixmap(ctx, tr->shape);
+ fz_drop_pixmap(ctx, tr->group_alpha);
fz_free(ctx, tr);
}
@@ -2328,12 +2482,13 @@ fz_drop_tile_record(fz_context *ctx, tile_record *tile)
}
static tile_record *
-fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape)
+fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape, fz_pixmap *group_alpha)
{
tile_record *tile = fz_malloc_struct(ctx, tile_record);
FZ_INIT_STORABLE(tile, 1, fz_drop_tile_record_imp);
tile->dest = fz_keep_pixmap(ctx, dest);
tile->shape = fz_keep_pixmap(ctx, shape);
+ tile->group_alpha = fz_keep_pixmap(ctx, group_alpha);
return tile;
}
@@ -2342,7 +2497,7 @@ fz_tile_size(fz_context *ctx, tile_record *tile)
{
if (!tile)
return 0;
- return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape);
+ return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape) + fz_pixmap_size(ctx, tile->group_alpha);
}
static int
@@ -2351,7 +2506,7 @@ fz_draw_begin_tile(fz_context *ctx, fz_device *devp, const fz_rect *area, const
fz_draw_device *dev = (fz_draw_device*)devp;
fz_matrix ctm = concat(in_ctm, &dev->transform);
fz_pixmap *dest = NULL;
- fz_pixmap *shape;
+ fz_pixmap *shape, *group_alpha;
fz_irect bbox;
fz_draw_state *state = &dev->stack[dev->top];
fz_colorspace *model = state->dest->colorspace;
@@ -2394,6 +2549,7 @@ fz_draw_begin_tile(fz_context *ctx, fz_device *devp, const fz_rect *area, const
{
state[1].dest = fz_keep_pixmap(ctx, tile->dest);
state[1].shape = fz_keep_pixmap(ctx, tile->shape);
+ state[1].group_alpha = fz_keep_pixmap(ctx, tile->group_alpha);
state[1].blendmode |= FZ_BLEND_ISOLATED;
state[1].xstep = xstep;
state[1].ystep = ystep;
@@ -2422,6 +2578,12 @@ fz_draw_begin_tile(fz_context *ctx, fz_device *devp, const fz_rect *area, const
state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
fz_clear_pixmap(ctx, shape);
}
+ group_alpha = state[0].group_alpha;
+ if (group_alpha)
+ {
+ state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, &bbox, NULL, 1);
+ fz_clear_pixmap(ctx, group_alpha);
+ }
state[1].blendmode |= FZ_BLEND_ISOLATED;
state[1].xstep = xstep;
state[1].ystep = ystep;
@@ -2448,7 +2610,7 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
{
fz_draw_device *dev = (fz_draw_device*)devp;
float xstep, ystep;
- fz_matrix ttm, ctm, shapectm;
+ fz_matrix ttm, ctm, shapectm, gactm;
fz_irect area, scissor, tile_bbox;
fz_rect scissor_tmp, tile_tmp;
int x0, y0, x1, y1, x, y, extra_x, extra_y;
@@ -2505,15 +2667,25 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
shapectm.e = state[1].shape->x;
shapectm.f = state[1].shape->y;
}
+ if (state[1].group_alpha)
+ {
+ gactm = ctm;
+ gactm.e = state[1].group_alpha->x;
+ gactm.f = state[1].group_alpha->y;
+ }
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
fz_dump_blend(ctx, "Tiling ", state[1].dest);
if (state[1].shape)
- fz_dump_blend(ctx, "/", state[1].shape);
+ fz_dump_blend(ctx, "/S=", state[1].shape);
+ if (state[1].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
fz_dump_blend(ctx, " onto ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
#endif
for (y = y0; y < y1; y++)
@@ -2538,6 +2710,14 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
state[1].shape->y = ttm.f;
fz_paint_pixmap_with_bbox(state[0].shape, state[1].shape, 255, state[0].scissor);
}
+ if (state[1].group_alpha)
+ {
+ ttm = gactm;
+ fz_pre_translate(&ttm, x * xstep, y * ystep);
+ state[1].group_alpha->x = ttm.e;
+ state[1].group_alpha->y = ttm.f;
+ fz_paint_pixmap_with_bbox(state[0].group_alpha, state[1].group_alpha, 255, state[0].scissor);
+ }
}
}
@@ -2548,6 +2728,11 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
state[1].shape->x = shapectm.e;
state[1].shape->y = shapectm.f;
}
+ if (state[1].group_alpha)
+ {
+ state[1].group_alpha->x = gactm.e;
+ state[1].group_alpha->y = gactm.f;
+ }
/* Now we try to cache the tiles. Any failure here will just result in us not caching. */
if (state[1].encache && state[1].id != 0)
@@ -2560,7 +2745,7 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
{
tile_record *existing_tile;
- tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape);
+ tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape, state[1].group_alpha);
key = fz_malloc_struct(ctx, tile_key);
key->refs = 1;
@@ -2598,10 +2783,14 @@ fz_draw_end_tile(fz_context *ctx, fz_device *devp)
fz_drop_pixmap(ctx, state[1].dest);
if (state[0].shape != state[1].shape)
fz_drop_pixmap(ctx, state[1].shape);
+ if (state[0].group_alpha != state[1].group_alpha)
+ fz_drop_pixmap(ctx, state[1].group_alpha);
#ifdef DUMP_GROUP_BLENDS
fz_dump_blend(ctx, " to get ", state[0].dest);
if (state[0].shape)
- fz_dump_blend(ctx, "/", state[0].shape);
+ fz_dump_blend(ctx, "/S=", state[0].shape);
+ if (state[0].group_alpha)
+ fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
printf("\n");
#endif
@@ -2643,6 +2832,8 @@ fz_draw_close_device(fz_context *ctx, fz_device *devp)
fz_drop_pixmap(ctx, state[1].dest);
if (state[1].shape != state[0].shape)
fz_drop_pixmap(ctx, state[1].shape);
+ if (state[1].group_alpha != state[0].group_alpha)
+ fz_drop_pixmap(ctx, state[1].group_alpha);
}
if (dev->resolve_spots && dev->top)
@@ -2652,6 +2843,7 @@ fz_draw_close_device(fz_context *ctx, fz_device *devp)
fz_drop_pixmap(ctx, state[1].dest);
assert(state[1].mask == NULL);
assert(state[1].shape == NULL);
+ assert(state[1].group_alpha == NULL);
}
}
@@ -2676,6 +2868,8 @@ fz_draw_drop_device(fz_context *ctx, fz_device *devp)
fz_drop_pixmap(ctx, state[1].dest);
if (state[1].shape != state[0].shape)
fz_drop_pixmap(ctx, state[1].shape);
+ if (state[1].group_alpha != state[0].group_alpha)
+ fz_drop_pixmap(ctx, state[1].group_alpha);
}
/* We never free the dest/mask/shape at level 0, as:
@@ -2734,6 +2928,7 @@ new_draw_device(fz_context *ctx, const fz_matrix *transform, fz_pixmap *dest, co
dev->stack_cap = STACK_SIZE;
dev->stack[0].dest = dest;
dev->stack[0].shape = NULL;
+ dev->stack[0].group_alpha = NULL;
dev->stack[0].mask = NULL;
dev->stack[0].blendmode = 0;
dev->stack[0].scissor.x0 = dest->x;