Code

fix typo
[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>
23 #include <glib/gtypes.h>
24 #include <stdio.h>
26 /* Common */
28 #define noNR_USE_GENERIC_RENDERER
30 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
31 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
33 static guint32 mls_state=0xa37e5375;
35 inline NR::Coord noise() {
36   guint32 lsb = mls_state & 1;
37   guint32 msb = ( lsb << 31 ) ^ ( ( mls_state & 2) << 30 );
38   mls_state = ( mls_state >> 1 ) | msb;
39   return lsb * 1.75;
40 }
42 inline unsigned char const *index_to_pointer(int idx,
43                                              unsigned char const *vector)
44 {
45   return vector + 4 * idx;
46 }
48 inline unsigned char const *r_to_pointer_pad(NR::Coord r,
49                                              unsigned char const *vector)
50 {
51   r += noise();
52   return index_to_pointer((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
53 }
55 inline unsigned char const *r_to_pointer_repeat(NR::Coord r,
56                                                 unsigned char const *vector)
57 {
58   r += noise();
59   return index_to_pointer((int)((long long)r & NRG_MASK), vector);
60 }
62 inline unsigned char const *r_to_pointer_reflect(NR::Coord r,
63                                                  unsigned char const *vector)
64 {
65   r += noise();
66   int idx = (int) ((long long)r & NRG_2MASK);
67   if (idx > NRG_MASK) idx = NRG_2MASK - idx;
68   return index_to_pointer(idx, vector);
69 }
71 /* Linear */
73 static void nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
74 static void nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
75 static void nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
76 static void nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs);
77 static void nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb);
79 NRRenderer *
80 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
81                              const unsigned char *cv, 
82                              unsigned int spread, 
83                              const NRMatrix *gs2px,
84                              float x0, float y0,
85                              float x1, float y1)
86 {
87         NRMatrix n2gs, n2px, px2n;
89         lgr->renderer.render = nr_lgradient_render_block;
91         lgr->vector = cv;
92         lgr->spread = spread;
94         n2gs.c[0] = x1 - x0;
95         n2gs.c[1] = y1 - y0;
96         n2gs.c[2] = y1 - y0;
97         n2gs.c[3] = x0 - x1;
98         n2gs.c[4] = x0;
99         n2gs.c[5] = y0;
101         nr_matrix_multiply (&n2px, &n2gs, gs2px);
102         nr_matrix_invert (&px2n, &n2px);
104         lgr->x0 = n2px.c[4] - 0.5;
105         lgr->y0 = n2px.c[5] - 0.5;
106         lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
107         lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
109         return (NRRenderer *) lgr;
112 static void
113 nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
115         NRLGradientRenderer *lgr;
116         int width, height;
118         lgr = (NRLGradientRenderer *) r;
120         width = pb->area.x1 - pb->area.x0;
121         height = pb->area.y1 - pb->area.y0;
123 #ifdef NR_USE_GENERIC_RENDERER
124         nr_lgradient_render_generic (lgr, pb);
125 #else
126         if (pb->empty) {
127                 switch (pb->mode) {
128                 case NR_PIXBLOCK_MODE_A8:
129                         nr_lgradient_render_generic (lgr, pb);
130                         break;
131                 case NR_PIXBLOCK_MODE_R8G8B8:
132                         nr_lgradient_render_generic (lgr, pb);
133                         break;
134                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
135                         nr_lgradient_render_R8G8B8A8N_EMPTY (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
136                         break;
137                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
138                         nr_lgradient_render_generic (lgr, pb);
139                         break;
140                 default:
141                         break;
142                 }
143         } else {
144                 switch (pb->mode) {
145                 case NR_PIXBLOCK_MODE_A8:
146                         nr_lgradient_render_generic (lgr, pb);
147                         break;
148                 case NR_PIXBLOCK_MODE_R8G8B8:
149                         nr_lgradient_render_R8G8B8 (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
150                         break;
151                 case NR_PIXBLOCK_MODE_R8G8B8A8N:
152                         nr_lgradient_render_R8G8B8A8N (lgr, NR_PIXBLOCK_PX (pb), pb->area.x0, pb->area.y0, width, height, pb->rs);
153                         break;
154                 case NR_PIXBLOCK_MODE_R8G8B8A8P:
155                         nr_lgradient_render_generic (lgr, pb);
156                         break;
157                 default:
158                         break;
159                 }
160         }
161 #endif
164 static void
165 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
167         int x, y;
168         double pos;
170         for (y = 0; y < height; y++) {
171                 const unsigned char *s;
172                 unsigned char *d;
173                 d = px + y * rs;
174                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
175                 if (lgr->spread == NR_GRADIENT_SPREAD_PAD) {
176                         for (x = 0; x < width; x++) {
177                                 s = r_to_pointer_pad(pos, lgr->vector);
178                                 d[0] = s[0];
179                                 d[1] = s[1];
180                                 d[2] = s[2];
181                                 d[3] = s[3];
182                                 d += 4;
183                                 pos += lgr->dx;
184                         }
185                 } else if (lgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
186                         for (x = 0; x < width; x++) {
187                                 s = r_to_pointer_reflect(pos, lgr->vector);
188                                 d[0] = s[0];
189                                 d[1] = s[1];
190                                 d[2] = s[2];
191                                 d[3] = s[3];
192                                 d += 4;
193                                 pos += lgr->dx;
194                         }
195                 } else {
196                         for (x = 0; x < width; x++) {
197                                 s = r_to_pointer_repeat(pos, lgr->vector);
198                                 d[0] = s[0];
199                                 d[1] = s[1];
200                                 d[2] = s[2];
201                                 d[3] = s[3];
202                                 d += 4;
203                                 pos += lgr->dx;
204                         }
205                 }
206         }
209 static void
210 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
212         int x, y;
213         unsigned char *d;
214         double pos;
216         for (y = 0; y < height; y++) {
217                 d = px + y * rs;
218                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
219                 for (x = 0; x < width; x++) {
220                         unsigned int ca;
221                         const unsigned char *s;
222                         switch (lgr->spread) {
223                         case NR_GRADIENT_SPREAD_PAD:
224                                 s = r_to_pointer_pad(pos, lgr->vector);
225                                 break;
226                         case NR_GRADIENT_SPREAD_REFLECT:
227                                 s = r_to_pointer_reflect(pos, lgr->vector);
228                                 break;
229                         case NR_GRADIENT_SPREAD_REPEAT:
230                                 s = r_to_pointer_repeat(pos, lgr->vector);
231                                 break;
232                         default:
233                                 s = lgr->vector;
234                                 break;
235                         }
236                         if (s[3] == 255) {
237                                 d[0] = s[0];
238                                 d[1] = s[1];
239                                 d[2] = s[2];
240                                 d[3] = 255;
241                         } else if (s[3] != 0) {
242                                 ca = NR_COMPOSEA_112(s[3],d[3]);
243                                 d[0] = NR_COMPOSENNN_111121 (s[0], s[3], d[0], d[3], ca);
244                                 d[1] = NR_COMPOSENNN_111121 (s[1], s[3], d[1], d[3], ca);
245                                 d[2] = NR_COMPOSENNN_111121 (s[2], s[3], d[2], d[3], ca);
246                                 d[3] = NR_NORMALIZE_21(ca);
247                         }
248                         d += 4;
249                         pos += lgr->dx;
250                 }
251         }
254 static void
255 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
257         int x, y;
258         unsigned char *d;
259         double pos;
261         for (y = 0; y < height; y++) {
262                 d = px + y * rs;
263                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
264                 for (x = 0; x < width; x++) {
265                         const unsigned char *s;
266                         switch (lgr->spread) {
267                         case NR_GRADIENT_SPREAD_PAD:
268                                 s = r_to_pointer_pad(pos, lgr->vector);
269                                 break;
270                         case NR_GRADIENT_SPREAD_REFLECT:
271                                 s = r_to_pointer_reflect(pos, lgr->vector);
272                                 break;
273                         case NR_GRADIENT_SPREAD_REPEAT:
274                                 s = r_to_pointer_repeat(pos, lgr->vector);
275                                 break;
276                         default:
277                                 s = lgr->vector;
278                                 break;
279                         }
280                         /* Full composition */
281                         d[0] = NR_COMPOSEN11_1111 (s[0], s[3], d[0]);
282                         d[1] = NR_COMPOSEN11_1111 (s[1], s[3], d[1]);
283                         d[2] = NR_COMPOSEN11_1111 (s[2], s[3], d[2]);
284                         d += 3;
285                         pos += lgr->dx;
286                 }
287         }
290 static void
291 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
293         int x, y;
294         unsigned char *d;
295         double pos;
296         int bpp;
297         NRPixBlock spb;
298         int x0, y0, width, height, rs;
300         x0 = pb->area.x0;
301         y0 = pb->area.y0;
302         width = pb->area.x1 - pb->area.x0;
303         height = pb->area.y1 - pb->area.y0;
304         rs = pb->rs;
306         nr_pixblock_setup_extern (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
307                                   (unsigned char *) lgr->vector,
308                                   4 * NR_GRADIENT_VECTOR_LENGTH,
309                                   0, 0);
310     spb.visible_area = pb->visible_area; 
311         bpp = (pb->mode == NR_PIXBLOCK_MODE_A8) ? 1 : (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
313         for (y = 0; y < height; y++) {
314                 d = NR_PIXBLOCK_PX (pb) + y * rs;
315                 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
316                 for (x = 0; x < width; x++) {
317                         const unsigned char *s;
318                         switch (lgr->spread) {
319                         case NR_GRADIENT_SPREAD_PAD:
320                                 s = r_to_pointer_pad(pos, lgr->vector);
321                                 break;
322                         case NR_GRADIENT_SPREAD_REFLECT:
323                                 s = r_to_pointer_reflect(pos, lgr->vector);
324                                 break;
325                         case NR_GRADIENT_SPREAD_REPEAT:
326                                 s = r_to_pointer_repeat(pos, lgr->vector);
327                                 break;
328                         default:
329                                 s = lgr->vector;
330                                 break;
331                         }
332                         nr_compose_pixblock_pixblock_pixel (pb, d, &spb, s);
333                         d += bpp;
334                         pos += lgr->dx;
335                 }
336         }
338         nr_pixblock_release (&spb);
341 /* Radial */
343 static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
344 static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
345 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
346 static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
347 static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
349 NRRenderer *
350 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
351                             unsigned char const *cv,
352                             unsigned spread,
353                             NRMatrix const *gs2px,
354                             float cx, float cy,
355                             float fx, float fy,
356                             float r)
358     rgr->vector = cv;
359     rgr->spread = spread;
361     if (r < NR_EPSILON) {
362         rgr->renderer.render = nr_rgradient_render_block_end;
363     } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
364                NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
365         rgr->renderer.render = nr_rgradient_render_block_symmetric;
367         nr_matrix_invert(&rgr->px2gs, gs2px);
368         rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
369         rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
370         rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
371         rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
372         rgr->px2gs.c[4] -= cx;
373         rgr->px2gs.c[5] -= cy;
374         rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
375         rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
377         rgr->cx = 0.0;
378         rgr->cy = 0.0;
379         rgr->fx = rgr->cx;
380         rgr->fy = rgr->cy;
381         rgr->r = 1.0;
382     } else {
383         rgr->renderer.render = nr_rgradient_render_block_optimized;
385         NR::Coord const df = hypot(fx - cx, fy - cy);
386         if (df >= r) {
387             fx = cx + (fx - cx ) * r / (float) df;
388             fy = cy + (fy - cy ) * r / (float) df;
389         }
391         NRMatrix n2gs;
392         n2gs.c[0] = cx - fx;
393         n2gs.c[1] = cy - fy;
394         n2gs.c[2] = cy - fy;
395         n2gs.c[3] = fx - cx;
396         n2gs.c[4] = fx;
397         n2gs.c[5] = fy;
399         NRMatrix n2px;
400         nr_matrix_multiply(&n2px, &n2gs, gs2px);
401         nr_matrix_invert(&rgr->px2gs, &n2px);
403         rgr->cx = 1.0;
404         rgr->cy = 0.0;
405         rgr->fx = 0.0;
406         rgr->fy = 0.0;
407         rgr->r = r / (float) hypot(fx - cx, fy - cy);
408         rgr->C = 1.0F - rgr->r * rgr->r;
409         /* INVARIANT: C < 0 */
410         rgr->C = MIN(rgr->C, -NR_EPSILON);
411     }
413     return (NRRenderer *) rgr;
416 static void
417 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
419     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
420     nr_rgradient_render_generic_symmetric(rgr, pb);
423 static void
424 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
426     NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
427     nr_rgradient_render_generic_optimized(rgr, pb);
430 static void
431 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
433     unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
435     nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
438 /*
439  * The archetype is following
440  *
441  * gx gy - pixel coordinates
442  * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
443  *
444  * (1)  (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
445  * (2)  (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
446  *
447  * (3)   Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
448  * (4)  (gy - fy) / (gx - fx) = D
449  * (5)   Py = D * Px - D * fx + fy
450  *
451  * (6)   D * fx - fy + cy = N
452  * (7)   Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
453  * (8)  (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
454  *
455  * (9)   A = D * D + 1
456  * (10)  B = -2 * (cx + D * N)
457  * (11)  C = cx * cx + N * N - r * r
458  *
459  * (12)  Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
460  */
462 static void
463 nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
465     NR::Coord const dx = rgr->px2gs.c[0];
466     NR::Coord const dy = rgr->px2gs.c[1];
468     if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
469         for (int y = pb->area.y0; y < pb->area.y1; y++) {
470             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
471             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
472             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
473             for (int x = pb->area.x0; x < pb->area.x1; x++) {
474                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
475                 unsigned char const *s;
476                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
477                     s = r_to_pointer_reflect(pos, rgr->vector);
478                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
479                     s = r_to_pointer_repeat(pos, rgr->vector);
480                 } else {
481                     s = r_to_pointer_pad(pos, rgr->vector);
482                 }
483                 d[0] = NR_COMPOSENPP_1111(s[0], s[3], d[0]);
484                 d[1] = NR_COMPOSENPP_1111(s[1], s[3], d[1]);
485                 d[2] = NR_COMPOSENPP_1111(s[2], s[3], d[2]);
486                 d[3] = NR_COMPOSEA_111(s[3], d[3]);
487                 d += 4;
488                 gx += dx;
489                 gy += dy;
490             }
491         }
492     } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
493         for (int y = pb->area.y0; y < pb->area.y1; y++) {
494             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
495             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
496             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
497             for (int x = pb->area.x0; x < pb->area.x1; x++) {
498                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
499                 unsigned char const *s;
500                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
501                     s = r_to_pointer_reflect(pos, rgr->vector);
502                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
503                     s = r_to_pointer_repeat(pos, rgr->vector);
504                 } else {
505                     s = r_to_pointer_pad(pos, rgr->vector);
506                 }
507                 if (s[3] == 255) {
508                     d[0] = s[0];
509                     d[1] = s[1];
510                     d[2] = s[2];
511                     d[3] = 255;
512                 } else if (s[3] != 0) {
513                     unsigned ca = NR_COMPOSEA_112(s[3], d[3]);
514                     d[0] = NR_COMPOSENNN_111121(s[0], s[3], d[0], d[3], ca);
515                     d[1] = NR_COMPOSENNN_111121(s[1], s[3], d[1], d[3], ca);
516                     d[2] = NR_COMPOSENNN_111121(s[2], s[3], d[2], d[3], ca);
517                     d[3] = NR_NORMALIZE_21(ca);
518                 }
519                 d += 4;
520                 gx += dx;
521                 gy += dy;
522             }
523         }
524     } else {
525         NRPixBlock spb;
526         nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
527                                  (unsigned char *) rgr->vector,
528                                  4 * NR_GRADIENT_VECTOR_LENGTH,
529                                  0, 0);
530         int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
531                           ? 1
532                           : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
533                           ? 3
534                           : 4 );
536         for (int y = pb->area.y0; y < pb->area.y1; y++) {
537             unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
538             NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
539             NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
540             for (int x = pb->area.x0; x < pb->area.x1; x++) {
541                 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
542                 unsigned char const *s;
543                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
544                     s = r_to_pointer_reflect(pos, rgr->vector);
545                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
546                     s = r_to_pointer_repeat(pos, rgr->vector);
547                 } else {
548                     s = r_to_pointer_pad(pos, rgr->vector);
549                 }
550                 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
551                 d += bpp;
552                 gx += dx;
553                 gy += dy;
554             }
555         }
557         nr_pixblock_release(&spb);
558     }
561 static void
562 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
564     int const x0 = pb->area.x0;
565     int const y0 = pb->area.y0;
566     int const x1 = pb->area.x1;
567     int const y1 = pb->area.y1;
568     int const rs = pb->rs;
570     NRPixBlock spb;
571     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
572                              (unsigned char *) rgr->vector,
573                              4 * NR_GRADIENT_VECTOR_LENGTH,
574                              0, 0);
575     int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
576                       ? 1
577                       : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
578                       ? 3
579                       : 4 );
581     for (int y = y0; y < y1; y++) {
582         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
583         NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
584         NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
585         NR::Coord const dx = rgr->px2gs.c[0];
586         NR::Coord const dy = rgr->px2gs.c[1];
587         for (int x = x0; x < x1; x++) {
588             NR::Coord const gx2 = gx * gx;
589             NR::Coord const gxy2 = gx2 + gy * gy;
590             NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
591             /* INVARIANT: qgx2_4 >= 0.0 */
592             /* qgx2_4 = MAX(qgx2_4, 0.0); */
593             NR::Coord const pxgx = gx + sqrt(qgx2_4);
594             /* We can safely divide by 0 here */
595             /* If we are sure pxgx cannot be -0 */
596             NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
598             unsigned char const *s;
599             if (pos < (1U << 31)) {
600                 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
601                     s = r_to_pointer_reflect(pos, rgr->vector);
602                 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
603                     s = r_to_pointer_repeat(pos, rgr->vector);
604                 } else {
605                     s = r_to_pointer_pad(pos, rgr->vector);
606                 }
607             } else {
608                 s = index_to_pointer(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
609             }
610             nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
611             d += bpp;
613             gx += dx;
614             gy += dy;
615         }
616     }
618     nr_pixblock_release(&spb);
622 /*
623   Local Variables:
624   mode:c++
625   c-file-style:"stroustrup"
626   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
627   indent-tabs-mode:nil
628   fill-column:99
629   End:
630 */
631 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :