xref: /plan9/sys/src/cmd/gs/src/gxblend.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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