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)
100 {
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
148 }
150 static void
151 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
152 {
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 }
193 }
195 static void
196 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
197 {
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 }
238 }
240 static void
241 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
242 {
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 }
274 }
276 static void
277 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
278 {
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);
325 }
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)
343 {
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;
400 }
402 static void
403 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
404 {
405 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
406 nr_rgradient_render_generic_symmetric(rgr, pb);
407 }
409 static void
410 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
411 {
412 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
413 nr_rgradient_render_generic_optimized(rgr, pb);
414 }
416 static void
417 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
418 {
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]);
422 }
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)
450 {
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 }
545 }
547 static void
548 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
549 {
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);
605 }
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 :