summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-09-06 14:57:48 +0100
committerRobin Watts <robin.watts@artifex.com>2017-10-24 15:16:36 +0100
commit313c72b9b3f3c888a51c9eb02187228b9e2aad11 (patch)
tree44230ad3d852cec8608fa766dea5bfe3714a30a5
parentc413f4db6a54846ae85393a87fa1b53b93a966ee (diff)
downloadmupdf-313c72b9b3f3c888a51c9eb02187228b9e2aad11.tar.xz
Fix knockout operation.
The "blend back" at the end of the inner knockout groups was attempting to reuse the existing blending code. This was going wrong for all sorts of reasons (not least the uncomposition phase) for knockout groups containing alpha, such as found on page 7 of Altona_Technical_v20_x4.pdf. Use a dedicated routine. This is much simpler as it doesn't have to cope with blend modes etc.
-rw-r--r--source/fitz/draw-blend.c100
-rw-r--r--source/fitz/draw-device.c70
-rw-r--r--source/fitz/draw-imp.h1
3 files changed, 149 insertions, 22 deletions
diff --git a/source/fitz/draw-blend.c b/source/fitz/draw-blend.c
index ac9e0492..6c2c58c7 100644
--- a/source/fitz/draw-blend.c
+++ b/source/fitz/draw-blend.c
@@ -1250,3 +1250,103 @@ fz_blend_pixmap(fz_context *ctx, fz_pixmap * restrict dst, fz_pixmap * restrict
verify_premultiply(ctx, dst);
#endif
}
+
+static inline void
+fz_blend_knockout(byte * restrict bp, int bal, const byte * restrict sp, int sal, int n1, int w, const byte * restrict hp)
+{
+ int k;
+ do
+ {
+ int ha = *hp++;
+
+ if (ha != 0)
+ {
+ int sa = (sal ? sp[n1] : 255);
+ int ba = (bal ? bp[n1] : 255);
+ if (ba == 0 && ha == 0xFF)
+ {
+ memcpy(bp, sp, n1);
+ if (bal)
+ bp[n1] = sa;
+ }
+ else
+ {
+ int hasa = fz_mul255(ha, sa);
+ /* ugh, division to get non-premul components */
+ int invsa = sa ? 255 * 256 / sa : 0;
+ int invba = ba ? 255 * 256 / ba : 0;
+ int ra = hasa + fz_mul255(255-ha, ba);
+
+ /* Process colorants + spots */
+ for (k = 0; k < n1; k++)
+ {
+ int sc = (sp[k] * invsa) >> 8;
+ int bc = (bp[k] * invba) >> 8;
+ int rc = fz_mul255(255 - ha, bc) + fz_mul255(ha, sc);
+
+ bp[k] = fz_mul255(ra, rc);
+ }
+
+ if (bal)
+ bp[k] = ra;
+ }
+ }
+ sp += n1 + sal;
+ bp += n1 + bal;
+ }
+ while (--w);
+}
+
+void
+fz_blend_pixmap_knockout(fz_context *ctx, fz_pixmap * restrict dst, fz_pixmap * restrict src, const fz_pixmap * restrict shape)
+{
+ unsigned char *sp;
+ unsigned char *dp;
+ fz_irect bbox;
+ fz_irect bbox2;
+ int x, y, w, h, n;
+ int da, sa;
+ const unsigned char *hp;
+
+ fz_pixmap_bbox_no_ctx(dst, &bbox);
+ fz_pixmap_bbox_no_ctx(src, &bbox2);
+ fz_intersect_irect(&bbox, &bbox2);
+
+ x = bbox.x0;
+ y = bbox.y0;
+ w = bbox.x1 - bbox.x0;
+ h = bbox.y1 - bbox.y0;
+
+ if (w == 0 || h == 0)
+ return;
+
+ n = src->n;
+ sp = src->samples + (unsigned int)((y - src->y) * src->stride + (x - src->x) * src->n);
+ sa = src->alpha;
+ dp = dst->samples + (unsigned int)((y - dst->y) * dst->stride + (x - dst->x) * dst->n);
+ da = dst->alpha;
+ hp = shape->samples + (unsigned int)((y - shape->y) * shape->stride + (x - shape->x));
+
+#ifdef PARANOID_PREMULTIPLY
+ if (sa)
+ verify_premultiply(ctx, src);
+ if (da)
+ verify_premultiply(ctx, dst);
+#endif
+
+ n -= sa;
+ assert(n == dst->n - da);
+
+ while (h--)
+ {
+ fz_blend_knockout(dp, da, sp, sa, n, w, hp);
+ sp += src->stride;
+ dp += dst->stride;
+ hp += shape->stride;
+ }
+
+#ifdef PARANOID_PREMULTIPLY
+ if (da)
+ verify_premultiply(ctx, dst);
+#endif
+}
diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c
index 64de42ae..260ca59e 100644
--- a/source/fitz/draw-device.c
+++ b/source/fitz/draw-device.c
@@ -195,8 +195,9 @@ static void emergency_pop_stack(fz_context *ctx, fz_draw_device *dev, fz_draw_st
static fz_draw_state *
fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
{
- fz_irect bbox;
+ fz_irect bbox, ga_bbox;
fz_pixmap *dest, *shape;
+ fz_pixmap *ga = NULL;
fz_draw_state *state = &dev->stack[dev->top];
int isolated = state->blendmode & FZ_BLEND_ISOLATED;
@@ -209,26 +210,47 @@ fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
fz_pixmap_bbox(ctx, state->dest, &bbox);
fz_intersect_irect(&bbox, &state->scissor);
dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, &bbox, state->dest->seps, state->dest->alpha);
+ if (state[0].group_alpha)
+ {
+ fz_pixmap_bbox(ctx, state->group_alpha, &ga_bbox);
+ fz_intersect_irect(&ga_bbox, &state->scissor);
+ ga = fz_new_pixmap_with_bbox(ctx, state->group_alpha->colorspace, &ga_bbox, state->group_alpha->seps, state->group_alpha->alpha);
+ }
if (isolated)
{
fz_clear_pixmap(ctx, dest);
+ if (ga)
+ fz_clear_pixmap(ctx, ga);
}
else
{
/* Find the last but one destination to copy */
int i = dev->top-1; /* i = the one on entry (i.e. the last one) */
- fz_pixmap *prev = state->dest;
+ fz_draw_state *prev = state;
while (i > 0)
{
- prev = dev->stack[--i].dest;
- if (prev != state->dest)
+ prev = &dev->stack[--i];
+ if (prev->dest != state->dest)
break;
}
- if (prev)
- fz_copy_pixmap_rect(ctx, dest, prev, &bbox, dev->default_cs);
+ if (prev->dest)
+ {
+ fz_copy_pixmap_rect(ctx, dest, prev->dest, &bbox, dev->default_cs);
+ if (ga)
+ {
+ if (prev->group_alpha)
+ fz_copy_pixmap_rect(ctx, ga, prev->group_alpha, &ga_bbox, dev->default_cs);
+ else
+ fz_clear_pixmap(ctx, ga);
+ }
+ }
else
+ {
fz_clear_pixmap(ctx, dest);
+ if (ga)
+ fz_clear_pixmap(ctx, ga);
+ }
}
/* Knockout groups (and only knockout groups) rely on shape */
@@ -239,8 +261,11 @@ fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
fz_dump_blend(ctx, "Knockout begin: background is ", dest);
if (shape)
fz_dump_blend(ctx, "/S=", shape);
+ if (ga)
+ fz_dump_blend(ctx, "/GA=", ga);
printf("\n");
#endif
+ state[1].group_alpha = ga;
state[1].scissor = bbox;
state[1].dest = dest;
state[1].shape = shape;
@@ -252,8 +277,6 @@ fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
{
fz_draw_state *state;
- int blendmode;
- int isolated;
if (dev->top == 0)
{
@@ -265,8 +288,8 @@ static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0)
return;
- blendmode = state->blendmode & FZ_BLEND_MODEMASK;
- isolated = state->blendmode & FZ_BLEND_ISOLATED;
+ assert((state[1].blendmode & FZ_BLEND_ISOLATED) == 0);
+ assert((state[1].blendmode & FZ_BLEND_MODEMASK) == 0);
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top, "");
@@ -280,32 +303,32 @@ static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
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)
+ if ((state->blendmode & FZ_BLEND_MODEMASK) != 0)
+ printf(" (blend %d)", state->blendmode & FZ_BLEND_MODEMASK);
+ if ((state->blendmode & FZ_BLEND_ISOLATED) != 0)
printf(" (isolated)");
printf(" (knockout)");
#endif
assert(state[1].shape);
- fz_blend_pixmap(ctx, state[0].dest, state[1].dest, 255, blendmode, 0, state[1].shape);
+ fz_blend_pixmap_knockout(ctx, state[0].dest, state[1].dest, 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
* bacon. */
if (state[0].dest != state[1].dest)
fz_drop_pixmap(ctx, state[1].dest);
+ if (state[1].group_alpha && state[0].group_alpha != state[1].group_alpha)
+ {
+ if (state[0].group_alpha)
+ fz_blend_pixmap_knockout(ctx, state[0].group_alpha, state[1].group_alpha, state[1].shape);
+ fz_drop_pixmap(ctx, state[1].group_alpha);
+ }
if (state[0].shape != state[1].shape)
{
if (state[0].shape)
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)
@@ -2260,6 +2283,7 @@ fz_draw_begin_group(fz_context *ctx, fz_device *devp, const fz_rect *rect, fz_co
fz_clear_pixmap(ctx, state[1].group_alpha);
}
+ /* shape is inherited from the previous group */
state[1].alpha = alpha;
#ifdef DUMP_GROUP_BLENDS
dump_spaces(dev->top-1, "");
@@ -2349,12 +2373,14 @@ fz_draw_end_group(fz_context *ctx, fz_device *devp)
if (state[0].shape != state[1].shape)
{
+ /* The 'D' on page 7 of Altona_Technical_v20_x4.pdf goes wrong if this
+ * isn't alpha * 255, as the blend back fails to take account of alpha. */
if (state[0].shape)
{
if (state[1].shape)
- fz_paint_pixmap(state[0].shape, state[1].shape, 255);
+ fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255);
else
- fz_paint_pixmap_alpha(state[0].shape, state[1].dest, 255);
+ fz_paint_pixmap_alpha(state[0].shape, state[1].dest, alpha * 255);
}
fz_drop_pixmap(ctx, state[1].shape);
}
diff --git a/source/fitz/draw-imp.h b/source/fitz/draw-imp.h
index e855b40a..f6cc1367 100644
--- a/source/fitz/draw-imp.h
+++ b/source/fitz/draw-imp.h
@@ -464,6 +464,7 @@ void fz_paint_pixmap_with_bbox(fz_pixmap * restrict dst, const fz_pixmap * restr
void fz_paint_pixmap_with_overprint(fz_pixmap * restrict dst, const fz_pixmap * restrict src, const fz_overprint *op);
void fz_blend_pixmap(fz_context *ctx, fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, int blendmode, int isolated, const fz_pixmap * restrict shape);
+void fz_blend_pixmap_knockout(fz_context *ctx, fz_pixmap * restrict dst, fz_pixmap * restrict src, const fz_pixmap * restrict shape);
void fz_paint_glyph(const unsigned char * restrict colorbv, fz_pixmap * restrict dst, unsigned char * restrict dp, const fz_glyph * restrict glyph, int w, int h, int skip_x, int skip_y, const fz_overprint * restrict eop);