diff options
Diffstat (limited to 'draw')
-rw-r--r-- | draw/blendmodes.c | 389 |
1 files changed, 238 insertions, 151 deletions
diff --git a/draw/blendmodes.c b/draw/blendmodes.c index 9dde35d1..5a7530ec 100644 --- a/draw/blendmodes.c +++ b/draw/blendmodes.c @@ -3,243 +3,321 @@ typedef unsigned char byte; /* -PDF 1.4 blend modes, except Normal and Multiply which are Over and In respectively. -Only the actual blend routines are here, not the node rendering logic which lives in render.c. +PDF 1.4 blend modes. +Only the actual blend routines are here, not the rendering logic. These are slow. */ -/* These functions apply to a single component, 0-255 range typically */ +/* These functions apply to a single component, 0-255 range */ static inline int -fz_screen_byte(int bd, int s) +fz_screen_byte(int b, int s) { - return bd + s - fz_mul255(bd, s); + return b + s - fz_mul255(b, s); } static inline int -fz_hardlight_byte(int bd, int s) +fz_hardlight_byte(int b, int s) { int s2 = s << 1; if (s <= 127) - return fz_mul255(bd, s2); + return fz_mul255(b, s2); else - return fz_screen_byte(bd, s2 - 1); + return fz_screen_byte(b, s2 - 255); } static inline int -fz_overlay_byte(int bd, int s) +fz_overlay_byte(int b, int s) { - return fz_hardlight_byte(s, bd); // note swapped order + return fz_hardlight_byte(s, b); /* note swapped order */ } static inline int -fz_darken_byte(int bd, int s) +fz_darken_byte(int b, int s) { - return MIN(bd, s); + return MIN(b, s); } static inline int -fz_lighten_byte(int bd, int s) +fz_lighten_byte(int b, int s) { - return MAX(bd, s); + return MAX(b, s); } static inline int -fz_colordodge_byte(int bd, int s) +fz_colordodge_byte(int b, int s) { - if (s < 255) - return MIN(255, 255 * bd / (255 - s)); - else + s = 255 - s; + if (b == 0) + return 0; + else if (b >= s) return 255; + else + return (0x1fe * b + s) / (s << 1); } static inline int -fz_colorburn_byte(int bd, int s) +fz_colorburn_byte(int b, int s) { - if (s > 0) - return 255 - MIN(255, 255 * (255 - bd) / s); - else + b = 255 - b; + if (b == 0) + return 255; + else if (b >= s) return 0; + else + return 0xff - (0x1fe * b + s) / (s << 1); } static inline int -fz_softlight_byte(int bd, int s) +fz_softlight_byte(int b, int s) { /* review this */ if (s < 128) { - return bd - fz_mul255(fz_mul255((255 - (s<<1)), bd), 255 - bd); + return b - fz_mul255(fz_mul255((255 - (s<<1)), b), 255 - b); } else { int dbd; - if (bd < 64) - dbd = fz_mul255(fz_mul255((bd << 4) - 12, bd) + 4, bd); + if (b < 64) + dbd = fz_mul255(fz_mul255((b << 4) - 12, b) + 4, b); else - dbd = (int)sqrtf(255.0f * bd); - return bd + fz_mul255(((s<<1) - 255), (dbd - bd)); + dbd = (int)sqrtf(255.0f * b); + return b + fz_mul255(((s<<1) - 255), (dbd - b)); } } static inline int -fz_difference_byte(int bd, int s) +fz_difference_byte(int b, int s) { - return ABS(bd - s); + return ABS(b - s); } static inline int -fz_exclusion_byte(int bd, int s) +fz_exclusion_byte(int b, int s) { - return bd + s - (fz_mul255(bd, s)<<1); + return b + s - (fz_mul255(b, s)<<1); } /* Non-separable blend modes */ -static inline int -lum(int r, int g, int b) +static inline void +fz_luminosity_rgb(int *rd, int *gd, int *bd, int rb, int gb, int bb, int rs, int gs, int bs) { - /* 0.3, 0.59, 0.11 in 16.16 fixed point */ - return (19662 * r + 38666 * g + 7208 * b) >> 16; -} + int delta, scale; + int r, g, b, y; -static void -clipcolor(int r, int g, int b, int *dr, int *dg, int *db) -{ - int l = lum(r, g, b); - int n = MIN(MIN(r, g), b); - int x = MAX(MAX(r, g), b); - if (n < 0) { - *dr = l + 255 * (r - l) / (l - n); - *dg = l + 255 * (g - l) / (l - n); - *db = l + 255 * (b - l) / (l - n); - } - else { - *dr = l + 255 * (255 - l) / (x - l); - *dg = l + 255 * (255 - l) / (x - l); - *db = l + 255 * (255 - l) / (x - l); - } -} + /* 0.3, 0.59, 0.11 in fixed point */ + delta = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8; + r = rb + delta; + g = gb + delta; + b = bb + delta; -static void -setlum(int r, int g, int b, int l, int *dr, int *dg, int *db) -{ - int d = 255 - lum(r, g, b); - clipcolor(r + d, g + d, b + d, dr, dg, db); -} + if ((r | g | b) & 0x100) + { + y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8; + if (delta > 0) { + int max; + max = r > g ? r : g; + max = b > max ? b : max; + scale = ((255 - y) << 16) / (max - y); + } else { + int min; + min = r < g ? r : g; + min = b < min ? b : min; + scale = (y << 16) / (y - min); + } + r = y + (((r - y) * scale + 0x8000) >> 16); + g = y + (((g - y) * scale + 0x8000) >> 16); + b = y + (((b - y) * scale + 0x8000) >> 16); + } -static inline int -sat(int r, int g, int b) -{ - return MAX(MAX(r, g), b) - MIN(MIN(r, g), b); + *rd = r; + *gd = g; + *bd = b; } static void -setsat(int r, int g, int b, int s, int *dr, int *dg, int *db) +fz_saturation_rgb(int *rd, int *gd, int *bd, int rb, int gb, int bb, int rs, int gs, int bs) { - int *m[3]; /* min, med, max */ - int *t; - m[0] = &r; - m[1] = &g; - m[2] = &b; -#define SWAP(a, b) (t = a, a = b, b = t) - if (*m[0] > *m[1]) - SWAP(m[0], m[1]); - if (*m[0] > *m[2]) - SWAP(m[0], m[2]); - if (*m[1] > *m[2]) - SWAP(m[1], m[2]); - - if (*m[2] > *m[0]) { - *m[1] = (*m[1] - *m[0]) * s / (*m[2] - *m[0]); - *m[2] = s; - } - else { - *m[1] = 0; - *m[2] = 0; + int minb, maxb; + int mins, maxs; + int y; + int scale; + int r, g, b; + + minb = rb < gb ? rb : gb; + minb = minb < bb ? minb : bb; + maxb = rb > gb ? rb : gb; + maxb = maxb > bb ? maxb : bb; + if (minb == maxb) { + /* backdrop has zero saturation, avoid divide by 0 */ + *rd = gb; + *gd = gb; + *bd = gb; + return; } - *dr = r; - *dg = g; - *db = b; -} -static void -fz_hue_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) -{ - int tr, tg, tb; - setsat(sr, sg, sb, sat(*bdr, *bdg, *bdb), &tr, &tg, &tb); - setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); -} + mins = rs < gs ? rs : gs; + mins = mins < bs ? mins : bs; + maxs = rs > gs ? rs : gs; + maxs = maxs > bs ? maxs : bs; -static void -fz_saturation_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) -{ - int tr, tg, tb; - setsat(*bdr, *bdg, *bdb, sat(sr, sg, sb), &tr, &tg, &tb); - setlum(tr, tg, tb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); + scale = ((maxs - mins) << 16) / (maxb - minb); + y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8; + r = y + ((((rb - y) * scale) + 0x8000) >> 16); + g = y + ((((gb - y) * scale) + 0x8000) >> 16); + b = y + ((((bb - y) * scale) + 0x8000) >> 16); + + if ((r | g | b) & 0x100) { + int scalemin, scalemax; + int min, max; + + min = r < g ? r : g; + min = min < b ? min : b; + max = r > g ? r : g; + max = max > b ? max : b; + + if (min < 0) + scalemin = (y << 16) / (y - min); + else + scalemin = 0x10000; + + if (max > 255) + scalemax = ((255 - y) << 16) / (max - y); + else + scalemax = 0x10000; + + scale = scalemin < scalemax ? scalemin : scalemax; + r = y + (((r - y) * scale + 0x8000) >> 16); + g = y + (((g - y) * scale + 0x8000) >> 16); + b = y + (((b - y) * scale + 0x8000) >> 16); + } + + *rd = r; + *gd = g; + *bd = b; } static void -fz_color_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +fz_color_rgb(int *rr, int *rg, int *rb, int br, int bg, int bb, int sr, int sg, int sb) { - setlum(sr, sg, sb, lum(*bdr, *bdg, *bdb), bdr, bdg, bdb); + fz_luminosity_rgb(rr, rg, rb, sr, sg, sb, br, bg, bb); } static void -fz_luminosity_rgb(int *bdr, int *bdg, int *bdb, int sr, int sg, int sb) +fz_hue_rgb(int *rr, int *rg, int *rb, int br, int bg, int bb, int sr, int sg, int sb) { - setlum(*bdr, *bdg, *bdb, lum(sr, sg, sb), bdr, bdg, bdb); + int tr, tg, tb; + fz_luminosity_rgb(&tr, &tg, &tb, sr, sg, sb, br, bg, bb); + fz_saturation_rgb(rr, rg, rb, tr, tg, tb, br, bg, bb); } /* * */ -void -fz_blend_nxn(byte * restrict sp, int sw, int sn, - byte * restrict dp, int dw, - int w0, int h, fz_blendmode blendmode) +static void +fz_blendseparable(byte * restrict sp, byte * restrict bp, int n, int w, fz_blendmode blendmode) { int k; - - sw -= w0*sn; - dw -= w0*sn; - while (h--) + int n1 = n - 1; + while (w--) { - int w = w0; - while (w--) + int sa = sp[n1]; + int ba = bp[n1]; + int saba = fz_mul255(sa, ba); + + for (k = 0; k < n1; k++) { - int sa = sp[sn-1]; - int da = dp[sn-1]; - int ta = 255 - sa; - int tb = 255 - da; - int tc = fz_mul255(sa, da); - for (k = 0; k < sn; k++) + int sc = sp[k]; + int bc = bp[k]; + int rc; + + /* ugh, division to get non-premul components */ + if (sa) sc = sc * 255 / sa; + if (ba) bc = bc * 255 / ba; + + switch (blendmode) { - int r, bd, s; - if (da) - bd = dp[k] * 255 / da; - if (sa) - s = sp[k] * 255 / sa; - switch (blendmode) - { - default: - case FZ_BMULTIPLY: r = fz_mul255(bd, s); break; - case FZ_BSCREEN: r = fz_screen_byte(bd, s); break; - case FZ_BOVERLAY: r = fz_overlay_byte(bd, s); break; - case FZ_BDARKEN: r = fz_darken_byte(bd, s); break; - case FZ_BLIGHTEN: r = fz_lighten_byte(bd, s); break; - case FZ_BCOLORDODGE: r = fz_colordodge_byte(bd, s); break; - case FZ_BCOLORBURN: r = fz_colorburn_byte(bd, s); break; - case FZ_BHARDLIGHT: r = fz_hardlight_byte(bd, s); break; - case FZ_BSOFTLIGHT: r = fz_softlight_byte(bd, s); break; - case FZ_BDIFFERENCE: r = fz_difference_byte(bd, s); break; - case FZ_BEXCLUSION: r = fz_exclusion_byte(bd, s); break; - } - dp[k] = fz_mul255(ta, dp[k]) + fz_mul255(tb, sp[k]) + fz_mul255(tc, r); + default: + case FZ_BNORMAL: rc = sc; break; + case FZ_BMULTIPLY: rc = fz_mul255(bc, sc); break; + case FZ_BSCREEN: rc = fz_screen_byte(bc, sc); break; + case FZ_BOVERLAY: rc = fz_overlay_byte(bc, sc); break; + case FZ_BDARKEN: rc = fz_darken_byte(bc, sc); break; + case FZ_BLIGHTEN: rc = fz_lighten_byte(bc, sc); break; + case FZ_BCOLORDODGE: rc = fz_colordodge_byte(bc, sc); break; + case FZ_BCOLORBURN: rc = fz_colorburn_byte(bc, sc); break; + case FZ_BHARDLIGHT: rc = fz_hardlight_byte(bc, sc); break; + case FZ_BSOFTLIGHT: rc = fz_softlight_byte(bc, sc); break; + case FZ_BDIFFERENCE: rc = fz_difference_byte(bc, sc); break; + case FZ_BEXCLUSION: rc = fz_exclusion_byte(bc, sc); break; } - sp += sn; - dp += sn; + + bp[k] = fz_mul255(255 - sa, bp[k]) + fz_mul255(255 - ba, sp[k]) + fz_mul255(saba, rc); + } + + bp[k] = ba + sa - saba; + + sp += n; + bp += n; + } +} + +static void +fz_blendnonseparable(byte * restrict sp, byte * restrict bp, int w, fz_blendmode blendmode) +{ + while (w--) + { + int rr, rg, rb, saba; + + int sr = sp[0]; + int sg = sp[1]; + int sb = sp[2]; + int sa = sp[3]; + + int br = bp[0]; + int bg = bp[1]; + int bb = bp[2]; + int ba = bp[3]; + + saba = fz_mul255(sa, ba); + + /* ugh, bivision to get non-premul components */ + if (sa) { + sr = sr * 255 / sa; + sg = sg * 255 / sa; + sb = sb * 255 / sa; + } + if (ba) { + br = br * 255 / ba; + bg = bg * 255 / ba; + bb = bb * 255 / ba; } - sp += sw; - dp += dw; + + switch (blendmode) + { + default: + case FZ_BHUE: + fz_hue_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BSATURATION: + fz_saturation_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BCOLOR: + fz_color_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + case FZ_BLUMINOSITY: + fz_luminosity_rgb(&rr, &rg, &rb, br, bg, bb, sr, sg, sb); + break; + } + + 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); + bp[3] = ba + sa - saba; + + sp += 4; + bp += 4; } } @@ -248,7 +326,7 @@ fz_blendpixmaps(fz_pixmap *src, fz_pixmap *dst, fz_blendmode blendmode) { unsigned char *sp, *dp; fz_bbox sr, dr; - int x, y, w, h; + int x, y, w, h, n; assert(src->n == dst->n); @@ -268,8 +346,17 @@ fz_blendpixmaps(fz_pixmap *src, fz_pixmap *dst, fz_blendmode blendmode) w = dr.x1 - dr.x0; h = dr.y1 - dr.y0; - sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * src->n; - dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n; + n = src->n; + sp = src->samples + ((y - src->y) * src->w + (x - src->x)) * n; + dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * n; - fz_blend_nxn(sp, src->w * src->n, src->n, dp, dst->w * dst->n, w, h, blendmode); + while (h--) + { + if (n == 4 && blendmode >= FZ_BHUE) + fz_blendnonseparable(sp, dp, w, blendmode); + else + fz_blendseparable(sp, dp, n, w, blendmode); + sp += src->w * n; + dp += dst->w * n; + } } |