Code

factor out gradient index calculation
[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 inline unsigned char const *index_to_pointer(int idx,
32                                              unsigned char const *vector)
33 {
34   return vector + 4 * idx;
35 }
37 inline unsigned char const *r_to_pointer_pad(NR::Coord r,
38                                              unsigned char const *vector)
39 {
40   return index_to_pointer((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
41 }
43 inline unsigned char const *r_to_pointer_repeat(NR::Coord r,
44                                                 unsigned char const *vector)
45 {
46   return index_to_pointer((int)((long long)r & NRG_MASK), vector);
47 }
49 inline unsigned char const *r_to_pointer_reflect(NR::Coord r,
50                                                  unsigned char const *vector)
51 {
52   int idx = (int) ((long long)r & NRG_2MASK);
53   if (idx > NRG_MASK) idx = NRG_2MASK - idx;
54   return index_to_pointer(idx, vector);
55 }
57 /* Linear */
59 static void nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
60 static void nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
61 static void nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
62 static void nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
63 static void nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb);
65 NRRenderer *
66 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
67                              const unsigned char *cv, 
68                              unsigned int spread, 
69                              const NRMatrix *gs2px,
70                              float x0, float y0,
71                              float x1, float y1)
72 {
73         NRMatrix n2gs, n2px, px2n;
75         lgr->renderer.render = nr_lgradient_render_block;
77         lgr->vector = cv;
78         lgr->spread = spread;
80         n2gs.c[0] = x1 - x0;
81         n2gs.c[1] = y1 - y0;
82         n2gs.c[2] = y1 - y0;
83         n2gs.c[3] = x0 - x1;
84         n2gs.c[4] = x0;
85         n2gs.c[5] = y0;
87         nr_matrix_multiply (&n2px, &n2gs, gs2px);
88         nr_matrix_invert (&px2n, &n2px);
90         lgr->x0 = n2px.c[4] - 0.5;
91         lgr->y0 = n2px.c[5] - 0.5;
92         lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
93         lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
95         return (NRRenderer *) lgr;
96 }
98 static void
99 nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
101         NRLGradientRenderer *lgr;
102         int width, height;
104         lgr = (NRLGradientRenderer *) r;
106         width = pb->area.x1 - pb->area.x0;
107         height = pb->area.y1 - pb->area.y0;
109 #ifdef NR_USE_GENERIC_RENDERER
110         nr_lgradient_render_generic (lgr, pb);
111 #else
112         if (pb->empty) {
113                 switch (pb->mode) {
114                 case NR_PIXBLOCK_MODE_A8:
115                         nr_lgradient_render_generic (lgr, pb);
116                         break;
117                 case NR_PIXBLOCK_MODE_R8G8B8:
118                         nr_lgradient_render_generic (lgr, pb);
119                         break;
120                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
121                         nr_lgradient_render_R8G8B8A8N_EMPTY (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
122                         break;
123                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
124                         nr_lgradient_render_generic (lgr, pb);
125                         break;
126                 default:
127                         break;
128                 }
129         } else {
130                 switch (pb->mode) {
131                 case NR_PIXBLOCK_MODE_A8:
132                         nr_lgradient_render_generic (lgr, pb);
133                         break;
134                 case NR_PIXBLOCK_MODE_R8G8B8:
135                         nr_lgradient_render_R8G8B8 (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
136                         break;
137                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
138                         nr_lgradient_render_R8G8B8A8N (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
139                         break;
140                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
141                         nr_lgradient_render_generic (lgr, pb);
142                         break;
143                 default:
144                         break;
145                 }
146         }
147 #endif
150 static void
151 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
153         int x, y;
154         double pos;
156         for (y = 0; y < height; y++) {
157                 const unsigned char *s;
158                 unsigned char *d;
159                 d = px + y * rs;
160                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
161                 if (lgr->spread == NR_GRADIENT_SPREAD_PAD) {
162                         for (x = 0; x < width; x++) {
163                                 s = r_to_pointer_pad(pos, lgr->vector);
164                                 d[0] = s[0];
165                                 d[1] = s[1];
166                                 d[2] = s[2];
167                                 d[3] = s[3];
168                                 d += 4;
169                                 pos += lgr->dx;
170                         }
171                 } else if (lgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
172                         for (x = 0; x < width; x++) {
173                                 s = r_to_pointer_reflect(pos, lgr->vector);
174                                 d[0] = s[0];
175                                 d[1] = s[1];
176                                 d[2] = s[2];
177                                 d[3] = s[3];
178                                 d += 4;
179                                 pos += lgr->dx;
180                         }
181                 } else {
182                         for (x = 0; x < width; x++) {
183                                 s = r_to_pointer_repeat(pos, lgr->vector);
184                                 d[0] = s[0];
185                                 d[1] = s[1];
186                                 d[2] = s[2];
187                                 d[3] = s[3];
188                                 d += 4;
189                                 pos += lgr->dx;
190                         }
191                 }
192         }
195 static void
196 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
198         int x, y;
199         unsigned char *d;
200         double pos;
202         for (y = 0; y < height; y++) {
203                 d = px + y * rs;
204                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
205                 for (x = 0; x < width; x++) {
206                         unsigned int ca;
207                         const unsigned char *s;
208                         switch (lgr->spread) {
209                         case NR_GRADIENT_SPREAD_PAD:
210                                 s = r_to_pointer_pad(pos, lgr->vector);
211                                 break;
212                         case NR_GRADIENT_SPREAD_REFLECT:
213                                 s = r_to_pointer_reflect(pos, lgr->vector);
214                                 break;
215                         case NR_GRADIENT_SPREAD_REPEAT:
216                                 s = r_to_pointer_repeat(pos, lgr->vector);
217                                 break;
218                         default:
219                                 s = lgr->vector;
220                                 break;
221                         }
222                         if (s[3] == 255) {
223                                 d[0] = s[0];
224                                 d[1] = s[1];
225                                 d[2] = s[2];
226                                 d[3] = 255;
227                         } else if (s[3] != 0) {
228                                 ca = NR_COMPOSEA_112(s[3],d[3]);
229                                 d[0] = NR_COMPOSENNN_111121 (s[0], s[3], d[0], d[3], ca);
230                                 d[1] = NR_COMPOSENNN_111121 (s[1], s[3], d[1], d[3], ca);
231                                 d[2] = NR_COMPOSENNN_111121 (s[2], s[3], d[2], d[3], ca);
232                                 d[3] = NR_NORMALIZE_21(ca);
233                         }
234                         d += 4;
235                         pos += lgr->dx;
236                 }
237         }
240 static void
241 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
243         int x, y;
244         unsigned char *d;
245         double pos;
247         for (y = 0; y < height; y++) {
248                 d = px + y * rs;
249                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
250                 for (x = 0; x < width; x++) {
251                         const unsigned char *s;
252                         switch (lgr->spread) {
253                         case NR_GRADIENT_SPREAD_PAD:
254                                 s = r_to_pointer_reflect(pos, lgr->vector);
255                                 break;
256                         case NR_GRADIENT_SPREAD_REFLECT:
257                                 s = r_to_pointer_reflect(pos, lgr->vector);
258                                 break;
259                         case NR_GRADIENT_SPREAD_REPEAT:
260                                 s = r_to_pointer_repeat(pos, lgr->vector);
261                                 break;
262                         default:
263                                 s = lgr->vector;
264                                 break;
265                         }
266                         /* Full composition */
267                         d[0] = NR_COMPOSEN11_1111 (s[0], s[3], d[0]);
268                         d[1] = NR_COMPOSEN11_1111 (s[1], s[3], d[1]);
269                         d[2] = NR_COMPOSEN11_1111 (s[2], s[3], d[2]);
270                         d += 3;
271                         pos += lgr->dx;
272                 }
273         }
276 static void
277 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
279         int x, y;
280         unsigned char *d;
281         double pos;
282         int bpp;
283         NRPixBlock spb;
284         int x0, y0, width, height, rs;
286         x0 = pb->area.x0;
287         y0 = pb->area.y0;
288         width = pb->area.x1 - pb->area.x0;
289         height = pb->area.y1 - pb->area.y0;
290         rs = pb->rs;
292         nr_pixblock_setup_extern (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
293                                   (unsigned char *) lgr->vector,
294                                   4 * NR_GRADIENT_VECTOR_LENGTH,
295                                   0, 0);
296     spb.visible_area = pb->visible_area; 
297         bpp = (pb->mode == NR_PIXBLOCK_MODE_A8) ? 1 : (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
299         for (y = 0; y < height; y++) {
300                 d = NR_PIXBLOCK_PX (pb) + y * rs;
301                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
302                 for (x = 0; x < width; x++) {
303                         const unsigned char *s;
304                         switch (lgr->spread) {
305                         case NR_GRADIENT_SPREAD_PAD:
306                                 s = r_to_pointer_pad(pos, lgr->vector);
307                                 break;
308                         case NR_GRADIENT_SPREAD_REFLECT:
309                                 s = r_to_pointer_reflect(pos, lgr->vector);
310                                 break;
311                         case NR_GRADIENT_SPREAD_REPEAT:
312                                 s = r_to_pointer_repeat(pos, lgr->vector);
313                                 break;
314                         default:
315                                 s = lgr->vector;
316                                 break;
317                         }
318                         nr_compose_pixblock_pixblock_pixel (pb, d, &spb, s);
319                         d += bpp;
320                         pos += lgr->dx;
321                 }
322         }
324         nr_pixblock_release (&spb);
327 /* Radial */
329 static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
330 static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
331 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
332 static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
333 static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
335 NRRenderer *
336 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
337                             unsigned char const *cv,
338                             unsigned spread,
339                             NRMatrix const *gs2px,
340                             float cx, float cy,
341                             float fx, float fy,
342                             float r)
344     rgr->vector = cv;
345     rgr->spread = spread;
347     if (r < NR_EPSILON) {
348         rgr->renderer.render = nr_rgradient_render_block_end;
349     } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
350                NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
351         rgr->renderer.render = nr_rgradient_render_block_symmetric;
353         nr_matrix_invert(&rgr->px2gs, gs2px);
354         rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
355         rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
356         rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
357         rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
358         rgr->px2gs.c[4] -= cx;
359         rgr->px2gs.c[5] -= cy;
360         rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
361         rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
363         rgr->cx = 0.0;
364         rgr->cy = 0.0;
365         rgr->fx = rgr->cx;
366         rgr->fy = rgr->cy;
367         rgr->r = 1.0;
368     } else {
369         rgr->renderer.render = nr_rgradient_render_block_optimized;
371         NR::Coord const df = hypot(fx - cx, fy - cy);
372         if (df >= r) {
373             fx = cx + (fx - cx ) * r / (float) df;
374             fy = cy + (fy - cy ) * r / (float) df;
375         }
377         NRMatrix n2gs;
378         n2gs.c[0] = cx - fx;
379         n2gs.c[1] = cy - fy;
380         n2gs.c[2] = cy - fy;
381         n2gs.c[3] = fx - cx;
382         n2gs.c[4] = fx;
383         n2gs.c[5] = fy;
385         NRMatrix n2px;
386         nr_matrix_multiply(&n2px, &n2gs, gs2px);
387         nr_matrix_invert(&rgr->px2gs, &n2px);
389         rgr->cx = 1.0;
390         rgr->cy = 0.0;
391         rgr->fx = 0.0;
392         rgr->fy = 0.0;
393         rgr->r = r / (float) hypot(fx - cx, fy - cy);
394         rgr->C = 1.0F - rgr->r * rgr->r;
395         /* INVARIANT: C < 0 */
396         rgr->C = MIN(rgr->C, -NR_EPSILON);
397     }
399     return (NRRenderer *) rgr;
402 static void
403 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
405     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
406     nr_rgradient_render_generic_symmetric(rgr, pb);
409 static void
410 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
412     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
413     nr_rgradient_render_generic_optimized(rgr, pb);
416 static void
417 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
419     unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
421     nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
424 /*
425  * The archetype is following
426  *
427  * gx gy - pixel coordinates
428  * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
429  *
430  * (1)  (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
431  * (2)  (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
432  *
433  * (3)   Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
434  * (4)  (gy - fy) / (gx - fx) = D
435  * (5)   Py = D * Px - D * fx + fy
436  *
437  * (6)   D * fx - fy + cy = N
438  * (7)   Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
439  * (8)  (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
440  *
441  * (9)   A = D * D + 1
442  * (10)  B = -2 * (cx + D * N)
443  * (11)  C = cx * cx + N * N - r * r
444  *
445  * (12)  Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
446  */
448 static void
449 nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
451     NR::Coord const dx = rgr->px2gs.c[0];
452     NR::Coord const dy = rgr->px2gs.c[1];
454     if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
455         for (int y = pb->area.y0; y < pb->area.y1; y++) {
456             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
457             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
458             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
459             for (int x = pb->area.x0; x < pb->area.x1; x++) {
460                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
461                 unsigned char const *s;
462                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
463                     s = r_to_pointer_reflect(pos, rgr->vector);
464                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
465                     s = r_to_pointer_repeat(pos, rgr->vector);
466                 } else {
467                     s = r_to_pointer_pad(pos, rgr->vector);
468                 }
469                 d[0] = NR_COMPOSENPP_1111(s[0], s[3], d[0]);
470                 d[1] = NR_COMPOSENPP_1111(s[1], s[3], d[1]);
471                 d[2] = NR_COMPOSENPP_1111(s[2], s[3], d[2]);
472                 d[3] = NR_COMPOSEA_111(s[3], d[3]);
473                 d += 4;
474                 gx += dx;
475                 gy += dy;
476             }
477         }
478     } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
479         for (int y = pb->area.y0; y < pb->area.y1; y++) {
480             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
481             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
482             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
483             for (int x = pb->area.x0; x < pb->area.x1; x++) {
484                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
485                 unsigned char const *s;
486                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
487                     s = r_to_pointer_reflect(pos, rgr->vector);
488                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
489                     s = r_to_pointer_repeat(pos, rgr->vector);
490                 } else {
491                     s = r_to_pointer_pad(pos, rgr->vector);
492                 }
493                 if (s[3] == 255) {
494                     d[0] = s[0];
495                     d[1] = s[1];
496                     d[2] = s[2];
497                     d[3] = 255;
498                 } else if (s[3] != 0) {
499                     unsigned ca = NR_COMPOSEA_112(s[3], d[3]);
500                     d[0] = NR_COMPOSENNN_111121(s[0], s[3], d[0], d[3], ca);
501                     d[1] = NR_COMPOSENNN_111121(s[1], s[3], d[1], d[3], ca);
502                     d[2] = NR_COMPOSENNN_111121(s[2], s[3], d[2], d[3], ca);
503                     d[3] = NR_NORMALIZE_21(ca);
504                 }
505                 d += 4;
506                 gx += dx;
507                 gy += dy;
508             }
509         }
510     } else {
511         NRPixBlock spb;
512         nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
513                                  (unsigned char *) rgr->vector,
514                                  4 * NR_GRADIENT_VECTOR_LENGTH,
515                                  0, 0);
516         int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
517                           ? 1
518                           : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
519                           ? 3
520                           : 4 );
522         for (int y = pb->area.y0; y < pb->area.y1; y++) {
523             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
524             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
525             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
526             for (int x = pb->area.x0; x < pb->area.x1; x++) {
527                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
528                 unsigned char const *s;
529                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
530                     s = r_to_pointer_reflect(pos, rgr->vector);
531                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
532                     s = r_to_pointer_repeat(pos, rgr->vector);
533                 } else {
534                     s = r_to_pointer_pad(pos, rgr->vector);
535                 }
536                 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
537                 d += bpp;
538                 gx += dx;
539                 gy += dy;
540             }
541         }
543         nr_pixblock_release(&spb);
544     }
547 static void
548 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
550     int const x0 = pb->area.x0;
551     int const y0 = pb->area.y0;
552     int const x1 = pb->area.x1;
553     int const y1 = pb->area.y1;
554     int const rs = pb->rs;
556     NRPixBlock spb;
557     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
558                              (unsigned char *) rgr->vector,
559                              4 * NR_GRADIENT_VECTOR_LENGTH,
560                              0, 0);
561     int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
562                       ? 1
563                       : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
564                       ? 3
565                       : 4 );
567     for (int y = y0; y < y1; y++) {
568         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
569         NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
570         NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
571         NR::Coord const dx = rgr->px2gs.c[0];
572         NR::Coord const dy = rgr->px2gs.c[1];
573         for (int x = x0; x < x1; x++) {
574             NR::Coord const gx2 = gx * gx;
575             NR::Coord const gxy2 = gx2 + gy * gy;
576             NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
577             /* INVARIANT: qgx2_4 >= 0.0 */
578             /* qgx2_4 = MAX(qgx2_4, 0.0); */
579             NR::Coord const pxgx = gx + sqrt(qgx2_4);
580             /* We can safely divide by 0 here */
581             /* If we are sure pxgx cannot be -0 */
582             NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
584             unsigned char const *s;
585             if (pos < (1U << 31)) {
586                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
587                     s = r_to_pointer_reflect(pos, rgr->vector);
588                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
589                     s = r_to_pointer_repeat(pos, rgr->vector);
590                 } else {
591                     s = r_to_pointer_pad(pos, rgr->vector);
592                 }
593             } else {
594                 s = index_to_pointer(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
595             }
596             nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
597             d += bpp;
599             gx += dx;
600             gy += dy;
601         }
602     }
604     nr_pixblock_release(&spb);
608 /*
609   Local Variables:
610   mode:c++
611   c-file-style:"stroustrup"
612   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
613   indent-tabs-mode:nil
614   fill-column:99
615   End:
616 */
617 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :