Code

unify linear and radial gradients in same file (with appropriate note
[inkscape.git] / src / libnr / nr-gradient.cpp
1 #define __NR_GRADIENT_C__
3 /*
4  * Pixel buffer rendering library
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 /*
16  * Derived in part from public domain code by Lauris Kaplinski
17  */
19 #include <libnr/nr-pixops.h>
20 #include <libnr/nr-pixblock-pixel.h>
21 #include <libnr/nr-blit.h>
22 #include <libnr/nr-gradient.h>
24 /* Common */
26 #define noNR_USE_GENERIC_RENDERER
28 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
29 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
31 /* Linear */
33 static void nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
34 static void nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
35 static void nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
36 static void nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
37 static void nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb);
39 NRRenderer *
40 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
41                              const unsigned char *cv, 
42                              unsigned int spread, 
43                              const NRMatrix *gs2px,
44                              float x0, float y0,
45                              float x1, float y1)
46 {
47         NRMatrix n2gs, n2px, px2n;
49         lgr->renderer.render = nr_lgradient_render_block;
51         lgr->vector = cv;
52         lgr->spread = spread;
54         n2gs.c[0] = x1 - x0;
55         n2gs.c[1] = y1 - y0;
56         n2gs.c[2] = y1 - y0;
57         n2gs.c[3] = x0 - x1;
58         n2gs.c[4] = x0;
59         n2gs.c[5] = y0;
61         nr_matrix_multiply (&n2px, &n2gs, gs2px);
62         nr_matrix_invert (&px2n, &n2px);
64         lgr->x0 = n2px.c[4] - 0.5;
65         lgr->y0 = n2px.c[5] - 0.5;
66         lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
67         lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
69         return (NRRenderer *) lgr;
70 }
72 static void
73 nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
74 {
75         NRLGradientRenderer *lgr;
76         int width, height;
78         lgr = (NRLGradientRenderer *) r;
80         width = pb->area.x1 - pb->area.x0;
81         height = pb->area.y1 - pb->area.y0;
83 #ifdef NR_USE_GENERIC_RENDERER
84         nr_lgradient_render_generic (lgr, pb);
85 #else
86         if (pb->empty) {
87                 switch (pb->mode) {
88                 case NR_PIXBLOCK_MODE_A8:
89                         nr_lgradient_render_generic (lgr, pb);
90                         break;
91                 case NR_PIXBLOCK_MODE_R8G8B8:
92                         nr_lgradient_render_generic (lgr, pb);
93                         break;
94                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
95                         nr_lgradient_render_R8G8B8A8N_EMPTY (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
96                         break;
97                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
98                         nr_lgradient_render_generic (lgr, pb);
99                         break;
100                 default:
101                         break;
102                 }
103         } else {
104                 switch (pb->mode) {
105                 case NR_PIXBLOCK_MODE_A8:
106                         nr_lgradient_render_generic (lgr, pb);
107                         break;
108                 case NR_PIXBLOCK_MODE_R8G8B8:
109                         nr_lgradient_render_R8G8B8 (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
110                         break;
111                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
112                         nr_lgradient_render_R8G8B8A8N (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
113                         break;
114                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
115                         nr_lgradient_render_generic (lgr, pb);
116                         break;
117                 default:
118                         break;
119                 }
120         }
121 #endif
124 static void
125 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
127         int x, y;
128         double pos;
130         for (y = 0; y < height; y++) {
131                 const unsigned char *s;
132                 unsigned char *d;
133                 int idx;
134                 d = px + y * rs;
135                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
136                 if (lgr->spread == NR_GRADIENT_SPREAD_PAD) {
137                         for (x = 0; x < width; x++) {
138                                 idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
139                                 s = lgr->vector + 4 * idx;
140                                 d[0] = s[0];
141                                 d[1] = s[1];
142                                 d[2] = s[2];
143                                 d[3] = s[3];
144                                 d += 4;
145                                 pos += lgr->dx;
146                         }
147                 } else if (lgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
148                         for (x = 0; x < width; x++) {
149                                 idx = (int) ((long long) pos & NRG_2MASK);
150                                 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
151                                 s = lgr->vector + 4 * idx;
152                                 d[0] = s[0];
153                                 d[1] = s[1];
154                                 d[2] = s[2];
155                                 d[3] = s[3];
156                                 d += 4;
157                                 pos += lgr->dx;
158                         }
159                 } else {
160                         for (x = 0; x < width; x++) {
161                                 idx = (int) ((long long) pos & NRG_MASK);
162                                 s = lgr->vector + 4 * idx;
163                                 d[0] = s[0];
164                                 d[1] = s[1];
165                                 d[2] = s[2];
166                                 d[3] = s[3];
167                                 d += 4;
168                                 pos += lgr->dx;
169                         }
170                 }
171         }
174 static void
175 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
177         int x, y;
178         unsigned char *d;
179         double pos;
181         for (y = 0; y < height; y++) {
182                 d = px + y * rs;
183                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
184                 for (x = 0; x < width; x++) {
185                         int idx;
186                         unsigned int ca;
187                         const unsigned char *s;
188                         switch (lgr->spread) {
189                         case NR_GRADIENT_SPREAD_PAD:
190                                 idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
191                                 break;
192                         case NR_GRADIENT_SPREAD_REFLECT:
193                                 idx = (int) ((long long) pos & NRG_2MASK);
194                                 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
195                                 break;
196                         case NR_GRADIENT_SPREAD_REPEAT:
197                                 idx = (int) ((long long) pos & NRG_MASK);
198                                 break;
199                         default:
200                                 idx = 0;
201                                 break;
202                         }
203                         /* Full composition */
204                         s = lgr->vector + 4 * idx;
205                         if (s[3] == 255) {
206                                 d[0] = s[0];
207                                 d[1] = s[1];
208                                 d[2] = s[2];
209                                 d[3] = 255;
210                         } else if (s[3] != 0) {
211                                 ca = NR_COMPOSEA_112(s[3],d[3]);
212                                 d[0] = NR_COMPOSENNN_111121 (s[0], s[3], d[0], d[3], ca);
213                                 d[1] = NR_COMPOSENNN_111121 (s[1], s[3], d[1], d[3], ca);
214                                 d[2] = NR_COMPOSENNN_111121 (s[2], s[3], d[2], d[3], ca);
215                                 d[3] = NR_NORMALIZE_21(ca);
216                         }
217                         d += 4;
218                         pos += lgr->dx;
219                 }
220         }
223 static void
224 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
226         int x, y;
227         unsigned char *d;
228         double pos;
230         for (y = 0; y < height; y++) {
231                 d = px + y * rs;
232                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
233                 for (x = 0; x < width; x++) {
234                         int idx;
235                         const unsigned char *s;
236                         switch (lgr->spread) {
237                         case NR_GRADIENT_SPREAD_PAD:
238                                 idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
239                                 break;
240                         case NR_GRADIENT_SPREAD_REFLECT:
241                                 idx = (int) ((long long) pos & NRG_2MASK);
242                                 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
243                                 break;
244                         case NR_GRADIENT_SPREAD_REPEAT:
245                                 idx = (int) ((long long) pos & NRG_MASK);
246                                 break;
247                         default:
248                                 idx = 0;
249                                 break;
250                         }
251                         /* Full composition */
252                         s = lgr->vector + 4 * idx;
253                         d[0] = NR_COMPOSEN11_1111 (s[0], s[3], d[0]);
254                         d[1] = NR_COMPOSEN11_1111 (s[1], s[3], d[1]);
255                         d[2] = NR_COMPOSEN11_1111 (s[2], s[3], d[2]);
256                         d += 3;
257                         pos += lgr->dx;
258                 }
259         }
262 static void
263 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
265         int x, y;
266         unsigned char *d;
267         double pos;
268         int bpp;
269         NRPixBlock spb;
270         int x0, y0, width, height, rs;
272         x0 = pb->area.x0;
273         y0 = pb->area.y0;
274         width = pb->area.x1 - pb->area.x0;
275         height = pb->area.y1 - pb->area.y0;
276         rs = pb->rs;
278         nr_pixblock_setup_extern (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
279                                   (unsigned char *) lgr->vector,
280                                   4 * NR_GRADIENT_VECTOR_LENGTH,
281                                   0, 0);
282     spb.visible_area = pb->visible_area; 
283         bpp = (pb->mode == NR_PIXBLOCK_MODE_A8) ? 1 : (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
285         for (y = 0; y < height; y++) {
286                 d = NR_PIXBLOCK_PX (pb) + y * rs;
287                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
288                 for (x = 0; x < width; x++) {
289                         int idx;
290                         const unsigned char *s;
291                         switch (lgr->spread) {
292                         case NR_GRADIENT_SPREAD_PAD:
293                                 idx = (int) CLAMP (pos, 0, (double) NRG_MASK);
294                                 break;
295                         case NR_GRADIENT_SPREAD_REFLECT:
296                                 idx = (int) ((long long) pos & NRG_2MASK);
297                                 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
298                                 break;
299                         case NR_GRADIENT_SPREAD_REPEAT:
300                                 idx = (int) ((long long) pos & NRG_MASK);
301                                 break;
302                         default:
303                                 idx = 0;
304                                 break;
305                         }
306                         s = lgr->vector + 4 * idx;
307                         nr_compose_pixblock_pixblock_pixel (pb, d, &spb, s);
308                         d += bpp;
309                         pos += lgr->dx;
310                 }
311         }
313         nr_pixblock_release (&spb);
316 /* Radial */
318 static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
319 static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
320 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
321 static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
322 static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
324 NRRenderer *
325 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
326                             unsigned char const *cv,
327                             unsigned spread,
328                             NRMatrix const *gs2px,
329                             float cx, float cy,
330                             float fx, float fy,
331                             float r)
333     rgr->vector = cv;
334     rgr->spread = spread;
336     if (r < NR_EPSILON) {
337         rgr->renderer.render = nr_rgradient_render_block_end;
338     } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
339                NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
340         rgr->renderer.render = nr_rgradient_render_block_symmetric;
342         nr_matrix_invert(&rgr->px2gs, gs2px);
343         rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
344         rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
345         rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
346         rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
347         rgr->px2gs.c[4] -= cx;
348         rgr->px2gs.c[5] -= cy;
349         rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
350         rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
352         rgr->cx = 0.0;
353         rgr->cy = 0.0;
354         rgr->fx = rgr->cx;
355         rgr->fy = rgr->cy;
356         rgr->r = 1.0;
357     } else {
358         rgr->renderer.render = nr_rgradient_render_block_optimized;
360         NR::Coord const df = hypot(fx - cx, fy - cy);
361         if (df >= r) {
362             fx = cx + (fx - cx ) * r / (float) df;
363             fy = cy + (fy - cy ) * r / (float) df;
364         }
366         NRMatrix n2gs;
367         n2gs.c[0] = cx - fx;
368         n2gs.c[1] = cy - fy;
369         n2gs.c[2] = cy - fy;
370         n2gs.c[3] = fx - cx;
371         n2gs.c[4] = fx;
372         n2gs.c[5] = fy;
374         NRMatrix n2px;
375         nr_matrix_multiply(&n2px, &n2gs, gs2px);
376         nr_matrix_invert(&rgr->px2gs, &n2px);
378         rgr->cx = 1.0;
379         rgr->cy = 0.0;
380         rgr->fx = 0.0;
381         rgr->fy = 0.0;
382         rgr->r = r / (float) hypot(fx - cx, fy - cy);
383         rgr->C = 1.0F - rgr->r * rgr->r;
384         /* INVARIANT: C < 0 */
385         rgr->C = MIN(rgr->C, -NR_EPSILON);
386     }
388     return (NRRenderer *) rgr;
391 static void
392 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
394     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
395     nr_rgradient_render_generic_symmetric(rgr, pb);
398 static void
399 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
401     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
402     nr_rgradient_render_generic_optimized(rgr, pb);
405 static void
406 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
408     unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
410     nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
413 /*
414  * The archetype is following
415  *
416  * gx gy - pixel coordinates
417  * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
418  *
419  * (1)  (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
420  * (2)  (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
421  *
422  * (3)   Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
423  * (4)  (gy - fy) / (gx - fx) = D
424  * (5)   Py = D * Px - D * fx + fy
425  *
426  * (6)   D * fx - fy + cy = N
427  * (7)   Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
428  * (8)  (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
429  *
430  * (9)   A = D * D + 1
431  * (10)  B = -2 * (cx + D * N)
432  * (11)  C = cx * cx + N * N - r * r
433  *
434  * (12)  Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
435  */
437 static void
438 nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
440     NR::Coord const dx = rgr->px2gs.c[0];
441     NR::Coord const dy = rgr->px2gs.c[1];
443     if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
444         for (int y = pb->area.y0; y < pb->area.y1; y++) {
445             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
446             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
447             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
448             for (int x = pb->area.x0; x < pb->area.x1; x++) {
449                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
450                 int idx;
451                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
452                     idx = (int) ((long long) pos & NRG_2MASK);
453                     if (idx > NRG_MASK) idx = NRG_2MASK - idx;
454                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
455                     idx = (int) ((long long) pos & NRG_MASK);
456                 } else {
457                     idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
458                 }
459                 unsigned char const *s = rgr->vector + 4 * idx;
460                 d[0] = NR_COMPOSENPP_1111(s[0], s[3], d[0]);
461                 d[1] = NR_COMPOSENPP_1111(s[1], s[3], d[1]);
462                 d[2] = NR_COMPOSENPP_1111(s[2], s[3], d[2]);
463                 d[3] = NR_COMPOSEA_111(s[3], d[3]);
464                 d += 4;
465                 gx += dx;
466                 gy += dy;
467             }
468         }
469     } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
470         for (int y = pb->area.y0; y < pb->area.y1; y++) {
471             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
472             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
473             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
474             for (int x = pb->area.x0; x < pb->area.x1; x++) {
475                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
476                 int idx;
477                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
478                     idx = (int) ((long long) pos & NRG_2MASK);
479                     if (idx > NRG_MASK) idx = NRG_2MASK - idx;
480                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
481                     idx = (int) ((long long) pos & NRG_MASK);
482                 } else {
483                     idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
484                 }
485                 unsigned char const *s = rgr->vector + 4 * idx;
486                 if (s[3] == 255) {
487                     d[0] = s[0];
488                     d[1] = s[1];
489                     d[2] = s[2];
490                     d[3] = 255;
491                 } else if (s[3] != 0) {
492                     unsigned ca = NR_COMPOSEA_112(s[3], d[3]);
493                     d[0] = NR_COMPOSENNN_111121(s[0], s[3], d[0], d[3], ca);
494                     d[1] = NR_COMPOSENNN_111121(s[1], s[3], d[1], d[3], ca);
495                     d[2] = NR_COMPOSENNN_111121(s[2], s[3], d[2], d[3], ca);
496                     d[3] = NR_NORMALIZE_21(ca);
497                 }
498                 d += 4;
499                 gx += dx;
500                 gy += dy;
501             }
502         }
503     } else {
504         NRPixBlock spb;
505         nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
506                                  (unsigned char *) rgr->vector,
507                                  4 * NR_GRADIENT_VECTOR_LENGTH,
508                                  0, 0);
509         int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
510                           ? 1
511                           : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
512                           ? 3
513                           : 4 );
515         for (int y = pb->area.y0; y < pb->area.y1; y++) {
516             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
517             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
518             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
519             for (int x = pb->area.x0; x < pb->area.x1; x++) {
520                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
521                 int idx;
522                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
523                     idx = (int) ((long long) pos & NRG_2MASK);
524                     if (idx > NRG_MASK) idx = NRG_2MASK - idx;
525                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
526                     idx = (int) ((long long) pos & NRG_MASK);
527                 } else {
528                     idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
529                 }
530                 unsigned char const *s = rgr->vector + 4 * idx;
531                 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
532                 d += bpp;
533                 gx += dx;
534                 gy += dy;
535             }
536         }
538         nr_pixblock_release(&spb);
539     }
542 static void
543 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
545     int const x0 = pb->area.x0;
546     int const y0 = pb->area.y0;
547     int const x1 = pb->area.x1;
548     int const y1 = pb->area.y1;
549     int const rs = pb->rs;
551     NRPixBlock spb;
552     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
553                              (unsigned char *) rgr->vector,
554                              4 * NR_GRADIENT_VECTOR_LENGTH,
555                              0, 0);
556     int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
557                       ? 1
558                       : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
559                       ? 3
560                       : 4 );
562     for (int y = y0; y < y1; y++) {
563         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
564         NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
565         NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
566         NR::Coord const dx = rgr->px2gs.c[0];
567         NR::Coord const dy = rgr->px2gs.c[1];
568         for (int x = x0; x < x1; x++) {
569             NR::Coord const gx2 = gx * gx;
570             NR::Coord const gxy2 = gx2 + gy * gy;
571             NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
572             /* INVARIANT: qgx2_4 >= 0.0 */
573             /* qgx2_4 = MAX(qgx2_4, 0.0); */
574             NR::Coord const pxgx = gx + sqrt(qgx2_4);
575             /* We can safely divide by 0 here */
576             /* If we are sure pxgx cannot be -0 */
577             NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
578             int idx;
579             if (pos < (1U << 31)) {
580                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
581                     idx = (int) ((long long) pos & NRG_2MASK);
582                     if (idx > NRG_MASK) idx = NRG_2MASK - idx;
583                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
584                     idx = (int) ((long long) pos & NRG_MASK);
585                 } else {
586                     idx = (int) CLAMP(pos, 0, (double) (NR_GRADIENT_VECTOR_LENGTH - 1));
587                 }
588             } else {
589                 idx = NR_GRADIENT_VECTOR_LENGTH - 1;
590             }
591             unsigned char const *s = rgr->vector + 4 * idx;
592             nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
593             d += bpp;
595             gx += dx;
596             gy += dy;
597         }
598     }
600     nr_pixblock_release(&spb);
604 /*
605   Local Variables:
606   mode:c++
607   c-file-style:"stroustrup"
608   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
609   indent-tabs-mode:nil
610   fill-column:99
611   End:
612 */
613 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :