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;
110 }
112 static void
113 nr_lgradient_render_block (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
114 {
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
162 }
164 static void
165 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
166 {
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 }
207 }
209 static void
210 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
211 {
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 }
252 }
254 static void
255 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
256 {
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 }
288 }
290 static void
291 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
292 {
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);
339 }
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)
357 {
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;
414 }
416 static void
417 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
418 {
419 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
420 nr_rgradient_render_generic_symmetric(rgr, pb);
421 }
423 static void
424 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
425 {
426 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
427 nr_rgradient_render_generic_optimized(rgr, pb);
428 }
430 static void
431 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
432 {
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]);
436 }
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)
464 {
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 }
559 }
561 static void
562 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
563 {
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);
619 }
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 :