Code

Fix for bug #577012
[inkscape.git] / src / libnr / nr-compose-transform.cpp
1 #define __NR_COMPOSE_TRANSFORM_C__
3 /*
4  * Pixel buffer rendering library
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * This code is in public domain
10  */
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
16 #include "nr-pixops.h"
17 #include "nr-matrix.h"
19 /*#ifdef WITH_MMX
20 #ifdef __cplusplus
21 extern "C" {
22 #endif // __cplusplus
23 / * fixme: * /
24 / *int nr_have_mmx (void);
25 #define NR_PIXOPS_MMX (1 && nr_have_mmx ())
26 #ifdef __cplusplus
27 }
28 #endif //__cplusplus
29 #endif
30 */
32 /* fixme: Implement missing (Lauris) */
33 /* fixme: PREMUL colors before calculating average (Lauris) */
35 /* Fixed point precision */
36 #define FBITS 12
37 #define FBITS_HP 18 // In some places we need a higher precision
39 void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
40                                                const unsigned char *spx, int sw, int sh, int srs,
41                                                const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
42 void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
43                                                const unsigned char *spx, int sw, int sh, int srs,
44                                                const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
45 void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
46                                                const unsigned char *spx, int sw, int sh, int srs,
47                                                const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
48 void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
49                                                const unsigned char *spx, int sw, int sh, int srs,
50                                                const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
52 void
53 nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
54                                                const unsigned char *spx, int sw, int sh, int srs,
55                                                const NR::Matrix &d2s, unsigned int alpha, int xd, int yd)
56 {
57         int xsize, ysize, size, dbits;
58         long FFs_x_x, FFs_x_y, FFs_y_x, FFs_y_y, FFs__x, FFs__y;
59         long FFs_x_x_S, FFs_x_y_S, FFs_y_x_S, FFs_y_y_S;
60         /* Subpixel positions */
61         int FF_sx_S[256];
62         int FF_sy_S[256];
63         unsigned char *d0;
64         int FFsx0, FFsy0;
65         int x, y;
67         if (alpha == 0) return;
68     if (alpha>255) {
69         g_warning("In transform PPN alpha=%u>255",alpha);
70     }
72     // The color component is stored temporarily with a range of [0,255^3], so more supersampling and we get an overflow (fortunately Inkscape's preferences also doesn't allow a higher setting)
73     if (xd+yd>8) {
74         xd = 4;
75         yd = 4;
76     }
78         xsize = (1 << xd);
79         ysize = (1 << yd);
80         size = xsize * ysize;
81         dbits = xd + yd;
82     unsigned int rounding_fix = size/2;
84         /* Set up fixed point matrix */
85         FFs_x_x = (long) floor(d2s[0] * (1 << FBITS) + 0.5);
86         FFs_x_y = (long) floor(d2s[1] * (1 << FBITS) + 0.5);
87         FFs_y_x = (long) floor(d2s[2] * (1 << FBITS) + 0.5);
88         FFs_y_y = (long) floor(d2s[3] * (1 << FBITS) + 0.5);
89         FFs__x = (long) floor(d2s[4] * (1 << FBITS) + 0.5);
90         FFs__y = (long) floor(d2s[5] * (1 << FBITS) + 0.5);
92         FFs_x_x_S = FFs_x_x >> xd;
93         FFs_x_y_S = FFs_x_y >> xd;
94         FFs_y_x_S = FFs_y_x >> yd;
95         FFs_y_y_S = FFs_y_y >> yd;
97         /* Set up subpixel matrix */
98         /* fixme: We can calculate that in floating point (Lauris) */
99         for (y = 0; y < ysize; y++) {
100                 for (x = 0; x < xsize; x++) {
101                         FF_sx_S[y * xsize + x] = FFs_x_x_S * x + FFs_y_x_S * y;
102                         FF_sy_S[y * xsize + x] = FFs_x_y_S * x + FFs_y_y_S * y;
103                 }
104         }
106         d0 = px;
107         FFsx0 = FFs__x;
108         FFsy0 = FFs__y;
110         for (y = 0; y < h; y++) {
111                 unsigned char *d;
112                 long FFsx, FFsy;
113                 d = d0;
114                 FFsx = FFsx0;
115                 FFsy = FFsy0;
116                 for (x = 0; x < w; x++) {
117                         unsigned int r, g, b, a;
118                         long sx, sy;
119                         int i;
120                         r = g = b = a = 0;
121                         for (i = 0; i < size; i++) {
122                                 sx = (FFsx + FF_sx_S[i]) >> FBITS;
123                                 if ((sx >= 0) && (sx < sw)) {
124                                         sy = (FFsy + FF_sy_S[i]) >> FBITS;
125                                         if ((sy >= 0) && (sy < sh)) {
126                                                 const unsigned char *s;
127                                                 s = spx + sy * srs + sx * 4;
128                                                 r += NR_PREMUL_112 (s[0], s[3]); // s in [0,255]
129                                                 g += NR_PREMUL_112 (s[1], s[3]);
130                                                 b += NR_PREMUL_112 (s[2], s[3]);
131                                                 a += s[3];
132                         // a=sum(s3)
133                         // r,g,b in [0,sum(s3)*255]
134                                         }
135                                 }
136                         }
137                         a = (a*alpha + rounding_fix) >> dbits;
138             // a=sum(s3)*alpha/size=avg(s3)*alpha
139             // Compare to nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P
140                         if (a != 0) {
141                                 r = (r*alpha + rounding_fix) >> dbits;
142                                 g = (g*alpha + rounding_fix) >> dbits;
143                                 b = (b*alpha + rounding_fix) >> dbits;
144                 // r,g,b in [0,avg(s3)*alpha*255]=[0,a*255]
145                 if (a == 255*255) {
146                                         /* Full coverage, demul src */
147                                         d[0] = NR_NORMALIZE_31(r);
148                                         d[1] = NR_NORMALIZE_31(g);
149                                         d[2] = NR_NORMALIZE_31(b);
150                                         d[3] = NR_NORMALIZE_21(a);
151                 } else if (d[3] == 0) {
152                     /* Only foreground, demul src */
153                     d[0] = NR_DEMUL_321(r,a);
154                     d[1] = NR_DEMUL_321(g,a);
155                     d[2] = NR_DEMUL_321(b,a);
156                     d[3] = NR_NORMALIZE_21(a);
157                                 } else {
158                                         unsigned int ca;
159                                         /* Full composition */
160                                         ca = NR_COMPOSEA_213(a, d[3]);
161                                         d[0] = NR_COMPOSEPNN_321131 (r, a, d[0], d[3], ca);
162                                         d[1] = NR_COMPOSEPNN_321131 (g, a, d[1], d[3], ca);
163                                         d[2] = NR_COMPOSEPNN_321131 (b, a, d[2], d[3], ca);
164                                         d[3] = NR_NORMALIZE_31(ca);
165                                 }
166                         }
167                         /* Advance pointers */
168                         FFsx += FFs_x_x;
169                         FFsy += FFs_x_y;
170                         d += 4;
171                 }
172                 FFsx0 += FFs_y_x;
173                 FFsy0 += FFs_y_y;
174                 d0 += rs;
175         }
178 void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
179                                                     const unsigned char *spx, int sw, int sh, int srs,
180                                                     const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
182 static void
183 nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h, int rs,
184                                                  const unsigned char *spx, int sw, int sh, int srs,
185                                                  const long long *FFd2s, unsigned int alpha)
187     unsigned char *d0;
188         long long FFsx0, FFsy0;
189         int x, y;
191         d0 = px;
192         FFsx0 = FFd2s[4];
193         FFsy0 = FFd2s[5];
195         for (y = 0; y < h; y++) {
196                 unsigned char *d;
197                 long long FFsx, FFsy;
198                 d = d0;
199                 FFsx = FFsx0;
200                 FFsy = FFsy0;
201                 for (x = 0; x < w; x++) {
202                         long sx, sy;
203                         sx = long(FFsx >> FBITS_HP);
204                         if ((sx >= 0) && (sx < sw)) {
205                                 sy = long(FFsy >> FBITS_HP);
206                                 if ((sy >= 0) && (sy < sh)) {
207                                         const unsigned char *s;
208                                         unsigned int a;
209                                         s = spx + sy * srs + sx * 4;
210                                         a = NR_PREMUL_112 (s[3], alpha);
211                                         if (a != 0) {
212                                                 if ((a == 255*255) || (d[3] == 0)) {
213                                                         /* Transparent BG, premul src */
214                                                         d[0] = NR_PREMUL_121 (s[0], a);
215                                                         d[1] = NR_PREMUL_121 (s[1], a);
216                                                         d[2] = NR_PREMUL_121 (s[2], a);
217                                                         d[3] = NR_NORMALIZE_21(a);
218                                                 } else {
219                                                         d[0] = NR_COMPOSENPP_1211 (s[0], a, d[0]);
220                                                         d[1] = NR_COMPOSENPP_1211 (s[1], a, d[1]);
221                                                         d[2] = NR_COMPOSENPP_1211 (s[2], a, d[2]);
222                                                         d[3] = NR_COMPOSEA_211(a, d[3]);
223                                                 }
224                                         }
225                                 }
226                         }
227                         /* Advance pointers */
228                         FFsx += FFd2s[0];
229                         FFsy += FFd2s[1];
230                         d += 4;
231                 }
232                 FFsx0 += FFd2s[2];
233                 FFsy0 += FFd2s[3];
234                 d0 += rs;
235         }
238 static void
239 nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h, int rs,
240                                                  const unsigned char *spx, int sw, int sh, int srs,
241                                                  const long long *FFd2s, const long *FF_S, unsigned int alpha, int dbits)
243         int size;
244         unsigned char *d0;
245         long long FFsx0, FFsy0;
246         int x, y;
248         size = (1 << dbits);
249     unsigned int rounding_fix = size/2;
251         d0 = px;
252         FFsx0 = FFd2s[4];
253         FFsy0 = FFd2s[5];
255         for (y = 0; y < h; y++) {
256                 unsigned char *d;
257                 long long FFsx, FFsy;
258                 d = d0;
259                 FFsx = FFsx0;
260                 FFsy = FFsy0;
261                 for (x = 0; x < w; x++) {
262                         unsigned int r, g, b, a;
263                         int i;
264                         r = g = b = a = 0;
265                         for (i = 0; i < size; i++) {
266                                 long sx, sy;
267                                 sx = (long (FFsx >> (FBITS_HP - FBITS)) + FF_S[2 * i]) >> FBITS;
268                                 if ((sx >= 0) && (sx < sw)) {
269                                         sy = (long (FFsy >> (FBITS_HP - FBITS)) + FF_S[2 * i + 1]) >> FBITS;
270                                         if ((sy >= 0) && (sy < sh)) {
271                                                 const unsigned char *s;
272                                                 s = spx + sy * srs + sx * 4;
273                                                 r += NR_PREMUL_112(s[0], s[3]);
274                                                 g += NR_PREMUL_112(s[1], s[3]);
275                                                 b += NR_PREMUL_112(s[2], s[3]);
276                                                 a += s[3];
277                                         }
278                                 }
279                         }
280                         a = (a*alpha + rounding_fix) >> dbits;
281                         if (a != 0) {
282                                 r = (r*alpha + rounding_fix) >> dbits;
283                                 g = (g*alpha + rounding_fix) >> dbits;
284                                 b = (b*alpha + rounding_fix) >> dbits;
285                                 if ((a == 255*255) || (d[3] == 0)) {
286                                         /* Transparent BG, premul src */
287                                         d[0] = NR_NORMALIZE_31(r);
288                                         d[1] = NR_NORMALIZE_31(g);
289                                         d[2] = NR_NORMALIZE_31(b);
290                                         d[3] = NR_NORMALIZE_21(a);
291                                 } else {
292                                         d[0] = NR_COMPOSEPPP_3211 (r, a, d[0]);
293                                         d[1] = NR_COMPOSEPPP_3211 (g, a, d[1]);
294                                         d[2] = NR_COMPOSEPPP_3211 (b, a, d[2]);
295                                         d[3] = NR_COMPOSEA_211(a, d[3]);
296                                 }
297                         }
298                         /* Advance pointers */
299                         FFsx += FFd2s[0];
300                         FFsy += FFd2s[1];
301                         d += 4;
302                 }
303                 FFsx0 += FFd2s[2];
304                 FFsy0 += FFd2s[3];
305                 d0 += rs;
306         }
309 void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
310                                                     const unsigned char *spx, int sw, int sh, int srs,
311                                                     const NR::Matrix &d2s, unsigned int alpha, int xd, int yd)
313         int dbits;
314         long FFd2s[6];
315         long long FFd2s_HP[6]; // with higher precision
316         int i;
318         if (alpha == 0) return;
319     if (alpha>255) {
320         g_warning("In transform PPN alpha=%u>255",alpha);
321     }
323     // The color component is stored temporarily with a range of [0,255^3], so more supersampling and we get an overflow (fortunately Inkscape's preferences also doesn't allow a higher setting)
324     if (xd+yd>8) {
325         xd = 4;
326         yd = 4;
327     }
329     dbits = xd + yd;
331         for (i = 0; i < 6; i++) {
332                 FFd2s[i] = (long) floor(d2s[i] * (1 << FBITS) + 0.5);
333                 FFd2s_HP[i] = (long long) floor(d2s[i] * (1 << FBITS_HP) + 0.5);;
334         }
336         if (dbits == 0) {
337                 nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s_HP, alpha);
338         } else {
339                 int xsize, ysize;
340                 long FFs_x_x_S, FFs_x_y_S, FFs_y_x_S, FFs_y_y_S;
341                 long FF_S[2 * 256];
342                 int x, y;
344                 xsize = (1 << xd);
345                 ysize = (1 << yd);
347                 FFs_x_x_S = FFd2s[0] >> xd;
348                 FFs_x_y_S = FFd2s[1] >> xd;
349                 FFs_y_x_S = FFd2s[2] >> yd;
350                 FFs_y_y_S = FFd2s[3] >> yd;
352                 /* Set up subpixel matrix */
353                 /* fixme: We can calculate that in floating point (Lauris) */
354                 for (y = 0; y < ysize; y++) {
355                         for (x = 0; x < xsize; x++) {
356                                 FF_S[2 * (y * xsize + x)] = FFs_x_x_S * x + FFs_y_x_S * y;
357                                 FF_S[2 * (y * xsize + x) + 1] = FFs_x_y_S * x + FFs_y_y_S * y;
358                         }
359                 }
361                 nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s_HP, FF_S, alpha, dbits);
362         }
365 void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
366                                                     const unsigned char *spx, int sw, int sh, int srs,
367                                                     const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);