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
122 }
124 static void
125 nr_lgradient_render_R8G8B8A8N_EMPTY (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
126 {
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 }
172 }
174 static void
175 nr_lgradient_render_R8G8B8A8N (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
176 {
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 }
221 }
223 static void
224 nr_lgradient_render_R8G8B8 (NRLGradientRenderer *lgr, unsigned char *px, int x0, int y0, int width, int height, int rs)
225 {
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 }
260 }
262 static void
263 nr_lgradient_render_generic (NRLGradientRenderer *lgr, NRPixBlock *pb)
264 {
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);
314 }
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)
332 {
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;
389 }
391 static void
392 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
393 {
394 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
395 nr_rgradient_render_generic_symmetric(rgr, pb);
396 }
398 static void
399 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
400 {
401 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
402 nr_rgradient_render_generic_optimized(rgr, pb);
403 }
405 static void
406 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
407 {
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]);
411 }
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)
439 {
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 }
540 }
542 static void
543 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
544 {
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);
601 }
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 :