1 #define __NR_GRADIENT_C__
3 /*
4 * Pixel buffer rendering library
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 *...Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
10 *
11 * Copyright (C) 2009 Jasper van de Gronde
12 * Copyright (C) 2007 MenTaLguY
13 * Copyright (C) 2001-2002 Lauris Kaplinski
14 * Copyright (C) 2001-2002 Ximian, Inc.
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 /*
20 * Derived in part from public domain code by Lauris Kaplinski
21 */
23 #include <cstring>
24 #include <libnr/nr-pixops.h>
25 #include <libnr/nr-pixblock-pixel.h>
26 #include <libnr/nr-blit.h>
27 #include <libnr/nr-gradient.h>
28 #include <libnr/nr-matrix-ops.h>
29 #include <glib/gtypes.h>
30 #include <stdio.h>
32 /* Common */
34 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
35 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
37 namespace {
38 inline unsigned char const *vector_index(int idx,
39 unsigned char const *vector)
40 {
41 return vector + 4 * idx;
42 }
44 template <NRGradientSpread spread> struct Spread;
46 template <>
47 struct Spread<NR_GRADIENT_SPREAD_PAD> {
48 static double index_at(NR::Coord r, double const unity = 1.0) {
49 return r<0.0?0.0:r>unity?unity:r;
50 }
51 static unsigned char const *color_at(NR::Coord r,
52 unsigned char const *vector)
53 {
54 return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector);
55 //return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
56 }
57 };
59 template <>
60 struct Spread<NR_GRADIENT_SPREAD_REPEAT> {
61 static double index_at(NR::Coord r, double const unity = 1.0) {
62 return r<0.0?(unity+fmod(r,unity)):fmod(r,unity);
63 }
64 static unsigned char const *color_at(NR::Coord r,
65 unsigned char const *vector)
66 {
67 return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector);
68 //return vector_index((int)((long long)r & NRG_MASK), vector);
69 }
70 };
72 template <>
73 struct Spread<NR_GRADIENT_SPREAD_REFLECT> {
74 static double index_at(NR::Coord r, double const unity = 1.0) {
75 r = r<0.0?(2*unity+fmod(r,2*unity)):fmod(r,2*unity);
76 if (r>unity) r=2*unity-r;
77 return r;
78 }
79 static unsigned char const *color_at(NR::Coord r,
80 unsigned char const *vector)
81 {
82 return vector_index((int)(index_at(r, NR_GRADIENT_VECTOR_LENGTH - 1)+.5), vector);
83 //int idx = (int) ((long long)r & NRG_2MASK);
84 //if (idx > NRG_MASK) idx = NRG_2MASK - idx;
85 //return vector_index(idx, vector);
86 }
87 };
89 template <NR_PIXBLOCK_MODE mode> struct ModeTraits;
91 template <>
92 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8N> {
93 static const unsigned bpp=4;
94 };
96 template <>
97 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8P> {
98 static const unsigned bpp=4;
99 };
101 template <>
102 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8> {
103 static const unsigned bpp=3;
104 };
106 template <>
107 struct ModeTraits<NR_PIXBLOCK_MODE_A8> {
108 static const unsigned bpp=1;
109 };
111 template <NR_PIXBLOCK_MODE mode, bool empty>
112 struct Compose {
113 static const unsigned bpp=ModeTraits<mode>::bpp;
114 static void compose(NRPixBlock *pb, unsigned char *dest,
115 NRPixBlock *spb, unsigned char const *src)
116 {
117 nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
118 }
119 };
121 template <>
122 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
123 static const unsigned bpp=4;
124 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
125 NRPixBlock */*spb*/, unsigned char const *src)
126 {
127 std::memcpy(dest, src, 4);
128 }
129 };
131 template <>
132 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> {
133 static const unsigned bpp=4;
134 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
135 NRPixBlock */*spb*/, unsigned char const *src)
136 {
137 dest[0] = NR_PREMUL_111(src[0], src[3]);
138 dest[1] = NR_PREMUL_111(src[1], src[3]);
139 dest[2] = NR_PREMUL_111(src[2], src[3]);
140 dest[3] = src[3];
141 }
142 };
144 template <>
145 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, true> {
146 static const unsigned bpp=3;
147 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
148 NRPixBlock */*spb*/, unsigned char const *src)
149 {
150 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], 255);
151 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], 255);
152 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], 255);
153 }
154 };
156 template <>
157 struct Compose<NR_PIXBLOCK_MODE_A8, true> {
158 static const unsigned bpp=1;
159 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
160 NRPixBlock */*spb*/, unsigned char const *src)
161 {
162 dest[0] = src[3];
163 }
164 };
166 template <>
167 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
168 static const unsigned bpp=4;
169 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
170 NRPixBlock */*spb*/, unsigned char const *src)
171 {
172 unsigned int ca;
173 ca = NR_COMPOSEA_112(src[3], dest[3]);
174 dest[0] = NR_COMPOSENNN_111121(src[0], src[3], dest[0], dest[3], ca);
175 dest[1] = NR_COMPOSENNN_111121(src[1], src[3], dest[1], dest[3], ca);
176 dest[2] = NR_COMPOSENNN_111121(src[2], src[3], dest[2], dest[3], ca);
177 dest[3] = NR_NORMALIZE_21(ca);
178 }
179 };
181 template <>
182 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
183 static const unsigned bpp=4;
184 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
185 NRPixBlock */*spb*/, unsigned char const *src)
186 {
187 dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
188 dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
189 dest[2] = NR_COMPOSENPP_1111(src[2], src[3], dest[2]);
190 dest[3] = NR_COMPOSEA_111(src[3], dest[3]);
191 }
192 };
194 template <>
195 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
196 static const unsigned bpp=3;
197 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
198 NRPixBlock */*spb*/, unsigned char const *src)
199 {
200 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
201 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
202 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], dest[2]);
203 }
204 };
206 template <typename Subtype, typename spread>
207 static void
208 render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
209 {
210 switch (pb->mode) {
211 case NR_PIXBLOCK_MODE_R8G8B8A8N:
212 if (pb->empty) {
213 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
214 Subtype::template render<compose, spread>(gr, pb);
215 } else {
216 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
217 Subtype::template render<compose, spread>(gr, pb);
218 }
219 break;
220 case NR_PIXBLOCK_MODE_R8G8B8A8P:
221 if (pb->empty) {
222 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> compose;
223 Subtype::template render<compose, spread>(gr, pb);
224 } else {
225 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
226 Subtype::template render<compose, spread>(gr, pb);
227 }
228 break;
229 case NR_PIXBLOCK_MODE_R8G8B8:
230 if (pb->empty) {
231 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, true> compose;
232 Subtype::template render<compose, spread>(gr, pb);
233 } else {
234 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
235 Subtype::template render<compose, spread>(gr, pb);
236 }
237 break;
238 case NR_PIXBLOCK_MODE_A8:
239 if (pb->empty) {
240 typedef Compose<NR_PIXBLOCK_MODE_A8, true> compose;
241 Subtype::template render<compose, spread>(gr, pb);
242 } else {
243 typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
244 Subtype::template render<compose, spread>(gr, pb);
245 }
246 break;
247 }
248 }
250 template <typename Subtype>
251 static void
252 render(NRRenderer *r, NRPixBlock *pb, NRPixBlock */*m*/)
253 {
254 NRGradientRenderer *gr = static_cast<NRGradientRenderer *>(r);
256 switch (gr->spread) {
257 case NR_GRADIENT_SPREAD_REPEAT:
258 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
259 break;
260 case NR_GRADIENT_SPREAD_REFLECT:
261 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
262 break;
263 case NR_GRADIENT_SPREAD_PAD:
264 default:
265 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
266 }
267 }
268 }
270 /* Linear */
272 namespace {
274 struct Linear {
275 template <typename compose, typename spread>
276 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
277 NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
279 int x, y;
280 unsigned char *d;
281 double pos;
282 NRPixBlock spb;
283 int x0, y0, width, height, rs;
285 x0 = pb->area.x0;
286 y0 = pb->area.y0;
287 width = pb->area.x1 - pb->area.x0;
288 height = pb->area.y1 - pb->area.y0;
289 rs = pb->rs;
291 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
292 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
293 (unsigned char *) lgr->vector,
294 4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
296 for (y = 0; y < height; y++) {
297 d = NR_PIXBLOCK_PX(pb) + y * rs;
298 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
299 for (x = 0; x < width; x++) {
300 unsigned char const *s=spread::color_at(pos, lgr->vector);
301 compose::compose(pb, d, &spb, s);
302 d += compose::bpp;
303 pos += lgr->dx;
304 }
305 }
307 nr_pixblock_release(&spb);
308 }
309 };
311 }
313 NRRenderer *
314 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
315 const unsigned char *cv,
316 unsigned int spread,
317 const NR::Matrix *gs2px,
318 float x0, float y0,
319 float x1, float y1)
320 {
321 NR::Matrix n2gs, n2px, px2n;
323 lgr->render = &render<Linear>;
325 lgr->vector = cv;
326 lgr->spread = spread;
328 n2gs[0] = x1 - x0;
329 n2gs[1] = y1 - y0;
330 n2gs[2] = y1 - y0;
331 n2gs[3] = x0 - x1;
332 n2gs[4] = x0;
333 n2gs[5] = y0;
335 n2px = n2gs * (*gs2px);
336 px2n = n2px.inverse();
338 lgr->x0 = n2px[4] - 0.5; // These -0.5 offsets make sure that the gradient is sampled in the MIDDLE of each pixel.
339 lgr->y0 = n2px[5] - 0.5;
340 lgr->dx = px2n[0] * (NR_GRADIENT_VECTOR_LENGTH-1);
341 lgr->dy = px2n[2] * (NR_GRADIENT_VECTOR_LENGTH-1);
343 return (NRRenderer *) lgr;
344 }
346 /* Radial */
348 /*
349 * The archetype is following
350 *
351 * gx gy - pixel coordinates
352 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
353 *
354 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
355 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
356 *
357 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
358 * (4) (gy - fy) / (gx - fx) = D
359 * (5) Py = D * Px - D * fx + fy
360 *
361 * (6) D * fx - fy + cy = N
362 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
363 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
364 *
365 * (9) A = D * D + 1
366 * (10) B = -2 * (cx + D * N)
367 * (11) C = cx * cx + N * N - r * r
368 *
369 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
370 */
372 namespace {
374 struct SymmetricRadial {
375 template <typename compose, typename spread>
376 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
377 {
378 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
380 NR::Coord const dx = rgr->px2gs[0];
381 NR::Coord const dy = rgr->px2gs[1];
383 NRPixBlock spb;
384 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
385 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
386 (unsigned char *) rgr->vector,
387 4 * NR_GRADIENT_VECTOR_LENGTH,
388 0, 0);
390 for (int y = pb->area.y0; y < pb->area.y1; y++) {
391 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
392 NR::Coord gx = rgr->px2gs[0] * pb->area.x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
393 NR::Coord gy = rgr->px2gs[1] * pb->area.x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
394 for (int x = pb->area.x0; x < pb->area.x1; x++) {
395 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
396 unsigned char const *s=spread::color_at(pos, rgr->vector);
397 compose::compose(pb, d, &spb, s);
398 d += compose::bpp;
399 gx += dx;
400 gy += dy;
401 }
402 }
404 nr_pixblock_release(&spb);
405 }
406 };
408 struct Radial {
409 template <typename compose, typename spread>
410 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
411 {
412 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
413 int const x0 = pb->area.x0;
414 int const y0 = pb->area.y0;
415 int const x1 = pb->area.x1;
416 int const y1 = pb->area.y1;
417 int const rs = pb->rs;
419 NRPixBlock spb;
420 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
421 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
422 (unsigned char *) rgr->vector,
423 4 * NR_GRADIENT_VECTOR_LENGTH,
424 0, 0);
426 for (int y = y0; y < y1; y++) {
427 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
428 NR::Coord gx = rgr->px2gs[0] * x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
429 NR::Coord gy = rgr->px2gs[1] * x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
430 NR::Coord const dx = rgr->px2gs[0];
431 NR::Coord const dy = rgr->px2gs[1];
432 for (int x = x0; x < x1; x++) {
433 NR::Coord const gx2 = gx * gx;
434 NR::Coord const gxy2 = gx2 + gy * gy;
435 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
436 /* INVARIANT: qgx2_4 >= 0.0 */
437 /* qgx2_4 = MAX(qgx2_4, 0.0); */
438 NR::Coord const pxgx = gx + sqrt(qgx2_4);
439 /* We can safely divide by 0 here */
440 /* If we are sure pxgx cannot be -0 */
441 NR::Coord const pos = gxy2 / pxgx * (NR_GRADIENT_VECTOR_LENGTH-1);
443 unsigned char const *s;
444 if (pos < (1U << 31)) {
445 s = spread::color_at(pos, rgr->vector);
446 } else {
447 s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
448 }
450 compose::compose(pb, d, &spb, s);
451 d += compose::bpp;
453 gx += dx;
454 gy += dy;
455 }
456 }
458 nr_pixblock_release(&spb);
459 }
460 };
462 }
464 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
466 NRRenderer *
467 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
468 unsigned char const *cv,
469 unsigned spread,
470 NR::Matrix const *gs2px,
471 float cx, float cy,
472 float fx, float fy,
473 float r)
474 {
475 rgr->vector = cv;
476 rgr->spread = spread;
478 if (r < NR_EPSILON) {
479 rgr->render = nr_rgradient_render_block_end;
480 } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
481 NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
482 rgr->render = render<SymmetricRadial>;
484 rgr->px2gs = gs2px->inverse();
485 rgr->px2gs[0] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
486 rgr->px2gs[1] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
487 rgr->px2gs[2] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
488 rgr->px2gs[3] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
489 rgr->px2gs[4] -= cx;
490 rgr->px2gs[5] -= cy;
491 rgr->px2gs[4] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
492 rgr->px2gs[5] *= (NR_GRADIENT_VECTOR_LENGTH-1) / r;
493 rgr->px2gs[4] += 0.5*(rgr->px2gs[0]+rgr->px2gs[2]); // These offsets make sure the gradient is sampled in the MIDDLE of each pixel
494 rgr->px2gs[5] += 0.5*(rgr->px2gs[1]+rgr->px2gs[3]);
496 rgr->cx = 0.0;
497 rgr->cy = 0.0;
498 rgr->fx = rgr->cx;
499 rgr->fy = rgr->cy;
500 rgr->r = 1.0;
501 } else {
502 rgr->render = render<Radial>;
504 NR::Coord const df = hypot(fx - cx, fy - cy);
505 if (df >= r) {
506 fx = cx + (fx - cx ) * r / (float) df;
507 fy = cy + (fy - cy ) * r / (float) df;
508 }
510 NR::Matrix n2gs;
511 n2gs[0] = cx - fx;
512 n2gs[1] = cy - fy;
513 n2gs[2] = cy - fy;
514 n2gs[3] = fx - cx;
515 n2gs[4] = fx;
516 n2gs[5] = fy;
518 NR::Matrix n2px;
519 n2px = n2gs * (*gs2px);
520 rgr->px2gs = n2px.inverse();
521 rgr->px2gs[4] += 0.5*(rgr->px2gs[0]+rgr->px2gs[2]); // These offsets make sure the gradient is sampled in the MIDDLE of each pixel
522 rgr->px2gs[5] += 0.5*(rgr->px2gs[1]+rgr->px2gs[3]);
524 rgr->cx = 1.0;
525 rgr->cy = 0.0;
526 rgr->fx = 0.0;
527 rgr->fy = 0.0;
528 rgr->r = r / (float) hypot(fx - cx, fy - cy);
529 rgr->C = 1.0F - rgr->r * rgr->r;
530 /* INVARIANT: C < 0 */
531 rgr->C = MIN(rgr->C, -NR_EPSILON);
532 }
534 return (NRRenderer *) rgr;
535 }
537 static void
538 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
539 {
540 unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
542 nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
543 }
545 /*
546 Local Variables:
547 mode:c++
548 c-file-style:"stroustrup"
549 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
550 indent-tabs-mode:nil
551 fill-column:99
552 End:
553 */
554 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :