13ff48bf5SDavid du Colombier /*
23ff48bf5SDavid du Colombier Copyright (C) 2001 artofcode LLC.
33ff48bf5SDavid du Colombier
4*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
5*593dc095SDavid du Colombier implied.
63ff48bf5SDavid du Colombier
7*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
8*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
9*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
103ff48bf5SDavid du Colombier
11*593dc095SDavid du Colombier For more information about licensing, please refer to
12*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
13*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
14*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
15*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
163ff48bf5SDavid du Colombier
173ff48bf5SDavid du Colombier Author: Raph Levien <raph@artofcode.com>
183ff48bf5SDavid du Colombier */
19*593dc095SDavid du Colombier /* $Id: gxblend.c,v 1.6 2004/08/18 04:48:56 dan Exp $ */
203ff48bf5SDavid du Colombier /* PDF 1.4 blending functions */
213ff48bf5SDavid du Colombier
223ff48bf5SDavid du Colombier #include "memory_.h"
233ff48bf5SDavid du Colombier #include "gx.h"
243ff48bf5SDavid du Colombier #include "gstparam.h"
253ff48bf5SDavid du Colombier #include "gxblend.h"
263ff48bf5SDavid du Colombier
273ff48bf5SDavid du Colombier typedef int art_s32;
283ff48bf5SDavid du Colombier
293ff48bf5SDavid du Colombier static void
art_blend_luminosity_rgb_8(byte * dst,const byte * backdrop,const byte * src)303ff48bf5SDavid du Colombier art_blend_luminosity_rgb_8(byte *dst, const byte *backdrop,
313ff48bf5SDavid du Colombier const byte *src)
323ff48bf5SDavid du Colombier {
333ff48bf5SDavid du Colombier int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
343ff48bf5SDavid du Colombier int rs = src[0], gs = src[1], bs = src[2];
353ff48bf5SDavid du Colombier int delta_y;
363ff48bf5SDavid du Colombier int r, g, b;
373ff48bf5SDavid du Colombier
38*593dc095SDavid du Colombier /*
39*593dc095SDavid du Colombier * From section 7.4 of the PDF 1.5 specification, for RGB, the luminosity
40*593dc095SDavid du Colombier * is: Y = 0.30 R + 0.59 G + 0.11 B)
41*593dc095SDavid du Colombier */
423ff48bf5SDavid du Colombier delta_y = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8;
433ff48bf5SDavid du Colombier r = rb + delta_y;
443ff48bf5SDavid du Colombier g = gb + delta_y;
453ff48bf5SDavid du Colombier b = bb + delta_y;
463ff48bf5SDavid du Colombier if ((r | g | b) & 0x100) {
473ff48bf5SDavid du Colombier int y;
483ff48bf5SDavid du Colombier int scale;
493ff48bf5SDavid du Colombier
503ff48bf5SDavid du Colombier y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8;
513ff48bf5SDavid du Colombier if (delta_y > 0) {
523ff48bf5SDavid du Colombier int max;
533ff48bf5SDavid du Colombier
543ff48bf5SDavid du Colombier max = r > g ? r : g;
553ff48bf5SDavid du Colombier max = b > max ? b : max;
563ff48bf5SDavid du Colombier scale = ((255 - y) << 16) / (max - y);
573ff48bf5SDavid du Colombier } else {
583ff48bf5SDavid du Colombier int min;
593ff48bf5SDavid du Colombier
603ff48bf5SDavid du Colombier min = r < g ? r : g;
613ff48bf5SDavid du Colombier min = b < min ? b : min;
623ff48bf5SDavid du Colombier scale = (y << 16) / (y - min);
633ff48bf5SDavid du Colombier }
643ff48bf5SDavid du Colombier r = y + (((r - y) * scale + 0x8000) >> 16);
653ff48bf5SDavid du Colombier g = y + (((g - y) * scale + 0x8000) >> 16);
663ff48bf5SDavid du Colombier b = y + (((b - y) * scale + 0x8000) >> 16);
673ff48bf5SDavid du Colombier }
683ff48bf5SDavid du Colombier dst[0] = r;
693ff48bf5SDavid du Colombier dst[1] = g;
703ff48bf5SDavid du Colombier dst[2] = b;
713ff48bf5SDavid du Colombier }
723ff48bf5SDavid du Colombier
73*593dc095SDavid du Colombier /*
74*593dc095SDavid du Colombier * The PDF 1.4 spec. does not give the details of the math involved in the
75*593dc095SDavid du Colombier * luminosity blending. All we are given is:
76*593dc095SDavid du Colombier * "Creates a color with the luminance of the source color and the hue
77*593dc095SDavid du Colombier * and saturation of the backdrop color. This produces an inverse
78*593dc095SDavid du Colombier * effect to that of the Color mode."
79*593dc095SDavid du Colombier * From section 7.4 of the PDF 1.5 specification, which is duscussing soft
80*593dc095SDavid du Colombier * masks, we are given that, for CMYK, the luminosity is:
81*593dc095SDavid du Colombier * Y = 0.30 (1 - C)(1 - K) + 0.59 (1 - M)(1 - K) + 0.11 (1 - Y)(1 - K)
82*593dc095SDavid du Colombier * However the results of this equation do not match the results seen from
83*593dc095SDavid du Colombier * Illustrator CS. Very different results are obtained if process gray
84*593dc095SDavid du Colombier * (.5, .5, .5, 0) is blended over pure cyan, versus gray (0, 0, 0, .5) over
85*593dc095SDavid du Colombier * the same pure cyan. The first gives a medium cyan while the later gives a
86*593dc095SDavid du Colombier * medium gray. This routine seems to match Illustrator's actions. C, M and Y
87*593dc095SDavid du Colombier * are treated similar to RGB in the previous routine and black is treated
88*593dc095SDavid du Colombier * separately.
89*593dc095SDavid du Colombier *
90*593dc095SDavid du Colombier * Our component values have already been complemented, i.e. (1 - X).
91*593dc095SDavid du Colombier */
92*593dc095SDavid du Colombier static void
art_blend_luminosity_cmyk_8(byte * dst,const byte * backdrop,const byte * src)93*593dc095SDavid du Colombier art_blend_luminosity_cmyk_8(byte *dst, const byte *backdrop,
94*593dc095SDavid du Colombier const byte *src)
95*593dc095SDavid du Colombier {
96*593dc095SDavid du Colombier /* Treat CMY the same as RGB. */
97*593dc095SDavid du Colombier art_blend_luminosity_rgb_8(dst, backdrop, src);
98*593dc095SDavid du Colombier dst[3] = src[3];
99*593dc095SDavid du Colombier }
100*593dc095SDavid du Colombier
1013ff48bf5SDavid du Colombier static void
art_blend_saturation_rgb_8(byte * dst,const byte * backdrop,const byte * src)1023ff48bf5SDavid du Colombier art_blend_saturation_rgb_8(byte *dst, const byte *backdrop,
1033ff48bf5SDavid du Colombier const byte *src)
1043ff48bf5SDavid du Colombier {
1053ff48bf5SDavid du Colombier int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
1063ff48bf5SDavid du Colombier int rs = src[0], gs = src[1], bs = src[2];
1073ff48bf5SDavid du Colombier int minb, maxb;
1083ff48bf5SDavid du Colombier int mins, maxs;
1093ff48bf5SDavid du Colombier int y;
1103ff48bf5SDavid du Colombier int scale;
1113ff48bf5SDavid du Colombier int r, g, b;
1123ff48bf5SDavid du Colombier
1133ff48bf5SDavid du Colombier minb = rb < gb ? rb : gb;
1143ff48bf5SDavid du Colombier minb = minb < bb ? minb : bb;
1153ff48bf5SDavid du Colombier maxb = rb > gb ? rb : gb;
1163ff48bf5SDavid du Colombier maxb = maxb > bb ? maxb : bb;
1173ff48bf5SDavid du Colombier if (minb == maxb) {
1183ff48bf5SDavid du Colombier /* backdrop has zero saturation, avoid divide by 0 */
1193ff48bf5SDavid du Colombier dst[0] = gb;
1203ff48bf5SDavid du Colombier dst[1] = gb;
1213ff48bf5SDavid du Colombier dst[2] = gb;
1223ff48bf5SDavid du Colombier return;
1233ff48bf5SDavid du Colombier }
1243ff48bf5SDavid du Colombier
1253ff48bf5SDavid du Colombier mins = rs < gs ? rs : gs;
1263ff48bf5SDavid du Colombier mins = mins < bs ? mins : bs;
1273ff48bf5SDavid du Colombier maxs = rs > gs ? rs : gs;
1283ff48bf5SDavid du Colombier maxs = maxs > bs ? maxs : bs;
1293ff48bf5SDavid du Colombier
1303ff48bf5SDavid du Colombier scale = ((maxs - mins) << 16) / (maxb - minb);
1313ff48bf5SDavid du Colombier y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8;
1323ff48bf5SDavid du Colombier r = y + ((((rb - y) * scale) + 0x8000) >> 16);
1333ff48bf5SDavid du Colombier g = y + ((((gb - y) * scale) + 0x8000) >> 16);
1343ff48bf5SDavid du Colombier b = y + ((((bb - y) * scale) + 0x8000) >> 16);
1353ff48bf5SDavid du Colombier
1363ff48bf5SDavid du Colombier if ((r | g | b) & 0x100) {
1373ff48bf5SDavid du Colombier int scalemin, scalemax;
1383ff48bf5SDavid du Colombier int min, max;
1393ff48bf5SDavid du Colombier
1403ff48bf5SDavid du Colombier min = r < g ? r : g;
1413ff48bf5SDavid du Colombier min = min < b ? min : b;
1423ff48bf5SDavid du Colombier max = r > g ? r : g;
1433ff48bf5SDavid du Colombier max = max > b ? max : b;
1443ff48bf5SDavid du Colombier
1453ff48bf5SDavid du Colombier if (min < 0)
1463ff48bf5SDavid du Colombier scalemin = (y << 16) / (y - min);
1473ff48bf5SDavid du Colombier else
1483ff48bf5SDavid du Colombier scalemin = 0x10000;
1493ff48bf5SDavid du Colombier
1503ff48bf5SDavid du Colombier if (max > 255)
1513ff48bf5SDavid du Colombier scalemax = ((255 - y) << 16) / (max - y);
1523ff48bf5SDavid du Colombier else
1533ff48bf5SDavid du Colombier scalemax = 0x10000;
1543ff48bf5SDavid du Colombier
1553ff48bf5SDavid du Colombier scale = scalemin < scalemax ? scalemin : scalemax;
1563ff48bf5SDavid du Colombier r = y + (((r - y) * scale + 0x8000) >> 16);
1573ff48bf5SDavid du Colombier g = y + (((g - y) * scale + 0x8000) >> 16);
1583ff48bf5SDavid du Colombier b = y + (((b - y) * scale + 0x8000) >> 16);
1593ff48bf5SDavid du Colombier }
1603ff48bf5SDavid du Colombier
1613ff48bf5SDavid du Colombier dst[0] = r;
1623ff48bf5SDavid du Colombier dst[1] = g;
1633ff48bf5SDavid du Colombier dst[2] = b;
1643ff48bf5SDavid du Colombier }
1653ff48bf5SDavid du Colombier
166*593dc095SDavid du Colombier /* Our component values have already been complemented, i.e. (1 - X). */
167*593dc095SDavid du Colombier static void
art_blend_saturation_cmyk_8(byte * dst,const byte * backdrop,const byte * src)168*593dc095SDavid du Colombier art_blend_saturation_cmyk_8(byte *dst, const byte *backdrop,
169*593dc095SDavid du Colombier const byte *src)
170*593dc095SDavid du Colombier {
171*593dc095SDavid du Colombier /* Treat CMY the same as RGB */
172*593dc095SDavid du Colombier art_blend_saturation_rgb_8(dst, backdrop, src);
173*593dc095SDavid du Colombier dst[3] = backdrop[3];
174*593dc095SDavid du Colombier }
175*593dc095SDavid du Colombier
1763ff48bf5SDavid du Colombier /* This array consists of floor ((x - x * x / 255.0) * 65536 / 255 +
1773ff48bf5SDavid du Colombier 0.5) for x in [0..255]. */
1783ff48bf5SDavid du Colombier const unsigned int art_blend_sq_diff_8[256] = {
1793ff48bf5SDavid du Colombier 0, 256, 510, 762, 1012, 1260, 1506, 1750, 1992, 2231, 2469, 2705,
1803ff48bf5SDavid du Colombier 2939, 3171, 3401, 3628, 3854, 4078, 4300, 4519, 4737, 4953, 5166,
1813ff48bf5SDavid du Colombier 5378, 5588, 5795, 6001, 6204, 6406, 6606, 6803, 6999, 7192, 7384,
1823ff48bf5SDavid du Colombier 7573, 7761, 7946, 8129, 8311, 8490, 8668, 8843, 9016, 9188, 9357,
1833ff48bf5SDavid du Colombier 9524, 9690, 9853, 10014, 10173, 10331, 10486, 10639, 10790, 10939,
1843ff48bf5SDavid du Colombier 11086, 11232, 11375, 11516, 11655, 11792, 11927, 12060, 12191, 12320,
1853ff48bf5SDavid du Colombier 12447, 12572, 12695, 12816, 12935, 13052, 13167, 13280, 13390, 13499,
1863ff48bf5SDavid du Colombier 13606, 13711, 13814, 13914, 14013, 14110, 14205, 14297, 14388, 14477,
1873ff48bf5SDavid du Colombier 14564, 14648, 14731, 14811, 14890, 14967, 15041, 15114, 15184, 15253,
1883ff48bf5SDavid du Colombier 15319, 15384, 15446, 15507, 15565, 15622, 15676, 15729, 15779, 15827,
1893ff48bf5SDavid du Colombier 15874, 15918, 15960, 16001, 16039, 16075, 16110, 16142, 16172, 16200,
1903ff48bf5SDavid du Colombier 16227, 16251, 16273, 16293, 16311, 16327, 16341, 16354, 16364, 16372,
1913ff48bf5SDavid du Colombier 16378, 16382, 16384, 16384, 16382, 16378, 16372, 16364, 16354, 16341,
1923ff48bf5SDavid du Colombier 16327, 16311, 16293, 16273, 16251, 16227, 16200, 16172, 16142, 16110,
1933ff48bf5SDavid du Colombier 16075, 16039, 16001, 15960, 15918, 15874, 15827, 15779, 15729, 15676,
1943ff48bf5SDavid du Colombier 15622, 15565, 15507, 15446, 15384, 15319, 15253, 15184, 15114, 15041,
1953ff48bf5SDavid du Colombier 14967, 14890, 14811, 14731, 14648, 14564, 14477, 14388, 14297, 14205,
1963ff48bf5SDavid du Colombier 14110, 14013, 13914, 13814, 13711, 13606, 13499, 13390, 13280, 13167,
1973ff48bf5SDavid du Colombier 13052, 12935, 12816, 12695, 12572, 12447, 12320, 12191, 12060, 11927,
1983ff48bf5SDavid du Colombier 11792, 11655, 11516, 11375, 11232, 11086, 10939, 10790, 10639, 10486,
1993ff48bf5SDavid du Colombier 10331, 10173, 10014, 9853, 9690, 9524, 9357, 9188, 9016, 8843, 8668,
2003ff48bf5SDavid du Colombier 8490, 8311, 8129, 7946, 7761, 7573, 7384, 7192, 6999, 6803, 6606,
2013ff48bf5SDavid du Colombier 6406, 6204, 6001, 5795, 5588, 5378, 5166, 4953, 4737, 4519, 4300,
2023ff48bf5SDavid du Colombier 4078, 3854, 3628, 3401, 3171, 2939, 2705, 2469, 2231, 1992, 1750,
2033ff48bf5SDavid du Colombier 1506, 1260, 1012, 762, 510, 256, 0
2043ff48bf5SDavid du Colombier };
2053ff48bf5SDavid du Colombier
2063ff48bf5SDavid du Colombier /* This array consists of SoftLight (x, 255) - x, for values of x in
2073ff48bf5SDavid du Colombier the range [0..255] (normalized to [0..255 range). The original
2083ff48bf5SDavid du Colombier values were directly sampled from Adobe Illustrator 9. I've fit a
2093ff48bf5SDavid du Colombier quadratic spline to the SoftLight (x, 1) function as follows
2103ff48bf5SDavid du Colombier (normalized to [0..1] range):
2113ff48bf5SDavid du Colombier
2123ff48bf5SDavid du Colombier Anchor point (0, 0)
2133ff48bf5SDavid du Colombier Control point (0.0755, 0.302)
2143ff48bf5SDavid du Colombier Anchor point (0.18, 0.4245)
2153ff48bf5SDavid du Colombier Control point (0.4263, 0.7131)
2163ff48bf5SDavid du Colombier Anchor point (1, 1)
2173ff48bf5SDavid du Colombier
2183ff48bf5SDavid du Colombier I don't believe this is _exactly_ the function that Adobe uses,
2193ff48bf5SDavid du Colombier but it really should be close enough for all practical purposes. */
2203ff48bf5SDavid du Colombier const byte art_blend_soft_light_8[256] = {
2213ff48bf5SDavid du Colombier 0, 3, 6, 9, 11, 14, 16, 19, 21, 23, 26, 28, 30, 32, 33, 35, 37, 39,
2223ff48bf5SDavid du Colombier 40, 42, 43, 45, 46, 47, 48, 49, 51, 52, 53, 53, 54, 55, 56, 57, 57,
2233ff48bf5SDavid du Colombier 58, 58, 59, 60, 60, 60, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 63,
2243ff48bf5SDavid du Colombier 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
2253ff48bf5SDavid du Colombier 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62,
2263ff48bf5SDavid du Colombier 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 59, 59,
2273ff48bf5SDavid du Colombier 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 55, 55,
2283ff48bf5SDavid du Colombier 55, 55, 54, 54, 54, 54, 53, 53, 53, 52, 52, 52, 51, 51, 51, 51, 50,
2293ff48bf5SDavid du Colombier 50, 50, 49, 49, 49, 48, 48, 48, 47, 47, 47, 46, 46, 46, 45, 45, 45,
2303ff48bf5SDavid du Colombier 44, 44, 43, 43, 43, 42, 42, 42, 41, 41, 40, 40, 40, 39, 39, 39, 38,
2313ff48bf5SDavid du Colombier 38, 37, 37, 37, 36, 36, 35, 35, 35, 34, 34, 33, 33, 33, 32, 32, 31,
2323ff48bf5SDavid du Colombier 31, 31, 30, 30, 29, 29, 28, 28, 28, 27, 27, 26, 26, 25, 25, 25, 24,
2333ff48bf5SDavid du Colombier 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
2343ff48bf5SDavid du Colombier 16, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7,
2353ff48bf5SDavid du Colombier 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0
2363ff48bf5SDavid du Colombier };
2373ff48bf5SDavid du Colombier
2383ff48bf5SDavid du Colombier void
art_blend_pixel_8(byte * dst,const byte * backdrop,const byte * src,int n_chan,gs_blend_mode_t blend_mode)2393ff48bf5SDavid du Colombier art_blend_pixel_8(byte *dst, const byte *backdrop,
2403ff48bf5SDavid du Colombier const byte *src, int n_chan, gs_blend_mode_t blend_mode)
2413ff48bf5SDavid du Colombier {
2423ff48bf5SDavid du Colombier int i;
2433ff48bf5SDavid du Colombier byte b, s;
2443ff48bf5SDavid du Colombier bits32 t;
2453ff48bf5SDavid du Colombier
2463ff48bf5SDavid du Colombier switch (blend_mode) {
2473ff48bf5SDavid du Colombier case BLEND_MODE_Normal:
2483ff48bf5SDavid du Colombier case BLEND_MODE_Compatible: /* todo */
2493ff48bf5SDavid du Colombier memcpy(dst, src, n_chan);
2503ff48bf5SDavid du Colombier break;
2513ff48bf5SDavid du Colombier case BLEND_MODE_Multiply:
2523ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
2533ff48bf5SDavid du Colombier t = ((bits32) backdrop[i]) * ((bits32) src[i]);
2543ff48bf5SDavid du Colombier t += 0x80;
2553ff48bf5SDavid du Colombier t += (t >> 8);
2563ff48bf5SDavid du Colombier dst[i] = t >> 8;
2573ff48bf5SDavid du Colombier }
2583ff48bf5SDavid du Colombier break;
2593ff48bf5SDavid du Colombier case BLEND_MODE_Screen:
2603ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
2613ff48bf5SDavid du Colombier t =
2623ff48bf5SDavid du Colombier ((bits32) (0xff - backdrop[i])) *
2633ff48bf5SDavid du Colombier ((bits32) (0xff - src[i]));
2643ff48bf5SDavid du Colombier t += 0x80;
2653ff48bf5SDavid du Colombier t += (t >> 8);
2663ff48bf5SDavid du Colombier dst[i] = 0xff - (t >> 8);
2673ff48bf5SDavid du Colombier }
2683ff48bf5SDavid du Colombier break;
2693ff48bf5SDavid du Colombier case BLEND_MODE_Overlay:
2703ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
2713ff48bf5SDavid du Colombier b = backdrop[i];
2723ff48bf5SDavid du Colombier s = src[i];
2733ff48bf5SDavid du Colombier if (b < 0x80)
2743ff48bf5SDavid du Colombier t = 2 * ((bits32) b) * ((bits32) s);
2753ff48bf5SDavid du Colombier else
2763ff48bf5SDavid du Colombier t = 0xfe01 -
2773ff48bf5SDavid du Colombier 2 * ((bits32) (0xff - b)) * ((bits32) (0xff - s));
2783ff48bf5SDavid du Colombier t += 0x80;
2793ff48bf5SDavid du Colombier t += (t >> 8);
2803ff48bf5SDavid du Colombier dst[i] = t >> 8;
2813ff48bf5SDavid du Colombier }
2823ff48bf5SDavid du Colombier break;
2833ff48bf5SDavid du Colombier case BLEND_MODE_SoftLight:
2843ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
2853ff48bf5SDavid du Colombier b = backdrop[i];
2863ff48bf5SDavid du Colombier s = src[i];
2873ff48bf5SDavid du Colombier if (s < 0x80) {
2883ff48bf5SDavid du Colombier t = (0xff - (s << 1)) * art_blend_sq_diff_8[b];
2893ff48bf5SDavid du Colombier t += 0x8000;
2903ff48bf5SDavid du Colombier dst[i] = b - (t >> 16);
2913ff48bf5SDavid du Colombier } else {
2923ff48bf5SDavid du Colombier t =
2933ff48bf5SDavid du Colombier ((s << 1) -
2943ff48bf5SDavid du Colombier 0xff) * ((bits32) (art_blend_soft_light_8[b]));
2953ff48bf5SDavid du Colombier t += 0x80;
2963ff48bf5SDavid du Colombier t += (t >> 8);
2973ff48bf5SDavid du Colombier dst[i] = b + (t >> 8);
2983ff48bf5SDavid du Colombier }
2993ff48bf5SDavid du Colombier }
3003ff48bf5SDavid du Colombier break;
3013ff48bf5SDavid du Colombier case BLEND_MODE_HardLight:
3023ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3033ff48bf5SDavid du Colombier b = backdrop[i];
3043ff48bf5SDavid du Colombier s = src[i];
3053ff48bf5SDavid du Colombier if (s < 0x80)
3063ff48bf5SDavid du Colombier t = 2 * ((bits32) b) * ((bits32) s);
3073ff48bf5SDavid du Colombier else
3083ff48bf5SDavid du Colombier t = 0xfe01 -
3093ff48bf5SDavid du Colombier 2 * ((bits32) (0xff - b)) * ((bits32) (0xff - s));
3103ff48bf5SDavid du Colombier t += 0x80;
3113ff48bf5SDavid du Colombier t += (t >> 8);
3123ff48bf5SDavid du Colombier dst[i] = t >> 8;
3133ff48bf5SDavid du Colombier }
3143ff48bf5SDavid du Colombier break;
3153ff48bf5SDavid du Colombier case BLEND_MODE_ColorDodge:
3163ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3173ff48bf5SDavid du Colombier b = backdrop[i];
3183ff48bf5SDavid du Colombier s = 0xff - src[i];
3193ff48bf5SDavid du Colombier if (b == 0)
3203ff48bf5SDavid du Colombier dst[i] = 0;
3213ff48bf5SDavid du Colombier else if (b >= s)
3223ff48bf5SDavid du Colombier dst[i] = 0xff;
3233ff48bf5SDavid du Colombier else
3243ff48bf5SDavid du Colombier dst[i] = (0x1fe * b + s) / (s << 1);
3253ff48bf5SDavid du Colombier }
3263ff48bf5SDavid du Colombier break;
3273ff48bf5SDavid du Colombier case BLEND_MODE_ColorBurn:
3283ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3293ff48bf5SDavid du Colombier b = 0xff - backdrop[i];
3303ff48bf5SDavid du Colombier s = src[i];
3313ff48bf5SDavid du Colombier if (b == 0)
3323ff48bf5SDavid du Colombier dst[i] = 0xff;
3333ff48bf5SDavid du Colombier else if (b >= s)
3343ff48bf5SDavid du Colombier dst[i] = 0;
3353ff48bf5SDavid du Colombier else
3363ff48bf5SDavid du Colombier dst[i] = 0xff - (0x1fe * b + s) / (s << 1);
3373ff48bf5SDavid du Colombier }
3383ff48bf5SDavid du Colombier break;
3393ff48bf5SDavid du Colombier case BLEND_MODE_Darken:
3403ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3413ff48bf5SDavid du Colombier b = backdrop[i];
3423ff48bf5SDavid du Colombier s = src[i];
3433ff48bf5SDavid du Colombier dst[i] = b < s ? b : s;
3443ff48bf5SDavid du Colombier }
3453ff48bf5SDavid du Colombier break;
3463ff48bf5SDavid du Colombier case BLEND_MODE_Lighten:
3473ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3483ff48bf5SDavid du Colombier b = backdrop[i];
3493ff48bf5SDavid du Colombier s = src[i];
3503ff48bf5SDavid du Colombier dst[i] = b > s ? b : s;
3513ff48bf5SDavid du Colombier }
3523ff48bf5SDavid du Colombier break;
3533ff48bf5SDavid du Colombier case BLEND_MODE_Difference:
3543ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3553ff48bf5SDavid du Colombier art_s32 tmp;
3563ff48bf5SDavid du Colombier
3573ff48bf5SDavid du Colombier tmp = ((art_s32) backdrop[i]) - ((art_s32) src[i]);
3583ff48bf5SDavid du Colombier dst[i] = tmp < 0 ? -tmp : tmp;
3593ff48bf5SDavid du Colombier }
3603ff48bf5SDavid du Colombier break;
3613ff48bf5SDavid du Colombier case BLEND_MODE_Exclusion:
3623ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
3633ff48bf5SDavid du Colombier b = backdrop[i];
3643ff48bf5SDavid du Colombier s = src[i];
3653ff48bf5SDavid du Colombier t = ((bits32) (0xff - b)) * ((bits32) s) +
3663ff48bf5SDavid du Colombier ((bits32) b) * ((bits32) (0xff - s));
3673ff48bf5SDavid du Colombier t += 0x80;
3683ff48bf5SDavid du Colombier t += (t >> 8);
3693ff48bf5SDavid du Colombier dst[i] = t >> 8;
3703ff48bf5SDavid du Colombier }
3713ff48bf5SDavid du Colombier break;
3723ff48bf5SDavid du Colombier case BLEND_MODE_Luminosity:
373*593dc095SDavid du Colombier switch (n_chan) {
374*593dc095SDavid du Colombier case 1: /* DeviceGray */
375*593dc095SDavid du Colombier dlprintf(
376*593dc095SDavid du Colombier "art_blend_pixel_8: DeviceGray luminosity blend mode not implemented\n");
377*593dc095SDavid du Colombier break;
378*593dc095SDavid du Colombier case 3: /* DeviceRGB */
3793ff48bf5SDavid du Colombier art_blend_luminosity_rgb_8(dst, backdrop, src);
3803ff48bf5SDavid du Colombier break;
381*593dc095SDavid du Colombier case 4: /* DeviceCMYK */
382*593dc095SDavid du Colombier art_blend_luminosity_cmyk_8(dst, backdrop, src);
383*593dc095SDavid du Colombier break;
384*593dc095SDavid du Colombier default: /* Should not happen */
385*593dc095SDavid du Colombier break;
386*593dc095SDavid du Colombier }
387*593dc095SDavid du Colombier break;
3883ff48bf5SDavid du Colombier case BLEND_MODE_Color:
389*593dc095SDavid du Colombier switch (n_chan) {
390*593dc095SDavid du Colombier case 1: /* DeviceGray */
391*593dc095SDavid du Colombier dlprintf(
392*593dc095SDavid du Colombier "art_blend_pixel_8: DeviceGray color blend mode not implemented\n");
393*593dc095SDavid du Colombier break;
394*593dc095SDavid du Colombier case 3: /* DeviceRGB */
3953ff48bf5SDavid du Colombier art_blend_luminosity_rgb_8(dst, src, backdrop);
3963ff48bf5SDavid du Colombier break;
397*593dc095SDavid du Colombier case 4: /* DeviceCMYK */
398*593dc095SDavid du Colombier art_blend_luminosity_cmyk_8(dst, src, backdrop);
399*593dc095SDavid du Colombier break;
400*593dc095SDavid du Colombier default: /* Should not happen */
401*593dc095SDavid du Colombier break;
402*593dc095SDavid du Colombier }
403*593dc095SDavid du Colombier break;
4043ff48bf5SDavid du Colombier case BLEND_MODE_Saturation:
405*593dc095SDavid du Colombier switch (n_chan) {
406*593dc095SDavid du Colombier case 1: /* DeviceGray */
407*593dc095SDavid du Colombier dlprintf(
408*593dc095SDavid du Colombier "art_blend_pixel_8: DeviceGray saturation blend mode not implemented\n");
409*593dc095SDavid du Colombier break;
410*593dc095SDavid du Colombier case 3: /* DeviceRGB */
4113ff48bf5SDavid du Colombier art_blend_saturation_rgb_8(dst, backdrop, src);
4123ff48bf5SDavid du Colombier break;
413*593dc095SDavid du Colombier case 4: /* DeviceCMYK */
414*593dc095SDavid du Colombier art_blend_saturation_cmyk_8(dst, backdrop, src);
415*593dc095SDavid du Colombier break;
416*593dc095SDavid du Colombier default: /* Should not happen */
417*593dc095SDavid du Colombier break;
418*593dc095SDavid du Colombier }
419*593dc095SDavid du Colombier break;
4203ff48bf5SDavid du Colombier case BLEND_MODE_Hue:
4213ff48bf5SDavid du Colombier {
422*593dc095SDavid du Colombier byte tmp[4];
4233ff48bf5SDavid du Colombier
424*593dc095SDavid du Colombier switch (n_chan) {
425*593dc095SDavid du Colombier case 1: /* DeviceGray */
426*593dc095SDavid du Colombier dlprintf(
427*593dc095SDavid du Colombier "art_blend_pixel_8: DeviceGray hue blend mode not implemented\n");
428*593dc095SDavid du Colombier break;
429*593dc095SDavid du Colombier case 3: /* DeviceRGB */
4303ff48bf5SDavid du Colombier art_blend_luminosity_rgb_8(tmp, src, backdrop);
4313ff48bf5SDavid du Colombier art_blend_saturation_rgb_8(dst, tmp, backdrop);
432*593dc095SDavid du Colombier break;
433*593dc095SDavid du Colombier case 4: /* DeviceCMYK */
434*593dc095SDavid du Colombier art_blend_luminosity_cmyk_8(tmp, src, backdrop);
435*593dc095SDavid du Colombier art_blend_saturation_cmyk_8(dst, tmp, backdrop);
436*593dc095SDavid du Colombier break;
437*593dc095SDavid du Colombier default: /* Should not happen */
438*593dc095SDavid du Colombier break;
439*593dc095SDavid du Colombier }
4403ff48bf5SDavid du Colombier }
4413ff48bf5SDavid du Colombier break;
4423ff48bf5SDavid du Colombier default:
4433ff48bf5SDavid du Colombier dlprintf1("art_blend_pixel_8: blend mode %d not implemented\n",
4443ff48bf5SDavid du Colombier blend_mode);
4453ff48bf5SDavid du Colombier memcpy(dst, src, n_chan);
4463ff48bf5SDavid du Colombier break;
4473ff48bf5SDavid du Colombier }
4483ff48bf5SDavid du Colombier }
4493ff48bf5SDavid du Colombier
4503ff48bf5SDavid du Colombier void
art_blend_pixel(ArtPixMaxDepth * dst,const ArtPixMaxDepth * backdrop,const ArtPixMaxDepth * src,int n_chan,gs_blend_mode_t blend_mode)4513ff48bf5SDavid du Colombier art_blend_pixel(ArtPixMaxDepth* dst, const ArtPixMaxDepth *backdrop,
4523ff48bf5SDavid du Colombier const ArtPixMaxDepth* src, int n_chan,
4533ff48bf5SDavid du Colombier gs_blend_mode_t blend_mode)
4543ff48bf5SDavid du Colombier {
4553ff48bf5SDavid du Colombier int i;
4563ff48bf5SDavid du Colombier ArtPixMaxDepth b, s;
4573ff48bf5SDavid du Colombier bits32 t;
4583ff48bf5SDavid du Colombier
4593ff48bf5SDavid du Colombier switch (blend_mode) {
4603ff48bf5SDavid du Colombier case BLEND_MODE_Normal:
4613ff48bf5SDavid du Colombier case BLEND_MODE_Compatible: /* todo */
4623ff48bf5SDavid du Colombier memcpy(dst, src, n_chan * sizeof(ArtPixMaxDepth));
4633ff48bf5SDavid du Colombier break;
4643ff48bf5SDavid du Colombier case BLEND_MODE_Multiply:
4653ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
4663ff48bf5SDavid du Colombier t = ((bits32) backdrop[i]) * ((bits32) src[i]);
4673ff48bf5SDavid du Colombier t += 0x8000;
4683ff48bf5SDavid du Colombier t += (t >> 16);
4693ff48bf5SDavid du Colombier dst[i] = t >> 16;
4703ff48bf5SDavid du Colombier }
4713ff48bf5SDavid du Colombier break;
4723ff48bf5SDavid du Colombier case BLEND_MODE_Screen:
4733ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
4743ff48bf5SDavid du Colombier t =
4753ff48bf5SDavid du Colombier ((bits32) (0xffff - backdrop[i])) *
4763ff48bf5SDavid du Colombier ((bits32) (0xffff - src[i]));
4773ff48bf5SDavid du Colombier t += 0x8000;
4783ff48bf5SDavid du Colombier t += (t >> 16);
4793ff48bf5SDavid du Colombier dst[i] = 0xffff - (t >> 16);
4803ff48bf5SDavid du Colombier }
4813ff48bf5SDavid du Colombier break;
4823ff48bf5SDavid du Colombier case BLEND_MODE_Overlay:
4833ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
4843ff48bf5SDavid du Colombier b = backdrop[i];
4853ff48bf5SDavid du Colombier s = src[i];
4863ff48bf5SDavid du Colombier if (b < 0x8000)
4873ff48bf5SDavid du Colombier t = 2 * ((bits32) b) * ((bits32) s);
4883ff48bf5SDavid du Colombier else
4893ff48bf5SDavid du Colombier t = 0xfffe0001u -
4903ff48bf5SDavid du Colombier 2 * ((bits32) (0xffff - b)) * ((bits32) (0xffff - s));
4913ff48bf5SDavid du Colombier t += 0x8000;
4923ff48bf5SDavid du Colombier t += (t >> 16);
4933ff48bf5SDavid du Colombier dst[i] = t >> 16;
4943ff48bf5SDavid du Colombier }
4953ff48bf5SDavid du Colombier break;
4963ff48bf5SDavid du Colombier case BLEND_MODE_HardLight:
4973ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
4983ff48bf5SDavid du Colombier b = backdrop[i];
4993ff48bf5SDavid du Colombier s = src[i];
5003ff48bf5SDavid du Colombier if (s < 0x8000)
5013ff48bf5SDavid du Colombier t = 2 * ((bits32) b) * ((bits32) s);
5023ff48bf5SDavid du Colombier else
5033ff48bf5SDavid du Colombier t = 0xfffe0001u -
5043ff48bf5SDavid du Colombier 2 * ((bits32) (0xffff - b)) * ((bits32) (0xffff - s));
5053ff48bf5SDavid du Colombier t += 0x8000;
5063ff48bf5SDavid du Colombier t += (t >> 16);
5073ff48bf5SDavid du Colombier dst[i] = t >> 16;
5083ff48bf5SDavid du Colombier }
5093ff48bf5SDavid du Colombier break;
5103ff48bf5SDavid du Colombier case BLEND_MODE_ColorDodge:
5113ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5123ff48bf5SDavid du Colombier b = backdrop[i];
5133ff48bf5SDavid du Colombier s = src[i];
5143ff48bf5SDavid du Colombier if (b == 0)
5153ff48bf5SDavid du Colombier dst[i] = 0;
5163ff48bf5SDavid du Colombier else if (s >= b)
5173ff48bf5SDavid du Colombier dst[i] = 0xffff;
5183ff48bf5SDavid du Colombier else
5193ff48bf5SDavid du Colombier dst[i] = (0x1fffe * s + b) / (b << 1);
5203ff48bf5SDavid du Colombier }
5213ff48bf5SDavid du Colombier break;
5223ff48bf5SDavid du Colombier case BLEND_MODE_ColorBurn:
5233ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5243ff48bf5SDavid du Colombier b = 0xffff - backdrop[i];
5253ff48bf5SDavid du Colombier s = src[i];
5263ff48bf5SDavid du Colombier if (b == 0)
5273ff48bf5SDavid du Colombier dst[i] = 0xffff;
5283ff48bf5SDavid du Colombier else if (b >= s)
5293ff48bf5SDavid du Colombier dst[i] = 0;
5303ff48bf5SDavid du Colombier else
5313ff48bf5SDavid du Colombier dst[i] = 0xffff - (0x1fffe * b + s) / (s << 1);
5323ff48bf5SDavid du Colombier }
5333ff48bf5SDavid du Colombier case BLEND_MODE_Darken:
5343ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5353ff48bf5SDavid du Colombier b = backdrop[i];
5363ff48bf5SDavid du Colombier s = src[i];
5373ff48bf5SDavid du Colombier dst[i] = b < s ? b : s;
5383ff48bf5SDavid du Colombier }
5393ff48bf5SDavid du Colombier break;
5403ff48bf5SDavid du Colombier case BLEND_MODE_Lighten:
5413ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5423ff48bf5SDavid du Colombier b = backdrop[i];
5433ff48bf5SDavid du Colombier s = src[i];
5443ff48bf5SDavid du Colombier dst[i] = b > s ? b : s;
5453ff48bf5SDavid du Colombier }
5463ff48bf5SDavid du Colombier break;
5473ff48bf5SDavid du Colombier case BLEND_MODE_Difference:
5483ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5493ff48bf5SDavid du Colombier art_s32 tmp;
5503ff48bf5SDavid du Colombier
5513ff48bf5SDavid du Colombier tmp = ((art_s32) backdrop[i]) - ((art_s32) src[i]);
5523ff48bf5SDavid du Colombier dst[i] = tmp < 0 ? -tmp : tmp;
5533ff48bf5SDavid du Colombier }
5543ff48bf5SDavid du Colombier break;
5553ff48bf5SDavid du Colombier case BLEND_MODE_Exclusion:
5563ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
5573ff48bf5SDavid du Colombier b = backdrop[i];
5583ff48bf5SDavid du Colombier s = src[i];
5593ff48bf5SDavid du Colombier t = ((bits32) (0xffff - b)) * ((bits32) s) +
5603ff48bf5SDavid du Colombier ((bits32) b) * ((bits32) (0xffff - s));
5613ff48bf5SDavid du Colombier t += 0x8000;
5623ff48bf5SDavid du Colombier t += (t >> 16);
5633ff48bf5SDavid du Colombier dst[i] = t >> 16;
5643ff48bf5SDavid du Colombier }
5653ff48bf5SDavid du Colombier break;
5663ff48bf5SDavid du Colombier default:
5673ff48bf5SDavid du Colombier dlprintf1("art_blend_pixel: blend mode %d not implemented\n",
5683ff48bf5SDavid du Colombier blend_mode);
5693ff48bf5SDavid du Colombier memcpy(dst, src, n_chan);
5703ff48bf5SDavid du Colombier break;
5713ff48bf5SDavid du Colombier }
5723ff48bf5SDavid du Colombier }
5733ff48bf5SDavid du Colombier
5743ff48bf5SDavid du Colombier byte
art_pdf_union_8(byte alpha1,byte alpha2)5753ff48bf5SDavid du Colombier art_pdf_union_8(byte alpha1, byte alpha2)
5763ff48bf5SDavid du Colombier {
5773ff48bf5SDavid du Colombier int tmp;
5783ff48bf5SDavid du Colombier
5793ff48bf5SDavid du Colombier tmp = (0xff - alpha1) * (0xff - alpha2) + 0x80;
5803ff48bf5SDavid du Colombier return 0xff - ((tmp + (tmp >> 8)) >> 8);
5813ff48bf5SDavid du Colombier }
5823ff48bf5SDavid du Colombier
5833ff48bf5SDavid du Colombier byte
art_pdf_union_mul_8(byte alpha1,byte alpha2,byte alpha_mask)5843ff48bf5SDavid du Colombier art_pdf_union_mul_8(byte alpha1, byte alpha2, byte alpha_mask)
5853ff48bf5SDavid du Colombier {
5863ff48bf5SDavid du Colombier int tmp;
5873ff48bf5SDavid du Colombier
5883ff48bf5SDavid du Colombier if (alpha_mask == 0xff) {
5893ff48bf5SDavid du Colombier tmp = (0xff - alpha1) * (0xff - alpha2) + 0x80;
5903ff48bf5SDavid du Colombier return 0xff - ((tmp + (tmp >> 8)) >> 8);
5913ff48bf5SDavid du Colombier } else {
5923ff48bf5SDavid du Colombier tmp = alpha2 * alpha_mask + 0x80;
5933ff48bf5SDavid du Colombier tmp = (tmp + (tmp >> 8)) >> 8;
5943ff48bf5SDavid du Colombier tmp = (0xff - alpha1) * (0xff - tmp) + 0x80;
5953ff48bf5SDavid du Colombier return 0xff - ((tmp + (tmp >> 8)) >> 8);
5963ff48bf5SDavid du Colombier }
5973ff48bf5SDavid du Colombier }
5983ff48bf5SDavid du Colombier
5993ff48bf5SDavid du Colombier void
art_pdf_composite_pixel_alpha_8(byte * dst,const byte * src,int n_chan,gs_blend_mode_t blend_mode)6003ff48bf5SDavid du Colombier art_pdf_composite_pixel_alpha_8(byte *dst, const byte *src, int n_chan,
6013ff48bf5SDavid du Colombier gs_blend_mode_t blend_mode)
6023ff48bf5SDavid du Colombier {
6033ff48bf5SDavid du Colombier byte a_b, a_s;
6043ff48bf5SDavid du Colombier unsigned int a_r;
6053ff48bf5SDavid du Colombier int tmp;
6063ff48bf5SDavid du Colombier int src_scale;
6073ff48bf5SDavid du Colombier int c_b, c_s;
6083ff48bf5SDavid du Colombier int i;
6093ff48bf5SDavid du Colombier
6103ff48bf5SDavid du Colombier a_s = src[n_chan];
6113ff48bf5SDavid du Colombier if (a_s == 0) {
6123ff48bf5SDavid du Colombier /* source alpha is zero, avoid all computations and possible
6133ff48bf5SDavid du Colombier divide by zero errors. */
6143ff48bf5SDavid du Colombier return;
6153ff48bf5SDavid du Colombier }
6163ff48bf5SDavid du Colombier
6173ff48bf5SDavid du Colombier a_b = dst[n_chan];
6183ff48bf5SDavid du Colombier if (a_b == 0) {
6193ff48bf5SDavid du Colombier /* backdrop alpha is zero, just copy source pixels and avoid
6203ff48bf5SDavid du Colombier computation. */
6213ff48bf5SDavid du Colombier
6223ff48bf5SDavid du Colombier /* this idiom is faster than memcpy (dst, src, n_chan + 1); for
6233ff48bf5SDavid du Colombier expected small values of n_chan. */
6243ff48bf5SDavid du Colombier for (i = 0; i <= n_chan >> 2; i++) {
6253ff48bf5SDavid du Colombier ((bits32 *) dst)[i] = ((const bits32 *)src)[i];
6263ff48bf5SDavid du Colombier }
6273ff48bf5SDavid du Colombier
6283ff48bf5SDavid du Colombier return;
6293ff48bf5SDavid du Colombier }
6303ff48bf5SDavid du Colombier
6313ff48bf5SDavid du Colombier /* Result alpha is Union of backdrop and source alpha */
6323ff48bf5SDavid du Colombier tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
6333ff48bf5SDavid du Colombier a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
6343ff48bf5SDavid du Colombier /* todo: verify that a_r is nonzero in all cases */
6353ff48bf5SDavid du Colombier
6363ff48bf5SDavid du Colombier /* Compute a_s / a_r in 16.16 format */
6373ff48bf5SDavid du Colombier src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
6383ff48bf5SDavid du Colombier
6393ff48bf5SDavid du Colombier if (blend_mode == BLEND_MODE_Normal) {
6403ff48bf5SDavid du Colombier /* Do simple compositing of source over backdrop */
6413ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
6423ff48bf5SDavid du Colombier c_s = src[i];
6433ff48bf5SDavid du Colombier c_b = dst[i];
6443ff48bf5SDavid du Colombier tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
6453ff48bf5SDavid du Colombier dst[i] = tmp >> 16;
6463ff48bf5SDavid du Colombier }
6473ff48bf5SDavid du Colombier } else {
6483ff48bf5SDavid du Colombier /* Do compositing with blending */
6493ff48bf5SDavid du Colombier byte blend[ART_MAX_CHAN];
6503ff48bf5SDavid du Colombier
6513ff48bf5SDavid du Colombier art_blend_pixel_8(blend, dst, src, n_chan, blend_mode);
6523ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
6533ff48bf5SDavid du Colombier int c_bl; /* Result of blend function */
6543ff48bf5SDavid du Colombier int c_mix; /* Blend result mixed with source color */
6553ff48bf5SDavid du Colombier
6563ff48bf5SDavid du Colombier c_s = src[i];
6573ff48bf5SDavid du Colombier c_b = dst[i];
6583ff48bf5SDavid du Colombier c_bl = blend[i];
6593ff48bf5SDavid du Colombier tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
6603ff48bf5SDavid du Colombier c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
6613ff48bf5SDavid du Colombier tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
6623ff48bf5SDavid du Colombier dst[i] = tmp >> 16;
6633ff48bf5SDavid du Colombier }
6643ff48bf5SDavid du Colombier }
6653ff48bf5SDavid du Colombier dst[n_chan] = a_r;
6663ff48bf5SDavid du Colombier }
6673ff48bf5SDavid du Colombier
6683ff48bf5SDavid du Colombier #if 0
6693ff48bf5SDavid du Colombier /**
6703ff48bf5SDavid du Colombier * art_pdf_composite_pixel_knockout_8: Composite two pixels with knockout.
6713ff48bf5SDavid du Colombier * @dst: Where to store resulting pixel, also immediate backdrop.
6723ff48bf5SDavid du Colombier * @backdrop: Initial backdrop color.
6733ff48bf5SDavid du Colombier * @src: Source pixel color.
6743ff48bf5SDavid du Colombier * @n_chan: Number of channels.
6753ff48bf5SDavid du Colombier * @blend_mode: Blend mode.
6763ff48bf5SDavid du Colombier *
6773ff48bf5SDavid du Colombier * Composites two pixels using the compositing operation specialized
6783ff48bf5SDavid du Colombier * for knockout groups (Section 5.5). A few things to keep in mind:
6793ff48bf5SDavid du Colombier *
6803ff48bf5SDavid du Colombier * 1. This is a reference implementation, not a high-performance one.
6813ff48bf5SDavid du Colombier *
6823ff48bf5SDavid du Colombier * 2. All pixels are assumed to have a single alpha channel.
6833ff48bf5SDavid du Colombier *
6843ff48bf5SDavid du Colombier * 3. Zero is black, one is white.
6853ff48bf5SDavid du Colombier *
6863ff48bf5SDavid du Colombier * Also note that src and dst are expected to be allocated aligned to
6873ff48bf5SDavid du Colombier * 32 bit boundaries, ie bytes from [0] to [(n_chan + 3) & -4] may
6883ff48bf5SDavid du Colombier * be accessed.
6893ff48bf5SDavid du Colombier *
6903ff48bf5SDavid du Colombier * All pixel values have both alpha and shape channels, ie with those
6913ff48bf5SDavid du Colombier * included the total number of channels is @n_chan + 2.
6923ff48bf5SDavid du Colombier *
6933ff48bf5SDavid du Colombier * An invariant: shape >= alpha.
6943ff48bf5SDavid du Colombier **/
6953ff48bf5SDavid du Colombier void
6963ff48bf5SDavid du Colombier art_pdf_composite_pixel_knockout_8(byte *dst,
6973ff48bf5SDavid du Colombier const byte *backdrop, const byte *src,
6983ff48bf5SDavid du Colombier int n_chan, gs_blend_mode_t blend_mode)
6993ff48bf5SDavid du Colombier {
7003ff48bf5SDavid du Colombier int i;
7013ff48bf5SDavid du Colombier byte ct[ART_MAX_CHAN + 1];
7023ff48bf5SDavid du Colombier byte src_shape;
7033ff48bf5SDavid du Colombier byte backdrop_alpha;
7043ff48bf5SDavid du Colombier byte dst_alpha;
7053ff48bf5SDavid du Colombier bits32 src_opacity;
7063ff48bf5SDavid du Colombier bits32 backdrop_weight, t_weight;
7073ff48bf5SDavid du Colombier int tmp;
7083ff48bf5SDavid du Colombier
7093ff48bf5SDavid du Colombier if (src[n_chan] == 0)
7103ff48bf5SDavid du Colombier return;
7113ff48bf5SDavid du Colombier if (src[n_chan + 1] == 255 && blend_mode == BLEND_MODE_Normal ||
7123ff48bf5SDavid du Colombier dst[n_chan] == 0) {
7133ff48bf5SDavid du Colombier /* this idiom is faster than memcpy (dst, src, n_chan + 2); for
7143ff48bf5SDavid du Colombier expected small values of n_chan. */
7153ff48bf5SDavid du Colombier for (i = 0; i <= (n_chan + 1) >> 2; i++) {
7163ff48bf5SDavid du Colombier ((bits32 *) dst)[i] = ((const bits32 *)src[i]);
7173ff48bf5SDavid du Colombier }
7183ff48bf5SDavid du Colombier
7193ff48bf5SDavid du Colombier return;
7203ff48bf5SDavid du Colombier }
7213ff48bf5SDavid du Colombier
7223ff48bf5SDavid du Colombier
7233ff48bf5SDavid du Colombier src_shape = src[n_chan + 1]; /* $fs_i$ */
7243ff48bf5SDavid du Colombier src_opacity = (255 * src[n_chan] + 0x80) / src_shape; /* $qs_i$ */
7253ff48bf5SDavid du Colombier #if 0
7263ff48bf5SDavid du Colombier for (i = 0; i < (n_chan + 3) >> 2; i++) {
7273ff48bf5SDavid du Colombier ((bits32 *) src_tmp)[i] = ((const bits32 *)src[i]);
7283ff48bf5SDavid du Colombier }
7293ff48bf5SDavid du Colombier src_tmp[n_chan] = src_opacity;
7303ff48bf5SDavid du Colombier
7313ff48bf5SDavid du Colombier for (i = 0; i <= n_chan >> 2; i++) {
7323ff48bf5SDavid du Colombier ((bits32 *) tmp)[i] = ((bits32 *) backdrop[i]);
7333ff48bf5SDavid du Colombier }
7343ff48bf5SDavid du Colombier #endif
7353ff48bf5SDavid du Colombier
7363ff48bf5SDavid du Colombier backdrop_scale = if (blend_mode == BLEND_MODE_Normal) {
7373ff48bf5SDavid du Colombier /* Do simple compositing of source over backdrop */
7383ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
7393ff48bf5SDavid du Colombier c_s = src[i];
7403ff48bf5SDavid du Colombier c_b = dst[i];
7413ff48bf5SDavid du Colombier tmp = (c_b << 16) + ct_scale * (c_s - c_b) + 0x8000;
7423ff48bf5SDavid du Colombier ct[i] = tmp >> 16;
7433ff48bf5SDavid du Colombier }
7443ff48bf5SDavid du Colombier } else {
7453ff48bf5SDavid du Colombier /* Do compositing with blending */
7463ff48bf5SDavid du Colombier byte blend[ART_MAX_CHAN];
7473ff48bf5SDavid du Colombier
7483ff48bf5SDavid du Colombier art_blend_pixel_8(blend, backdrop, src, n_chan, blend_mode);
7493ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
7503ff48bf5SDavid du Colombier int c_bl; /* Result of blend function */
7513ff48bf5SDavid du Colombier int c_mix; /* Blend result mixed with source color */
7523ff48bf5SDavid du Colombier
7533ff48bf5SDavid du Colombier c_s = src[i];
7543ff48bf5SDavid du Colombier c_b = dst[i];
7553ff48bf5SDavid du Colombier c_bl = blend[i];
7563ff48bf5SDavid du Colombier tmp = a_b * (((int)c_bl) - ((int)c_s)) + 0x80;
7573ff48bf5SDavid du Colombier c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
7583ff48bf5SDavid du Colombier tmp = (c_b << 16) + ct_scale * (c_mix - c_b) + 0x8000;
7593ff48bf5SDavid du Colombier ct[i] = tmp >> 16;
7603ff48bf5SDavid du Colombier }
7613ff48bf5SDavid du Colombier }
7623ff48bf5SDavid du Colombier
7633ff48bf5SDavid du Colombier /* do weighted average of $Ct$ using relative alpha contribution as weight */
7643ff48bf5SDavid du Colombier backdrop_alpha = backdrop[n_chan];
7653ff48bf5SDavid du Colombier tmp = (0xff - blend_alpha) * (0xff - backdrop_alpha) + 0x80;
7663ff48bf5SDavid du Colombier dst_alpha = 0xff - (((tmp >> 8) + tmp) >> 8);
7673ff48bf5SDavid du Colombier dst[n_chan] = dst_alpha;
7683ff48bf5SDavid du Colombier t_weight = ((blend_alpha << 16) + 0x8000) / dst_alpha;
7693ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
7703ff48bf5SDavid du Colombier
7713ff48bf5SDavid du Colombier }
7723ff48bf5SDavid du Colombier }
7733ff48bf5SDavid du Colombier #endif
7743ff48bf5SDavid du Colombier
7753ff48bf5SDavid du Colombier void
art_pdf_uncomposite_group_8(byte * dst,const byte * backdrop,const byte * src,byte src_alpha_g,int n_chan)7763ff48bf5SDavid du Colombier art_pdf_uncomposite_group_8(byte *dst,
7773ff48bf5SDavid du Colombier const byte *backdrop,
7783ff48bf5SDavid du Colombier const byte *src, byte src_alpha_g, int n_chan)
7793ff48bf5SDavid du Colombier {
7803ff48bf5SDavid du Colombier byte backdrop_alpha = backdrop[n_chan];
7813ff48bf5SDavid du Colombier int i;
7823ff48bf5SDavid du Colombier int tmp;
7833ff48bf5SDavid du Colombier int scale;
7843ff48bf5SDavid du Colombier
7853ff48bf5SDavid du Colombier dst[n_chan] = src_alpha_g;
7863ff48bf5SDavid du Colombier
7873ff48bf5SDavid du Colombier if (src_alpha_g == 0)
7883ff48bf5SDavid du Colombier return;
7893ff48bf5SDavid du Colombier
7903ff48bf5SDavid du Colombier scale = (backdrop_alpha * 255 * 2 + src_alpha_g) / (src_alpha_g << 1) -
7913ff48bf5SDavid du Colombier backdrop_alpha;
7923ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
7933ff48bf5SDavid du Colombier int si, di;
7943ff48bf5SDavid du Colombier
7953ff48bf5SDavid du Colombier si = src[i];
7963ff48bf5SDavid du Colombier di = backdrop[i];
7973ff48bf5SDavid du Colombier tmp = (si - di) * scale + 0x80;
7983ff48bf5SDavid du Colombier tmp = si + ((tmp + (tmp >> 8)) >> 8);
7993ff48bf5SDavid du Colombier
8003ff48bf5SDavid du Colombier /* todo: it should be possible to optimize these cond branches */
8013ff48bf5SDavid du Colombier if (tmp < 0)
8023ff48bf5SDavid du Colombier tmp = 0;
8033ff48bf5SDavid du Colombier if (tmp > 255)
8043ff48bf5SDavid du Colombier tmp = 255;
8053ff48bf5SDavid du Colombier dst[i] = tmp;
8063ff48bf5SDavid du Colombier }
8073ff48bf5SDavid du Colombier
8083ff48bf5SDavid du Colombier }
8093ff48bf5SDavid du Colombier
8103ff48bf5SDavid du Colombier void
art_pdf_recomposite_group_8(byte * dst,byte * dst_alpha_g,const byte * src,byte src_alpha_g,int n_chan,byte alpha,gs_blend_mode_t blend_mode)8113ff48bf5SDavid du Colombier art_pdf_recomposite_group_8(byte *dst, byte *dst_alpha_g,
8123ff48bf5SDavid du Colombier const byte *src, byte src_alpha_g,
8133ff48bf5SDavid du Colombier int n_chan,
8143ff48bf5SDavid du Colombier byte alpha, gs_blend_mode_t blend_mode)
8153ff48bf5SDavid du Colombier {
8163ff48bf5SDavid du Colombier byte dst_alpha;
8173ff48bf5SDavid du Colombier int i;
8183ff48bf5SDavid du Colombier int tmp;
8193ff48bf5SDavid du Colombier int scale;
8203ff48bf5SDavid du Colombier
8213ff48bf5SDavid du Colombier if (src_alpha_g == 0)
8223ff48bf5SDavid du Colombier return;
8233ff48bf5SDavid du Colombier
8243ff48bf5SDavid du Colombier if (blend_mode == BLEND_MODE_Normal && alpha == 255) {
8253ff48bf5SDavid du Colombier /* In this case, uncompositing and recompositing cancel each
8263ff48bf5SDavid du Colombier other out. Note: if the reason that alpha == 255 is that
8273ff48bf5SDavid du Colombier there is no constant mask and no soft mask, then this
8283ff48bf5SDavid du Colombier operation should be optimized away at a higher level. */
8293ff48bf5SDavid du Colombier for (i = 0; i <= n_chan >> 2; i++)
8303ff48bf5SDavid du Colombier ((bits32 *) dst)[i] = ((const bits32 *)src)[i];
8313ff48bf5SDavid du Colombier if (dst_alpha_g != NULL) {
8323ff48bf5SDavid du Colombier tmp = (255 - *dst_alpha_g) * (255 - src_alpha_g) + 0x80;
8333ff48bf5SDavid du Colombier *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
8343ff48bf5SDavid du Colombier }
8353ff48bf5SDavid du Colombier return;
8363ff48bf5SDavid du Colombier } else {
8373ff48bf5SDavid du Colombier /* "interesting" blend mode */
8383ff48bf5SDavid du Colombier byte ca[ART_MAX_CHAN + 1]; /* $C, \alpha$ */
8393ff48bf5SDavid du Colombier
8403ff48bf5SDavid du Colombier dst_alpha = dst[n_chan];
8413ff48bf5SDavid du Colombier if (src_alpha_g == 255 || dst_alpha == 0) {
8423ff48bf5SDavid du Colombier for (i = 0; i < (n_chan + 3) >> 2; i++)
8433ff48bf5SDavid du Colombier ((bits32 *) ca)[i] = ((const bits32 *)src)[i];
8443ff48bf5SDavid du Colombier } else {
8453ff48bf5SDavid du Colombier /* Uncomposite the color. In other words, solve
8463ff48bf5SDavid du Colombier "src = (ca, src_alpha_g) over dst" for ca */
8473ff48bf5SDavid du Colombier
8483ff48bf5SDavid du Colombier /* todo (maybe?): replace this code with call to
8493ff48bf5SDavid du Colombier art_pdf_uncomposite_group_8() to reduce code
8503ff48bf5SDavid du Colombier duplication. */
8513ff48bf5SDavid du Colombier
8523ff48bf5SDavid du Colombier scale = (dst_alpha * 255 * 2 + src_alpha_g) / (src_alpha_g << 1) -
8533ff48bf5SDavid du Colombier dst_alpha;
8543ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
8553ff48bf5SDavid du Colombier int si, di;
8563ff48bf5SDavid du Colombier
8573ff48bf5SDavid du Colombier si = src[i];
8583ff48bf5SDavid du Colombier di = dst[i];
8593ff48bf5SDavid du Colombier tmp = (si - di) * scale + 0x80;
8603ff48bf5SDavid du Colombier tmp = si + ((tmp + (tmp >> 8)) >> 8);
8613ff48bf5SDavid du Colombier
8623ff48bf5SDavid du Colombier /* todo: it should be possible to optimize these cond branches */
8633ff48bf5SDavid du Colombier if (tmp < 0)
8643ff48bf5SDavid du Colombier tmp = 0;
8653ff48bf5SDavid du Colombier if (tmp > 255)
8663ff48bf5SDavid du Colombier tmp = 255;
8673ff48bf5SDavid du Colombier ca[i] = tmp;
8683ff48bf5SDavid du Colombier }
8693ff48bf5SDavid du Colombier }
8703ff48bf5SDavid du Colombier
8713ff48bf5SDavid du Colombier tmp = src_alpha_g * alpha + 0x80;
8723ff48bf5SDavid du Colombier tmp = (tmp + (tmp >> 8)) >> 8;
8733ff48bf5SDavid du Colombier ca[n_chan] = tmp;
8743ff48bf5SDavid du Colombier if (dst_alpha_g != NULL) {
8753ff48bf5SDavid du Colombier tmp = (255 - *dst_alpha_g) * (255 - tmp) + 0x80;
8763ff48bf5SDavid du Colombier *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
8773ff48bf5SDavid du Colombier }
8783ff48bf5SDavid du Colombier art_pdf_composite_pixel_alpha_8(dst, ca, n_chan, blend_mode);
8793ff48bf5SDavid du Colombier }
8803ff48bf5SDavid du Colombier /* todo: optimize BLEND_MODE_Normal buf alpha != 255 case */
8813ff48bf5SDavid du Colombier }
8823ff48bf5SDavid du Colombier
8833ff48bf5SDavid du Colombier void
art_pdf_composite_group_8(byte * dst,byte * dst_alpha_g,const byte * src,int n_chan,byte alpha,gs_blend_mode_t blend_mode)8843ff48bf5SDavid du Colombier art_pdf_composite_group_8(byte *dst, byte *dst_alpha_g,
8853ff48bf5SDavid du Colombier const byte *src,
8863ff48bf5SDavid du Colombier int n_chan, byte alpha, gs_blend_mode_t blend_mode)
8873ff48bf5SDavid du Colombier {
8883ff48bf5SDavid du Colombier byte src_alpha; /* $\alpha g_n$ */
8893ff48bf5SDavid du Colombier byte src_tmp[ART_MAX_CHAN + 1];
8903ff48bf5SDavid du Colombier int i;
8913ff48bf5SDavid du Colombier int tmp;
8923ff48bf5SDavid du Colombier
8933ff48bf5SDavid du Colombier if (alpha == 255) {
8943ff48bf5SDavid du Colombier art_pdf_composite_pixel_alpha_8(dst, src, n_chan, blend_mode);
8953ff48bf5SDavid du Colombier if (dst_alpha_g != NULL) {
8963ff48bf5SDavid du Colombier tmp = (255 - *dst_alpha_g) * (255 - src[n_chan]) + 0x80;
8973ff48bf5SDavid du Colombier *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
8983ff48bf5SDavid du Colombier }
8993ff48bf5SDavid du Colombier } else {
9003ff48bf5SDavid du Colombier src_alpha = src[n_chan];
9013ff48bf5SDavid du Colombier if (src_alpha == 0)
9023ff48bf5SDavid du Colombier return;
9033ff48bf5SDavid du Colombier for (i = 0; i < (n_chan + 3) >> 2; i++)
9043ff48bf5SDavid du Colombier ((bits32 *) src_tmp)[i] = ((const bits32 *)src)[i];
9053ff48bf5SDavid du Colombier tmp = src_alpha * alpha + 0x80;
9063ff48bf5SDavid du Colombier src_tmp[n_chan] = (tmp + (tmp >> 8)) >> 8;
9073ff48bf5SDavid du Colombier art_pdf_composite_pixel_alpha_8(dst, src_tmp, n_chan, blend_mode);
9083ff48bf5SDavid du Colombier if (dst_alpha_g != NULL) {
9093ff48bf5SDavid du Colombier tmp = (255 - *dst_alpha_g) * (255 - src_tmp[n_chan]) + 0x80;
9103ff48bf5SDavid du Colombier *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
9113ff48bf5SDavid du Colombier }
9123ff48bf5SDavid du Colombier }
9133ff48bf5SDavid du Colombier }
9143ff48bf5SDavid du Colombier
9153ff48bf5SDavid du Colombier void
art_pdf_composite_knockout_simple_8(byte * dst,byte * dst_shape,const byte * src,int n_chan,byte opacity)9163ff48bf5SDavid du Colombier art_pdf_composite_knockout_simple_8(byte *dst,
9173ff48bf5SDavid du Colombier byte *dst_shape,
9183ff48bf5SDavid du Colombier const byte *src,
9193ff48bf5SDavid du Colombier int n_chan, byte opacity)
9203ff48bf5SDavid du Colombier {
9213ff48bf5SDavid du Colombier byte src_shape = src[n_chan];
9223ff48bf5SDavid du Colombier int i;
9233ff48bf5SDavid du Colombier
9243ff48bf5SDavid du Colombier if (src_shape == 0)
9253ff48bf5SDavid du Colombier return;
9263ff48bf5SDavid du Colombier else if (src_shape == 255) {
9273ff48bf5SDavid du Colombier for (i = 0; i < (n_chan + 3) >> 2; i++)
9283ff48bf5SDavid du Colombier ((bits32 *) dst)[i] = ((const bits32 *)src)[i];
9293ff48bf5SDavid du Colombier dst[n_chan] = opacity;
9303ff48bf5SDavid du Colombier if (dst_shape != NULL)
9313ff48bf5SDavid du Colombier *dst_shape = 255;
9323ff48bf5SDavid du Colombier } else {
9333ff48bf5SDavid du Colombier /* Use src_shape to interpolate (in premultiplied alpha space)
9343ff48bf5SDavid du Colombier between dst and (src, opacity). */
9353ff48bf5SDavid du Colombier int dst_alpha = dst[n_chan];
9363ff48bf5SDavid du Colombier byte result_alpha;
9373ff48bf5SDavid du Colombier int tmp;
9383ff48bf5SDavid du Colombier
9393ff48bf5SDavid du Colombier tmp = (opacity - dst_alpha) * src_shape + 0x80;
9403ff48bf5SDavid du Colombier result_alpha = dst_alpha + ((tmp + (tmp >> 8)) >> 8);
9413ff48bf5SDavid du Colombier
9423ff48bf5SDavid du Colombier if (result_alpha != 0)
9433ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
9443ff48bf5SDavid du Colombier /* todo: optimize this - can strength-reduce so that
9453ff48bf5SDavid du Colombier inner loop is a single interpolation */
9463ff48bf5SDavid du Colombier tmp = dst[i] * dst_alpha * (255 - src_shape) +
9473ff48bf5SDavid du Colombier ((int)src[i]) * opacity * src_shape + (result_alpha << 7);
9483ff48bf5SDavid du Colombier dst[i] = tmp / (result_alpha * 255);
9493ff48bf5SDavid du Colombier }
9503ff48bf5SDavid du Colombier dst[n_chan] = result_alpha;
9513ff48bf5SDavid du Colombier
9523ff48bf5SDavid du Colombier /* union in dst_shape if non-null */
9533ff48bf5SDavid du Colombier if (dst_shape != NULL) {
9543ff48bf5SDavid du Colombier tmp = (255 - *dst_shape) * (255 - src_shape) + 0x80;
9553ff48bf5SDavid du Colombier *dst_shape = 255 - ((tmp + (tmp >> 8)) >> 8);
9563ff48bf5SDavid du Colombier }
9573ff48bf5SDavid du Colombier }
9583ff48bf5SDavid du Colombier }
9593ff48bf5SDavid du Colombier
9603ff48bf5SDavid du Colombier void
art_pdf_composite_knockout_isolated_8(byte * dst,byte * dst_shape,const byte * src,int n_chan,byte shape,byte alpha_mask,byte shape_mask)9613ff48bf5SDavid du Colombier art_pdf_composite_knockout_isolated_8(byte *dst,
9623ff48bf5SDavid du Colombier byte *dst_shape,
9633ff48bf5SDavid du Colombier const byte *src,
9643ff48bf5SDavid du Colombier int n_chan,
9653ff48bf5SDavid du Colombier byte shape,
9663ff48bf5SDavid du Colombier byte alpha_mask, byte shape_mask)
9673ff48bf5SDavid du Colombier {
9683ff48bf5SDavid du Colombier int tmp;
9693ff48bf5SDavid du Colombier int i;
9703ff48bf5SDavid du Colombier
9713ff48bf5SDavid du Colombier if (shape == 0)
9723ff48bf5SDavid du Colombier return;
9733ff48bf5SDavid du Colombier else if ((shape & shape_mask) == 255) {
9743ff48bf5SDavid du Colombier for (i = 0; i < (n_chan + 3) >> 2; i++)
9753ff48bf5SDavid du Colombier ((bits32 *) dst)[i] = ((const bits32 *)src)[i];
9763ff48bf5SDavid du Colombier tmp = src[n_chan] * alpha_mask + 0x80;
9773ff48bf5SDavid du Colombier dst[n_chan] = (tmp + (tmp >> 8)) >> 8;
9783ff48bf5SDavid du Colombier if (dst_shape != NULL)
9793ff48bf5SDavid du Colombier *dst_shape = 255;
9803ff48bf5SDavid du Colombier } else {
9813ff48bf5SDavid du Colombier /* Use src_shape to interpolate (in premultiplied alpha space)
9823ff48bf5SDavid du Colombier between dst and (src, opacity). */
9833ff48bf5SDavid du Colombier byte src_shape, src_alpha;
9843ff48bf5SDavid du Colombier int dst_alpha = dst[n_chan];
9853ff48bf5SDavid du Colombier byte result_alpha;
9863ff48bf5SDavid du Colombier int tmp;
9873ff48bf5SDavid du Colombier
9883ff48bf5SDavid du Colombier tmp = shape * shape_mask + 0x80;
9893ff48bf5SDavid du Colombier src_shape = (tmp + (tmp >> 8)) >> 8;
9903ff48bf5SDavid du Colombier
9913ff48bf5SDavid du Colombier tmp = src[n_chan] * alpha_mask + 0x80;
9923ff48bf5SDavid du Colombier src_alpha = (tmp + (tmp >> 8)) >> 8;
9933ff48bf5SDavid du Colombier
9943ff48bf5SDavid du Colombier tmp = (src_alpha - dst_alpha) * src_shape + 0x80;
9953ff48bf5SDavid du Colombier result_alpha = dst_alpha + ((tmp + (tmp >> 8)) >> 8);
9963ff48bf5SDavid du Colombier
9973ff48bf5SDavid du Colombier if (result_alpha != 0)
9983ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
9993ff48bf5SDavid du Colombier /* todo: optimize this - can strength-reduce so that
10003ff48bf5SDavid du Colombier inner loop is a single interpolation */
10013ff48bf5SDavid du Colombier tmp = dst[i] * dst_alpha * (255 - src_shape) +
10023ff48bf5SDavid du Colombier ((int)src[i]) * src_alpha * src_shape +
10033ff48bf5SDavid du Colombier (result_alpha << 7);
10043ff48bf5SDavid du Colombier dst[i] = tmp / (result_alpha * 255);
10053ff48bf5SDavid du Colombier }
10063ff48bf5SDavid du Colombier dst[n_chan] = result_alpha;
10073ff48bf5SDavid du Colombier
10083ff48bf5SDavid du Colombier /* union in dst_shape if non-null */
10093ff48bf5SDavid du Colombier if (dst_shape != NULL) {
10103ff48bf5SDavid du Colombier tmp = (255 - *dst_shape) * (255 - src_shape) + 0x80;
10113ff48bf5SDavid du Colombier *dst_shape = 255 - ((tmp + (tmp >> 8)) >> 8);
10123ff48bf5SDavid du Colombier }
10133ff48bf5SDavid du Colombier }
10143ff48bf5SDavid du Colombier }
10153ff48bf5SDavid du Colombier
10163ff48bf5SDavid du Colombier void
art_pdf_composite_knockout_8(byte * dst,byte * dst_alpha_g,const byte * backdrop,const byte * src,int n_chan,byte shape,byte alpha_mask,byte shape_mask,gs_blend_mode_t blend_mode)10173ff48bf5SDavid du Colombier art_pdf_composite_knockout_8(byte *dst,
10183ff48bf5SDavid du Colombier byte *dst_alpha_g,
10193ff48bf5SDavid du Colombier const byte *backdrop,
10203ff48bf5SDavid du Colombier const byte *src,
10213ff48bf5SDavid du Colombier int n_chan,
10223ff48bf5SDavid du Colombier byte shape,
10233ff48bf5SDavid du Colombier byte alpha_mask,
10243ff48bf5SDavid du Colombier byte shape_mask, gs_blend_mode_t blend_mode)
10253ff48bf5SDavid du Colombier {
10263ff48bf5SDavid du Colombier /* This implementation follows the Adobe spec pretty closely, rather
10273ff48bf5SDavid du Colombier than trying to do anything clever. For example, in the case of a
10283ff48bf5SDavid du Colombier Normal blend_mode when the top group is non-isolated, uncompositing
10293ff48bf5SDavid du Colombier and recompositing is more work than needed. So be it. Right now,
10303ff48bf5SDavid du Colombier I'm more worried about manageability than raw performance. */
10313ff48bf5SDavid du Colombier byte alpha_t;
10323ff48bf5SDavid du Colombier byte src_alpha, src_shape;
10333ff48bf5SDavid du Colombier byte src_opacity;
10343ff48bf5SDavid du Colombier byte ct[ART_MAX_CHAN];
10353ff48bf5SDavid du Colombier byte backdrop_alpha;
10363ff48bf5SDavid du Colombier byte alpha_g_i_1, alpha_g_i, alpha_i;
10373ff48bf5SDavid du Colombier int tmp;
10383ff48bf5SDavid du Colombier int i;
10393ff48bf5SDavid du Colombier int scale_b;
10403ff48bf5SDavid du Colombier int scale_src;
10413ff48bf5SDavid du Colombier
10423ff48bf5SDavid du Colombier if (shape == 0 || shape_mask == 0)
10433ff48bf5SDavid du Colombier return;
10443ff48bf5SDavid du Colombier
10453ff48bf5SDavid du Colombier tmp = shape * shape_mask + 0x80;
10463ff48bf5SDavid du Colombier /* $f s_i$ */
10473ff48bf5SDavid du Colombier src_shape = (tmp + (tmp >> 8)) >> 8;
10483ff48bf5SDavid du Colombier
10493ff48bf5SDavid du Colombier tmp = src[n_chan] * alpha_mask + 0x80;
10503ff48bf5SDavid du Colombier src_alpha = (tmp + (tmp >> 8)) >> 8;
10513ff48bf5SDavid du Colombier
10523ff48bf5SDavid du Colombier /* $q s_i$ */
10533ff48bf5SDavid du Colombier src_opacity = (src_alpha * 510 + src_shape) / (2 * src_shape);
10543ff48bf5SDavid du Colombier
10553ff48bf5SDavid du Colombier /* $\alpha t$, \alpha g_b is always zero for knockout groups */
10563ff48bf5SDavid du Colombier alpha_t = src_opacity;
10573ff48bf5SDavid du Colombier
10583ff48bf5SDavid du Colombier /* $\alpha b$ */
10593ff48bf5SDavid du Colombier backdrop_alpha = backdrop[n_chan];
10603ff48bf5SDavid du Colombier
10613ff48bf5SDavid du Colombier tmp = (0xff - src_opacity) * backdrop_alpha;
10623ff48bf5SDavid du Colombier /* $(1 - q s_i) \cdot alpha_b$ scaled by 2^16 */
10633ff48bf5SDavid du Colombier scale_b = tmp + (tmp >> 7) + (tmp >> 14);
10643ff48bf5SDavid du Colombier
10653ff48bf5SDavid du Colombier /* $q s_i$ scaled by 2^16 */
10663ff48bf5SDavid du Colombier scale_src = (src_opacity << 8) + (src_opacity) + (src_opacity >> 7);
10673ff48bf5SDavid du Colombier
10683ff48bf5SDavid du Colombier /* Do simple compositing of source over backdrop */
10693ff48bf5SDavid du Colombier if (blend_mode == BLEND_MODE_Normal) {
10703ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
10713ff48bf5SDavid du Colombier int c_s;
10723ff48bf5SDavid du Colombier int c_b;
10733ff48bf5SDavid du Colombier
10743ff48bf5SDavid du Colombier c_s = src[i];
10753ff48bf5SDavid du Colombier c_b = backdrop[i];
10763ff48bf5SDavid du Colombier tmp = (c_b << 16) * scale_b + (c_s - c_b) + scale_src + 0x8000;
10773ff48bf5SDavid du Colombier ct[i] = tmp >> 16;
10783ff48bf5SDavid du Colombier }
10793ff48bf5SDavid du Colombier } else {
10803ff48bf5SDavid du Colombier byte blend[ART_MAX_CHAN];
10813ff48bf5SDavid du Colombier
10823ff48bf5SDavid du Colombier art_blend_pixel_8(blend, backdrop, src, n_chan, blend_mode);
10833ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
10843ff48bf5SDavid du Colombier int c_s;
10853ff48bf5SDavid du Colombier int c_b;
10863ff48bf5SDavid du Colombier int c_bl; /* Result of blend function */
10873ff48bf5SDavid du Colombier int c_mix; /* Blend result mixed with source color */
10883ff48bf5SDavid du Colombier
10893ff48bf5SDavid du Colombier c_s = src[i];
10903ff48bf5SDavid du Colombier c_b = backdrop[i];
10913ff48bf5SDavid du Colombier c_bl = blend[i];
10923ff48bf5SDavid du Colombier tmp = backdrop_alpha * (c_bl - ((int)c_s)) + 0x80;
10933ff48bf5SDavid du Colombier c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
10943ff48bf5SDavid du Colombier tmp = (c_b << 16) * scale_b + (c_mix - c_b) + scale_src + 0x8000;
10953ff48bf5SDavid du Colombier ct[i] = tmp >> 16;
10963ff48bf5SDavid du Colombier }
10973ff48bf5SDavid du Colombier }
10983ff48bf5SDavid du Colombier
10993ff48bf5SDavid du Colombier /* $\alpha g_{i - 1}$ */
11003ff48bf5SDavid du Colombier alpha_g_i_1 = *dst_alpha_g;
11013ff48bf5SDavid du Colombier
11023ff48bf5SDavid du Colombier tmp = src_shape * (((int)alpha_t) - alpha_g_i_1) + 0x80;
11033ff48bf5SDavid du Colombier /* $\alpha g_i$ */
11043ff48bf5SDavid du Colombier alpha_g_i = alpha_g_i_1 + ((tmp + (tmp >> 8)) >> 8);
11053ff48bf5SDavid du Colombier
11063ff48bf5SDavid du Colombier tmp = (0xff - backdrop_alpha) * (0xff - alpha_g_i) + 0x80;
11073ff48bf5SDavid du Colombier /* $\alpha_i$ */
11083ff48bf5SDavid du Colombier alpha_i = 0xff - ((tmp + (tmp >> 8)) >> 8);
11093ff48bf5SDavid du Colombier
11103ff48bf5SDavid du Colombier if (alpha_i > 0) {
11113ff48bf5SDavid du Colombier int scale_dst;
11123ff48bf5SDavid du Colombier int scale_t;
11133ff48bf5SDavid du Colombier byte dst_alpha;
11143ff48bf5SDavid du Colombier
11153ff48bf5SDavid du Colombier /* $f s_i / \alpha_i$ scaled by 2^16 */
11163ff48bf5SDavid du Colombier scale_t = ((src_shape << 17) + alpha_i) / (2 * alpha_i);
11173ff48bf5SDavid du Colombier
11183ff48bf5SDavid du Colombier /* $\alpha_{i - 1}$ */
11193ff48bf5SDavid du Colombier dst_alpha = dst[n_chan];
11203ff48bf5SDavid du Colombier
11213ff48bf5SDavid du Colombier tmp = (1 - src_shape) * dst_alpha;
11223ff48bf5SDavid du Colombier tmp = (tmp << 9) + (tmp << 1) + (tmp >> 7) + alpha_i;
11233ff48bf5SDavid du Colombier scale_dst = tmp / (2 * alpha_i);
11243ff48bf5SDavid du Colombier
11253ff48bf5SDavid du Colombier for (i = 0; i < n_chan; i++) {
11263ff48bf5SDavid du Colombier tmp = dst[i] * scale_dst + ct[i] * scale_t + 0x8000;
11273ff48bf5SDavid du Colombier /* todo: clamp? */
11283ff48bf5SDavid du Colombier dst[i] = tmp >> 16;
11293ff48bf5SDavid du Colombier }
11303ff48bf5SDavid du Colombier }
11313ff48bf5SDavid du Colombier dst[n_chan] = alpha_i;
11323ff48bf5SDavid du Colombier *dst_alpha_g = alpha_g_i;
11333ff48bf5SDavid du Colombier }
1134