From 6dc533a774c732b0292e5b566766f3a000cd45fb Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 14 Jul 2017 17:22:19 +0200 Subject: Complement subtractive colors before applying blend modes. (Incorporates fixes from Tor, Michael and Robin). --- source/fitz/draw-blend.c | 390 ++++++++++++++++++++++++++++------------------- 1 file changed, 235 insertions(+), 155 deletions(-) (limited to 'source/fitz/draw-blend.c') diff --git a/source/fitz/draw-blend.c b/source/fitz/draw-blend.c index f955e5f9..d819107a 100644 --- a/source/fitz/draw-blend.c +++ b/source/fitz/draw-blend.c @@ -235,85 +235,75 @@ fz_hue_rgb(unsigned char *rr, unsigned char *rg, unsigned char *rb, int br, int fz_saturation_rgb(rr, rg, rb, tr, tg, tb, br, bg, bb); } -void -fz_blend_pixel(unsigned char dp[3], unsigned char bp[3], unsigned char sp[3], int blendmode) -{ - int k; - /* non-separable blend modes */ - switch (blendmode) - { - case FZ_BLEND_HUE: fz_hue_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; - case FZ_BLEND_SATURATION: fz_saturation_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; - case FZ_BLEND_COLOR: fz_color_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; - case FZ_BLEND_LUMINOSITY: fz_luminosity_rgb(&dp[0], &dp[1], &dp[2], bp[0], bp[1], bp[2], sp[0], sp[1], sp[2]); return; - } - /* separable blend modes */ - for (k = 0; k < 3; k++) - { - switch (blendmode) - { - default: - case FZ_BLEND_NORMAL: dp[k] = sp[k]; break; - case FZ_BLEND_MULTIPLY: dp[k] = fz_mul255(bp[k], sp[k]); break; - case FZ_BLEND_SCREEN: dp[k] = fz_screen_byte(bp[k], sp[k]); break; - case FZ_BLEND_OVERLAY: dp[k] = fz_overlay_byte(bp[k], sp[k]); break; - case FZ_BLEND_DARKEN: dp[k] = fz_darken_byte(bp[k], sp[k]); break; - case FZ_BLEND_LIGHTEN: dp[k] = fz_lighten_byte(bp[k], sp[k]); break; - case FZ_BLEND_COLOR_DODGE: dp[k] = fz_color_dodge_byte(bp[k], sp[k]); break; - case FZ_BLEND_COLOR_BURN: dp[k] = fz_color_burn_byte(bp[k], sp[k]); break; - case FZ_BLEND_HARD_LIGHT: dp[k] = fz_hard_light_byte(bp[k], sp[k]); break; - case FZ_BLEND_SOFT_LIGHT: dp[k] = fz_soft_light_byte(bp[k], sp[k]); break; - case FZ_BLEND_DIFFERENCE: dp[k] = fz_difference_byte(bp[k], sp[k]); break; - case FZ_BLEND_EXCLUSION: dp[k] = fz_exclusion_byte(bp[k], sp[k]); break; - } - } -} - /* Blending loops */ static inline void -fz_blend_separable(byte * restrict bp, int bal, const byte * restrict sp, int sal, int n1, int w, int blendmode) +fz_blend_separable(byte * restrict bp, int bal, const byte * restrict sp, int sal, int n1, int w, int blendmode, int complement) { int k; do { int sa = (sal ? sp[n1] : 255); - int ba = (bal ? bp[n1] : 255); - int saba = fz_mul255(sa, ba); - - /* ugh, division to get non-premul components */ - int invsa = sa ? 255 * 256 / sa : 0; - int invba = ba ? 255 * 256 / ba : 0; - for (k = 0; k < n1; k++) + if (sa != 0) { - int sc = (sp[k] * invsa) >> 8; - int bc = (bp[k] * invba) >> 8; - int rc; - - switch (blendmode) + int ba = (bal ? bp[n1] : 255); + if (ba == 0) { - default: - case FZ_BLEND_NORMAL: rc = sc; break; - case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break; - case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break; - case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break; - case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break; - case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break; - case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break; - case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break; - case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break; - case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break; - case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break; - case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break; + memcpy(bp, sp, n1 + (sal && bal)); + if (bal && !sal) + bp[n1+1] = 255; } + else + { + int saba = fz_mul255(sa, ba); - bp[k] = fz_mul255(255 - sa, bp[k]) + fz_mul255(255 - ba, sp[k]) + fz_mul255(saba, rc); - } + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; - if (bal) - bp[k] = ba + sa - saba; + for (k = 0; k < n1; k++) + { + int sc = (sp[k] * invsa) >> 8; + int bc = (bp[k] * invba) >> 8; + int rc; + + if (complement) + { + sc = 255 - sc; + bc = 255 - bc; + } + + switch (blendmode) + { + default: + case FZ_BLEND_NORMAL: rc = sc; break; + case FZ_BLEND_MULTIPLY: rc = fz_mul255(bc, sc); break; + case FZ_BLEND_SCREEN: rc = fz_screen_byte(bc, sc); break; + case FZ_BLEND_OVERLAY: rc = fz_overlay_byte(bc, sc); break; + case FZ_BLEND_DARKEN: rc = fz_darken_byte(bc, sc); break; + case FZ_BLEND_LIGHTEN: rc = fz_lighten_byte(bc, sc); break; + case FZ_BLEND_COLOR_DODGE: rc = fz_color_dodge_byte(bc, sc); break; + case FZ_BLEND_COLOR_BURN: rc = fz_color_burn_byte(bc, sc); break; + case FZ_BLEND_HARD_LIGHT: rc = fz_hard_light_byte(bc, sc); break; + case FZ_BLEND_SOFT_LIGHT: rc = fz_soft_light_byte(bc, sc); break; + case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break; + case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break; + } + + if (complement) + { + rc = 255 - rc; + } + + bp[k] = fz_mul255(255 - sa, bp[k]) + fz_mul255(255 - ba, sp[k]) + fz_mul255(saba, rc); + } + + if (bal) + bp[k] = ba + sa - saba; + } + } sp += n1 + sal; bp += n1 + bal; } @@ -326,31 +316,43 @@ fz_blend_nonseparable_gray(byte * restrict bp, int bal, const byte * restrict sp do { int sa = (sal ? sp[n] : 255); - int ba = (bal ? bp[n] : 255); - int saba = fz_mul255(sa, ba); - /* ugh, division to get non-premul components */ - int invsa = sa ? 255 * 256 / sa : 0; - int invba = ba ? 255 * 256 / ba : 0; + if (sa != 0) + { + int ba = (bal ? bp[n] : 255); + if (ba == 0) + { + memcpy(bp, sp, n + (sal && bal)); + if (bal && !sal) + bp [n + 1] = 255; + } + else + { + int saba = fz_mul255(sa, ba); - int sg = (sp[0] * invsa) >> 8; - int bg = (bp[0] * invba) >> 8; + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; - switch (blendmode) - { - default: - case FZ_BLEND_HUE: - case FZ_BLEND_SATURATION: - case FZ_BLEND_COLOR: - bp[0] = fz_mul255(bp[n], bg); - break; - case FZ_BLEND_LUMINOSITY: - bp[0] = fz_mul255(bp[n], sg); - break; - } - if (bal) - bp[n] = ba + sa - saba; + int sg = (sp[0] * invsa) >> 8; + int bg = (bp[0] * invba) >> 8; + switch (blendmode) + { + default: + case FZ_BLEND_HUE: + case FZ_BLEND_SATURATION: + case FZ_BLEND_COLOR: + bp[0] = fz_mul255(bp[n], bg); + break; + case FZ_BLEND_LUMINOSITY: + bp[0] = fz_mul255(bp[n], sg); + break; + } + if (bal) + bp[n] = ba + sa - saba; + } + } sp += n + sal; bp += n + bal; } while (--w); @@ -364,61 +366,91 @@ fz_blend_nonseparable(byte * restrict bp, int bal, const byte * restrict sp, int unsigned char rr, rg, rb; int sa = (sal ? sp[n] : 255); - int ba = (bal ? bp[n] : 255); - int saba = fz_mul255(sa, ba); - /* ugh, division to get non-premul components */ - int invsa = sa ? 255 * 256 / sa : 0; - int invba = ba ? 255 * 256 / ba : 0; + if (sa != 0) + { + int ba = (bal ? bp[n] : 255); + if (ba == 0) + { + memcpy(bp, sp, n + (sal && bal)); + if (bal && !sal) + bp [n + 1] = 255; + } + else + { + int saba = fz_mul255(sa, ba); - int sr = (sp[0] * invsa) >> 8; - int sg = (sp[1] * invsa) >> 8; - int sb = (sp[2] * invsa) >> 8; + /* ugh, division to get non-premul components */ + int invsa = sa ? 255 * 256 / sa : 0; + int invba = ba ? 255 * 256 / ba : 0; - int br = (bp[0] * invba) >> 8; - int bg = (bp[1] * invba) >> 8; - int bb = (bp[2] * invba) >> 8; + int sr = (sp[0] * invsa) >> 8; + int sg = (sp[1] * invsa) >> 8; + int sb = (sp[2] * invsa) >> 8; - switch (blendmode) - { - default: - case FZ_BLEND_HUE: - fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); - break; - case FZ_BLEND_SATURATION: - fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); - break; - case FZ_BLEND_COLOR: - fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); - break; - case FZ_BLEND_LUMINOSITY: - fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); - break; - } + int br = (bp[0] * invba) >> 8; + int bg = (bp[1] * invba) >> 8; + int bb = (bp[2] * invba) >> 8; - bp[0] = fz_mul255(255 - sa, bp[0]) + fz_mul255(255 - ba, sp[0]) + fz_mul255(saba, rr); - bp[1] = fz_mul255(255 - sa, bp[1]) + fz_mul255(255 - ba, sp[1]) + fz_mul255(saba, rg); - bp[2] = fz_mul255(255 - sa, bp[2]) + fz_mul255(255 - ba, sp[2]) + fz_mul255(saba, rb); - if (bal) - bp[n] = ba + sa - saba; + if (n == 4) + { + sr = 255 - sr; + sg = 255 - sg; + sb = 255 - sb; + br = 255 - br; + bg = 255 - bg; + bb = 255 - bb; + } - /* Black */ - if (n == 4) - { - int sk = (sp[3] * invsa) >> 8; - int bk = (bp[3] * invba) >> 8; + switch (blendmode) + { + default: + case FZ_BLEND_HUE: + fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_SATURATION: + fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_COLOR: + fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLEND_LUMINOSITY: + fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + } - switch (blendmode) - { - default: - case FZ_BLEND_HUE: - case FZ_BLEND_SATURATION: - case FZ_BLEND_COLOR: - bp[3] = fz_mul255(bp[n], bk); - break; - case FZ_BLEND_LUMINOSITY: - bp[3] = fz_mul255(bp[n], sk); - break; + if (n == 4) + { + rr = 255 - rr; + rg = 255 - rg; + rb = 255 - rb; + } + + bp[0] = fz_mul255(255 - sa, bp[0]) + fz_mul255(255 - ba, sp[0]) + fz_mul255(saba, rr); + bp[1] = fz_mul255(255 - sa, bp[1]) + fz_mul255(255 - ba, sp[1]) + fz_mul255(saba, rg); + bp[2] = fz_mul255(255 - sa, bp[2]) + fz_mul255(255 - ba, sp[2]) + fz_mul255(saba, rb); + if (bal) + bp[n] = ba + sa - saba; + + /* Black */ + if (n == 4) + { + int sk = (sp[3] * invsa) >> 8; + int bk = (bp[3] * invba) >> 8; + + switch (blendmode) + { + default: + case FZ_BLEND_HUE: + case FZ_BLEND_SATURATION: + case FZ_BLEND_COLOR: + bp[3] = fz_mul255(bp[n], bk); + break; + case FZ_BLEND_LUMINOSITY: + bp[3] = fz_mul255(bp[n], sk); + break; + } + } } } sp += n + sal; @@ -428,7 +460,7 @@ fz_blend_nonseparable(byte * restrict bp, int bal, const byte * restrict sp, int } static inline void -fz_blend_separable_nonisolated(byte * restrict bp, int bal, const byte * restrict sp, int sal, int n1, int w, int blendmode, const byte * restrict hp, int alpha) +fz_blend_separable_nonisolated(byte * restrict bp, int bal, const byte * restrict sp, int sal, int n1, int w, int blendmode, int complement, const byte * restrict hp, int alpha) { int k; @@ -476,9 +508,7 @@ fz_blend_separable_nonisolated(byte * restrict bp, int bal, const byte * restric /* Just copy pixels (allowing for change in * premultiplied alphas) */ for (k = 0; k < n1; k++) - { bp[k] = fz_mul255((sp[k] * invsa) >> 8, haa); - } if (bal) bp[n1] = haa; break; @@ -522,6 +552,12 @@ fz_blend_separable_nonisolated(byte * restrict bp, int bal, const byte * restric int bc = (bp[k] * invba + 128) >> 8; int rc; + if (complement) + { + sc = 255 - sc; + bc = 255 - bc; + } + /* Uncomposite (see above) */ sc = (((sc-bc) * invha + 128)>>8) + bc; if (sc < 0) sc = 0; @@ -543,10 +579,17 @@ fz_blend_separable_nonisolated(byte * restrict bp, int bal, const byte * restric case FZ_BLEND_DIFFERENCE: rc = fz_difference_byte(bc, sc); break; case FZ_BLEND_EXCLUSION: rc = fz_exclusion_byte(bc, sc); break; } + /* Composition formula, as given in pdf_reference17.pdf: * rc = ( 1 - (ha/ra)) * bc + (ha/ra) * ((1-ba)*sc + ba * rc) */ rc = bc + fz_mul255(sa, fz_mul255(255 - ba, sc) + fz_mul255(ba, rc) - bc); + + if (complement) + { + rc = 255 - rc; + } + if (rc < 0) rc = 0; if (rc > 255) rc = 255; bp[k] = fz_mul255(rc, ra); @@ -634,7 +677,7 @@ fz_blend_nonseparable_nonisolated(byte * restrict bp, int bal, const byte * rest if (ba == 0) { - memcpy(bp, sp, n + (bal && sal)); + memcpy(bp, sp, n + (sal && bal)); if (bal && !sal) bp[n] = 255; } @@ -674,6 +717,16 @@ fz_blend_nonseparable_nonisolated(byte * restrict bp, int bal, const byte * rest int bg = (bp[1] * invba) >> 8; int bb = (bp[2] * invba) >> 8; + if (n == 4) + { + sr = 255 - sr; + sg = 255 - sg; + sb = 255 - sb; + br = 255 - br; + bg = 255 - bg; + bb = 255 - bb; + } + /* Uncomposite */ sr = (((sr - br)*invha) >> 8) + br; sg = (((sg - bg)*invha) >> 8) + bg; @@ -699,6 +752,14 @@ fz_blend_nonseparable_nonisolated(byte * restrict bp, int bal, const byte * rest rr = fz_mul255(255 - haa, bp[0]) + fz_mul255(fz_mul255(255 - ba, sr), haa) + fz_mul255(baha, rr); rg = fz_mul255(255 - haa, bp[1]) + fz_mul255(fz_mul255(255 - ba, sg), haa) + fz_mul255(baha, rg); rb = fz_mul255(255 - haa, bp[2]) + fz_mul255(fz_mul255(255 - ba, sb), haa) + fz_mul255(baha, rb); + + if (n == 4) + { + rr = 255 - rr; + rg = 255 - rg; + rb = 255 - rb; + } + bp[0] = fz_mul255(ra, rr); bp[1] = fz_mul255(ra, rg); bp[2] = fz_mul255(ra, rb); @@ -733,14 +794,15 @@ fz_blend_nonseparable_nonisolated(byte * restrict bp, int bal, const byte * rest } void -fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, int blendmode, int isolated, const fz_pixmap * restrict shape) +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) { - const unsigned char *sp; + unsigned char *sp; unsigned char *dp; fz_irect bbox; fz_irect bbox2; int x, y, w, h, n; int da, sa; + int complement; /* TODO: fix this hack! */ if (isolated && alpha < 255) @@ -774,6 +836,7 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i if (w == 0 || h == 0) return; + complement = fz_colorspace_is_subtractive(ctx, src->colorspace); n = src->n; sp = src->samples + (unsigned int)((y - src->y) * src->stride + (x - src->x) * src->n); sa = src->alpha; @@ -783,6 +846,9 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i n -= sa; assert(n == dst->n - da); + if (blendmode >= FZ_BLEND_HUE && (complement ? n != 4 : n != 1 && n != 3)) + fz_throw(ctx, FZ_ERROR_GENERIC, "can only use non-separable blend modes on gray or RGB colors"); + if (!isolated) { const unsigned char *hp = shape->samples + (unsigned int)((y - shape->y) * shape->stride + (x - shape->x)); @@ -790,6 +856,7 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i while (h--) { if (blendmode >= FZ_BLEND_HUE) + { if (da) if (sa) if (n == 1) @@ -812,17 +879,23 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i fz_blend_nonseparable_nonisolated_gray(dp, 0, sp, 0, n, w, blendmode, hp, alpha); else fz_blend_nonseparable_nonisolated(dp, 0, sp, 0, n, w, blendmode, hp, alpha); + } else - if (da) - if (sa) - fz_blend_separable_nonisolated(dp, 1, sp, 1, n, w, blendmode, hp, alpha); - else - fz_blend_separable_nonisolated(dp, 1, sp, 0, n, w, blendmode, hp, alpha); + { + if (complement) + fz_blend_separable_nonisolated(dp, da, sp, sa, n, w, blendmode, 1, hp, alpha); else - if (sa) - fz_blend_separable_nonisolated(dp, 0, sp, 1, n, w, blendmode, hp, alpha); + if (da) + if (sa) + fz_blend_separable_nonisolated(dp, 1, sp, 1, n, w, blendmode, 0, hp, alpha); + else + fz_blend_separable_nonisolated(dp, 1, sp, 0, n, w, blendmode, 0, hp, alpha); else - fz_blend_separable_nonisolated(dp, 0, sp, 0, n, w, blendmode, hp, alpha); + if (sa) + fz_blend_separable_nonisolated(dp, 0, sp, 1, n, w, blendmode, 0, hp, alpha); + else + fz_blend_separable_nonisolated(dp, 0, sp, 0, n, w, blendmode, 0, hp, alpha); + } sp += src->stride; dp += dst->stride; hp += shape->stride; @@ -833,6 +906,7 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i while (h--) { if (blendmode >= FZ_BLEND_HUE) + { if (da) if (sa) if (n == 1) @@ -855,17 +929,23 @@ fz_blend_pixmap(fz_pixmap * restrict dst, fz_pixmap * restrict src, int alpha, i fz_blend_nonseparable_gray(dp, 0, sp, 0, n, w, blendmode); else fz_blend_nonseparable(dp, 0, sp, 0, n, w, blendmode); + } else - if (da) - if (sa) - fz_blend_separable(dp, 1, sp, 1, n, w, blendmode); - else - fz_blend_separable(dp, 1, sp, 0, n, w, blendmode); + { + if (complement) + fz_blend_separable(dp, da, sp, sa, n, w, blendmode, 1); else - if (sa) - fz_blend_separable(dp, 0, sp, 1, n, w, blendmode); + if (da) + if (sa) + fz_blend_separable(dp, 1, sp, 1, n, w, blendmode, 0); + else + fz_blend_separable(dp, 1, sp, 0, n, w, blendmode, 0); else - fz_blend_separable(dp, 0, sp, 0, n, w, blendmode); + if (sa) + fz_blend_separable(dp, 0, sp, 1, n, w, blendmode, 0); + else + fz_blend_separable(dp, 0, sp, 0, n, w, blendmode, 0); + } sp += src->stride; dp += dst->stride; } -- cgit v1.2.3