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 *
10 * Copyright (C) 2007 MenTaLguY
11 * Copyright (C) 2001-2002 Lauris Kaplinski
12 * Copyright (C) 2001-2002 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 /*
18 * Derived in part from public domain code by Lauris Kaplinski
19 */
21 #include <cstring>
22 #include <libnr/nr-pixops.h>
23 #include <libnr/nr-pixblock-pixel.h>
24 #include <libnr/nr-blit.h>
25 #include <libnr/nr-gradient.h>
26 #include <libnr/nr-matrix-ops.h>
27 #include <glib/gtypes.h>
28 #include <stdio.h>
30 /* Common */
32 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
33 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
35 namespace {
36 inline unsigned char const *vector_index(int idx,
37 unsigned char const *vector)
38 {
39 return vector + 4 * idx;
40 }
42 template <NRGradientSpread spread> struct Spread;
44 template <>
45 struct Spread<NR_GRADIENT_SPREAD_PAD> {
46 static unsigned char const *color_at(NR::Coord r,
47 unsigned char const *vector)
48 {
49 return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
50 }
51 };
53 template <>
54 struct Spread<NR_GRADIENT_SPREAD_REPEAT> {
55 static unsigned char const *color_at(NR::Coord r,
56 unsigned char const *vector)
57 {
58 return vector_index((int)((long long)r & NRG_MASK), vector);
59 }
60 };
62 template <>
63 struct Spread<NR_GRADIENT_SPREAD_REFLECT> {
64 static unsigned char const *color_at(NR::Coord r,
65 unsigned char const *vector)
66 {
67 int idx = (int) ((long long)r & NRG_2MASK);
68 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
69 return vector_index(idx, vector);
70 }
71 };
73 template <NR_PIXBLOCK_MODE mode> struct ModeTraits;
75 template <>
76 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8N> {
77 static const unsigned bpp=4;
78 };
80 template <>
81 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8P> {
82 static const unsigned bpp=4;
83 };
85 template <>
86 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8> {
87 static const unsigned bpp=3;
88 };
90 template <>
91 struct ModeTraits<NR_PIXBLOCK_MODE_A8> {
92 static const unsigned bpp=1;
93 };
95 template <NR_PIXBLOCK_MODE mode, bool empty>
96 struct Compose {
97 static const unsigned bpp=ModeTraits<mode>::bpp;
98 static void compose(NRPixBlock *pb, unsigned char *dest,
99 NRPixBlock *spb, unsigned char const *src)
100 {
101 nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
102 }
103 };
105 template <>
106 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
107 static const unsigned bpp=4;
108 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
109 NRPixBlock */*spb*/, unsigned char const *src)
110 {
111 std::memcpy(dest, src, 4);
112 }
113 };
115 template <>
116 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> {
117 static const unsigned bpp=4;
118 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
119 NRPixBlock */*spb*/, unsigned char const *src)
120 {
121 dest[0] = NR_PREMUL_111(src[0], src[3]);
122 dest[1] = NR_PREMUL_111(src[1], src[3]);
123 dest[2] = NR_PREMUL_111(src[2], src[3]);
124 dest[3] = src[3];
125 }
126 };
128 template <>
129 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, true> {
130 static const unsigned bpp=3;
131 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
132 NRPixBlock */*spb*/, unsigned char const *src)
133 {
134 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], 255);
135 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], 255);
136 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], 255);
137 }
138 };
140 template <>
141 struct Compose<NR_PIXBLOCK_MODE_A8, true> {
142 static const unsigned bpp=1;
143 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
144 NRPixBlock */*spb*/, unsigned char const *src)
145 {
146 dest[0] = src[3];
147 }
148 };
150 template <>
151 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
152 static const unsigned bpp=4;
153 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
154 NRPixBlock */*spb*/, unsigned char const *src)
155 {
156 unsigned int ca;
157 ca = NR_COMPOSEA_112(src[3], dest[3]);
158 dest[0] = NR_COMPOSENNN_111121(src[0], src[3], dest[0], dest[3], ca);
159 dest[1] = NR_COMPOSENNN_111121(src[1], src[3], dest[1], dest[3], ca);
160 dest[2] = NR_COMPOSENNN_111121(src[2], src[3], dest[2], dest[3], ca);
161 dest[3] = NR_NORMALIZE_21(ca);
162 }
163 };
165 template <>
166 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
167 static const unsigned bpp=4;
168 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
169 NRPixBlock */*spb*/, unsigned char const *src)
170 {
171 dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
172 dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
173 dest[2] = NR_COMPOSENPP_1111(src[2], src[3], dest[2]);
174 dest[3] = NR_COMPOSEA_111(src[3], dest[3]);
175 }
176 };
178 template <>
179 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
180 static const unsigned bpp=3;
181 static void compose(NRPixBlock */*pb*/, unsigned char *dest,
182 NRPixBlock */*spb*/, unsigned char const *src)
183 {
184 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
185 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
186 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], dest[2]);
187 }
188 };
190 template <typename Subtype, typename spread>
191 static void
192 render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
193 {
194 switch (pb->mode) {
195 case NR_PIXBLOCK_MODE_R8G8B8A8N:
196 if (pb->empty) {
197 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
198 Subtype::template render<compose, spread>(gr, pb);
199 } else {
200 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
201 Subtype::template render<compose, spread>(gr, pb);
202 }
203 break;
204 case NR_PIXBLOCK_MODE_R8G8B8A8P:
205 if (pb->empty) {
206 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> compose;
207 Subtype::template render<compose, spread>(gr, pb);
208 } else {
209 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
210 Subtype::template render<compose, spread>(gr, pb);
211 }
212 break;
213 case NR_PIXBLOCK_MODE_R8G8B8:
214 if (pb->empty) {
215 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, true> compose;
216 Subtype::template render<compose, spread>(gr, pb);
217 } else {
218 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
219 Subtype::template render<compose, spread>(gr, pb);
220 }
221 break;
222 case NR_PIXBLOCK_MODE_A8:
223 if (pb->empty) {
224 typedef Compose<NR_PIXBLOCK_MODE_A8, true> compose;
225 Subtype::template render<compose, spread>(gr, pb);
226 } else {
227 typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
228 Subtype::template render<compose, spread>(gr, pb);
229 }
230 break;
231 }
232 }
234 template <typename Subtype>
235 static void
236 render(NRRenderer *r, NRPixBlock *pb, NRPixBlock */*m*/)
237 {
238 NRGradientRenderer *gr = static_cast<NRGradientRenderer *>(r);
240 switch (gr->spread) {
241 case NR_GRADIENT_SPREAD_REPEAT:
242 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
243 break;
244 case NR_GRADIENT_SPREAD_REFLECT:
245 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
246 break;
247 case NR_GRADIENT_SPREAD_PAD:
248 default:
249 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
250 }
251 }
252 }
254 /* Linear */
256 namespace {
258 struct Linear {
259 template <typename compose, typename spread>
260 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
261 NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
263 int x, y;
264 unsigned char *d;
265 double pos;
266 NRPixBlock spb;
267 int x0, y0, width, height, rs;
269 x0 = pb->area.x0;
270 y0 = pb->area.y0;
271 width = pb->area.x1 - pb->area.x0;
272 height = pb->area.y1 - pb->area.y0;
273 rs = pb->rs;
275 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
276 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
277 (unsigned char *) lgr->vector,
278 4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
280 for (y = 0; y < height; y++) {
281 d = NR_PIXBLOCK_PX(pb) + y * rs;
282 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
283 for (x = 0; x < width; x++) {
284 unsigned char const *s=spread::color_at(pos, lgr->vector);
285 compose::compose(pb, d, &spb, s);
286 d += compose::bpp;
287 pos += lgr->dx;
288 }
289 }
291 nr_pixblock_release(&spb);
292 }
293 };
295 }
297 NRRenderer *
298 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
299 const unsigned char *cv,
300 unsigned int spread,
301 const NR::Matrix *gs2px,
302 float x0, float y0,
303 float x1, float y1)
304 {
305 NR::Matrix n2gs, n2px, px2n;
307 lgr->render = &render<Linear>;
309 lgr->vector = cv;
310 lgr->spread = spread;
312 n2gs[0] = x1 - x0;
313 n2gs[1] = y1 - y0;
314 n2gs[2] = y1 - y0;
315 n2gs[3] = x0 - x1;
316 n2gs[4] = x0;
317 n2gs[5] = y0;
319 n2px = n2gs * (*gs2px);
320 px2n = n2px.inverse();
322 lgr->x0 = n2px[4] - 0.5;
323 lgr->y0 = n2px[5] - 0.5;
324 lgr->dx = px2n[0] * NR_GRADIENT_VECTOR_LENGTH;
325 lgr->dy = px2n[2] * NR_GRADIENT_VECTOR_LENGTH;
327 return (NRRenderer *) lgr;
328 }
330 /* Radial */
332 /*
333 * The archetype is following
334 *
335 * gx gy - pixel coordinates
336 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
337 *
338 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
339 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
340 *
341 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
342 * (4) (gy - fy) / (gx - fx) = D
343 * (5) Py = D * Px - D * fx + fy
344 *
345 * (6) D * fx - fy + cy = N
346 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
347 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
348 *
349 * (9) A = D * D + 1
350 * (10) B = -2 * (cx + D * N)
351 * (11) C = cx * cx + N * N - r * r
352 *
353 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
354 */
356 namespace {
358 struct SymmetricRadial {
359 template <typename compose, typename spread>
360 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
361 {
362 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
364 NR::Coord const dx = rgr->px2gs[0];
365 NR::Coord const dy = rgr->px2gs[1];
367 NRPixBlock spb;
368 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
369 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
370 (unsigned char *) rgr->vector,
371 4 * NR_GRADIENT_VECTOR_LENGTH,
372 0, 0);
374 for (int y = pb->area.y0; y < pb->area.y1; y++) {
375 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
376 NR::Coord gx = rgr->px2gs[0] * pb->area.x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
377 NR::Coord gy = rgr->px2gs[1] * pb->area.x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
378 for (int x = pb->area.x0; x < pb->area.x1; x++) {
379 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
380 unsigned char const *s=spread::color_at(pos, rgr->vector);
381 compose::compose(pb, d, &spb, s);
382 d += compose::bpp;
383 gx += dx;
384 gy += dy;
385 }
386 }
388 nr_pixblock_release(&spb);
389 }
390 };
392 struct Radial {
393 template <typename compose, typename spread>
394 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
395 {
396 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
397 int const x0 = pb->area.x0;
398 int const y0 = pb->area.y0;
399 int const x1 = pb->area.x1;
400 int const y1 = pb->area.y1;
401 int const rs = pb->rs;
403 NRPixBlock spb;
404 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
405 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
406 (unsigned char *) rgr->vector,
407 4 * NR_GRADIENT_VECTOR_LENGTH,
408 0, 0);
410 for (int y = y0; y < y1; y++) {
411 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
412 NR::Coord gx = rgr->px2gs[0] * x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
413 NR::Coord gy = rgr->px2gs[1] * x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
414 NR::Coord const dx = rgr->px2gs[0];
415 NR::Coord const dy = rgr->px2gs[1];
416 for (int x = x0; x < x1; x++) {
417 NR::Coord const gx2 = gx * gx;
418 NR::Coord const gxy2 = gx2 + gy * gy;
419 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
420 /* INVARIANT: qgx2_4 >= 0.0 */
421 /* qgx2_4 = MAX(qgx2_4, 0.0); */
422 NR::Coord const pxgx = gx + sqrt(qgx2_4);
423 /* We can safely divide by 0 here */
424 /* If we are sure pxgx cannot be -0 */
425 NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
427 unsigned char const *s;
428 if (pos < (1U << 31)) {
429 s = spread::color_at(pos, rgr->vector);
430 } else {
431 s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
432 }
434 compose::compose(pb, d, &spb, s);
435 d += compose::bpp;
437 gx += dx;
438 gy += dy;
439 }
440 }
442 nr_pixblock_release(&spb);
443 }
444 };
446 }
448 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
450 NRRenderer *
451 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
452 unsigned char const *cv,
453 unsigned spread,
454 NR::Matrix const *gs2px,
455 float cx, float cy,
456 float fx, float fy,
457 float r)
458 {
459 rgr->vector = cv;
460 rgr->spread = spread;
462 if (r < NR_EPSILON) {
463 rgr->render = nr_rgradient_render_block_end;
464 } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
465 NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
466 rgr->render = render<SymmetricRadial>;
468 rgr->px2gs = gs2px->inverse();
469 rgr->px2gs[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
470 rgr->px2gs[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
471 rgr->px2gs[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
472 rgr->px2gs[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
473 rgr->px2gs[4] -= cx;
474 rgr->px2gs[5] -= cy;
475 rgr->px2gs[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
476 rgr->px2gs[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
478 rgr->cx = 0.0;
479 rgr->cy = 0.0;
480 rgr->fx = rgr->cx;
481 rgr->fy = rgr->cy;
482 rgr->r = 1.0;
483 } else {
484 rgr->render = render<Radial>;
486 NR::Coord const df = hypot(fx - cx, fy - cy);
487 if (df >= r) {
488 fx = cx + (fx - cx ) * r / (float) df;
489 fy = cy + (fy - cy ) * r / (float) df;
490 }
492 NR::Matrix n2gs;
493 n2gs[0] = cx - fx;
494 n2gs[1] = cy - fy;
495 n2gs[2] = cy - fy;
496 n2gs[3] = fx - cx;
497 n2gs[4] = fx;
498 n2gs[5] = fy;
500 NR::Matrix n2px;
501 n2px = n2gs * (*gs2px);
502 rgr->px2gs = n2px.inverse();
504 rgr->cx = 1.0;
505 rgr->cy = 0.0;
506 rgr->fx = 0.0;
507 rgr->fy = 0.0;
508 rgr->r = r / (float) hypot(fx - cx, fy - cy);
509 rgr->C = 1.0F - rgr->r * rgr->r;
510 /* INVARIANT: C < 0 */
511 rgr->C = MIN(rgr->C, -NR_EPSILON);
512 }
514 return (NRRenderer *) rgr;
515 }
517 static void
518 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
519 {
520 unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
522 nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
523 }
525 /*
526 Local Variables:
527 mode:c++
528 c-file-style:"stroustrup"
529 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
530 indent-tabs-mode:nil
531 fill-column:99
532 End:
533 */
534 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :