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 <glib/gtypes.h>
27 #include <stdio.h>
29 /* Common */
31 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
32 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
34 namespace {
35 inline unsigned char const *vector_index(int idx,
36 unsigned char const *vector)
37 {
38 return vector + 4 * idx;
39 }
41 template <NRGradientSpread spread> struct Spread;
43 template <>
44 struct Spread<NR_GRADIENT_SPREAD_PAD> {
45 static unsigned char const *color_at(NR::Coord r,
46 unsigned char const *vector)
47 {
48 return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
49 }
50 };
52 template <>
53 struct Spread<NR_GRADIENT_SPREAD_REPEAT> {
54 static unsigned char const *color_at(NR::Coord r,
55 unsigned char const *vector)
56 {
57 return vector_index((int)((long long)r & NRG_MASK), vector);
58 }
59 };
61 template <>
62 struct Spread<NR_GRADIENT_SPREAD_REFLECT> {
63 static unsigned char const *color_at(NR::Coord r,
64 unsigned char const *vector)
65 {
66 int idx = (int) ((long long)r & NRG_2MASK);
67 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
68 return vector_index(idx, vector);
69 }
70 };
72 template <NR_PIXBLOCK_MODE mode> struct ModeTraits;
74 template <>
75 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8N> {
76 static const unsigned bpp=4;
77 };
79 template <>
80 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8A8P> {
81 static const unsigned bpp=4;
82 };
84 template <>
85 struct ModeTraits<NR_PIXBLOCK_MODE_R8G8B8> {
86 static const unsigned bpp=3;
87 };
89 template <>
90 struct ModeTraits<NR_PIXBLOCK_MODE_A8> {
91 static const unsigned bpp=1;
92 };
94 template <NR_PIXBLOCK_MODE mode, bool empty>
95 struct Compose {
96 static const unsigned bpp=ModeTraits<mode>::bpp;
97 static void compose(NRPixBlock *pb, unsigned char *dest,
98 NRPixBlock *spb, unsigned char const *src)
99 {
100 nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
101 }
102 };
104 template <>
105 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
106 static const unsigned bpp=4;
107 static void compose(NRPixBlock *pb, unsigned char *dest,
108 NRPixBlock *spb, unsigned char const *src)
109 {
110 std::memcpy(dest, src, 4);
111 }
112 };
114 template <>
115 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> {
116 static const unsigned bpp=4;
117 static void compose(NRPixBlock *pb, unsigned char *dest,
118 NRPixBlock *spb, unsigned char const *src)
119 {
120 dest[0] = NR_PREMUL_111(src[0], src[3]);
121 dest[1] = NR_PREMUL_111(src[1], src[3]);
122 dest[2] = NR_PREMUL_111(src[2], src[3]);
123 dest[3] = src[3];
124 }
125 };
127 template <>
128 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, true> {
129 static const unsigned bpp=3;
130 static void compose(NRPixBlock *pb, unsigned char *dest,
131 NRPixBlock *spb, unsigned char const *src)
132 {
133 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], 255);
134 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], 255);
135 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], 255);
136 }
137 };
139 template <>
140 struct Compose<NR_PIXBLOCK_MODE_A8, true> {
141 static const unsigned bpp=1;
142 static void compose(NRPixBlock *pb, unsigned char *dest,
143 NRPixBlock *spb, unsigned char const *src)
144 {
145 dest[0] = src[3];
146 }
147 };
149 template <>
150 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
151 static const unsigned bpp=4;
152 static void compose(NRPixBlock *pb, unsigned char *dest,
153 NRPixBlock *spb, unsigned char const *src)
154 {
155 unsigned int ca;
156 ca = NR_COMPOSEA_112(src[3], dest[3]);
157 dest[0] = NR_COMPOSENNN_111121(src[0], src[3], dest[0], dest[3], ca);
158 dest[1] = NR_COMPOSENNN_111121(src[1], src[3], dest[1], dest[3], ca);
159 dest[2] = NR_COMPOSENNN_111121(src[2], src[3], dest[2], dest[3], ca);
160 dest[3] = NR_NORMALIZE_21(ca);
161 }
162 };
164 template <>
165 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
166 static const unsigned bpp=4;
167 static void compose(NRPixBlock *pb, unsigned char *dest,
168 NRPixBlock *spb, unsigned char const *src)
169 {
170 dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
171 dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
172 dest[2] = NR_COMPOSENPP_1111(src[2], src[3], dest[2]);
173 dest[3] = NR_COMPOSEA_111(src[3], dest[3]);
174 }
175 };
177 template <>
178 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
179 static const unsigned bpp=3;
180 static void compose(NRPixBlock *pb, unsigned char *dest,
181 NRPixBlock *spb, unsigned char const *src)
182 {
183 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
184 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
185 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], dest[2]);
186 }
187 };
189 template <typename Subtype, typename spread>
190 static void
191 render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
192 {
193 switch (pb->mode) {
194 case NR_PIXBLOCK_MODE_R8G8B8A8N:
195 if (pb->empty) {
196 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
197 Subtype::template render<compose, spread>(gr, pb);
198 } else {
199 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
200 Subtype::template render<compose, spread>(gr, pb);
201 }
202 break;
203 case NR_PIXBLOCK_MODE_R8G8B8A8P:
204 if (pb->empty) {
205 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> compose;
206 Subtype::template render<compose, spread>(gr, pb);
207 } else {
208 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
209 Subtype::template render<compose, spread>(gr, pb);
210 }
211 break;
212 case NR_PIXBLOCK_MODE_R8G8B8:
213 if (pb->empty) {
214 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, true> compose;
215 Subtype::template render<compose, spread>(gr, pb);
216 } else {
217 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
218 Subtype::template render<compose, spread>(gr, pb);
219 }
220 break;
221 case NR_PIXBLOCK_MODE_A8:
222 if (pb->empty) {
223 typedef Compose<NR_PIXBLOCK_MODE_A8, true> compose;
224 Subtype::template render<compose, spread>(gr, pb);
225 } else {
226 typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
227 Subtype::template render<compose, spread>(gr, pb);
228 }
229 break;
230 }
231 }
233 template <typename Subtype>
234 static void
235 render(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
236 {
237 NRGradientRenderer *gr;
239 gr = static_cast<NRGradientRenderer *>(r);
241 switch (gr->spread) {
242 case NR_GRADIENT_SPREAD_REPEAT:
243 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
244 break;
245 case NR_GRADIENT_SPREAD_REFLECT:
246 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
247 break;
248 case NR_GRADIENT_SPREAD_PAD:
249 default:
250 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
251 }
252 }
253 }
255 /* Linear */
257 namespace {
259 struct Linear {
260 template <typename compose, typename spread>
261 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
262 NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
264 int x, y;
265 unsigned char *d;
266 double pos;
267 NRPixBlock spb;
268 int x0, y0, width, height, rs;
270 x0 = pb->area.x0;
271 y0 = pb->area.y0;
272 width = pb->area.x1 - pb->area.x0;
273 height = pb->area.y1 - pb->area.y0;
274 rs = pb->rs;
276 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
277 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
278 (unsigned char *) lgr->vector,
279 4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
281 for (y = 0; y < height; y++) {
282 d = NR_PIXBLOCK_PX(pb) + y * rs;
283 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
284 for (x = 0; x < width; x++) {
285 unsigned char const *s=spread::color_at(pos, lgr->vector);
286 compose::compose(pb, d, &spb, s);
287 d += compose::bpp;
288 pos += lgr->dx;
289 }
290 }
292 nr_pixblock_release(&spb);
293 }
294 };
296 }
298 NRRenderer *
299 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
300 const unsigned char *cv,
301 unsigned int spread,
302 const NRMatrix *gs2px,
303 float x0, float y0,
304 float x1, float y1)
305 {
306 NRMatrix n2gs, n2px, px2n;
308 lgr->render = &render<Linear>;
310 lgr->vector = cv;
311 lgr->spread = spread;
313 n2gs.c[0] = x1 - x0;
314 n2gs.c[1] = y1 - y0;
315 n2gs.c[2] = y1 - y0;
316 n2gs.c[3] = x0 - x1;
317 n2gs.c[4] = x0;
318 n2gs.c[5] = y0;
320 nr_matrix_multiply (&n2px, &n2gs, gs2px);
321 nr_matrix_invert (&px2n, &n2px);
323 lgr->x0 = n2px.c[4] - 0.5;
324 lgr->y0 = n2px.c[5] - 0.5;
325 lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
326 lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
328 return (NRRenderer *) lgr;
329 }
331 /* Radial */
333 /*
334 * The archetype is following
335 *
336 * gx gy - pixel coordinates
337 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
338 *
339 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
340 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
341 *
342 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
343 * (4) (gy - fy) / (gx - fx) = D
344 * (5) Py = D * Px - D * fx + fy
345 *
346 * (6) D * fx - fy + cy = N
347 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
348 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
349 *
350 * (9) A = D * D + 1
351 * (10) B = -2 * (cx + D * N)
352 * (11) C = cx * cx + N * N - r * r
353 *
354 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
355 */
357 namespace {
359 struct SymmetricRadial {
360 template <typename compose, typename spread>
361 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
362 {
363 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
365 NR::Coord const dx = rgr->px2gs.c[0];
366 NR::Coord const dy = rgr->px2gs.c[1];
368 NRPixBlock spb;
369 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
370 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
371 (unsigned char *) rgr->vector,
372 4 * NR_GRADIENT_VECTOR_LENGTH,
373 0, 0);
375 for (int y = pb->area.y0; y < pb->area.y1; y++) {
376 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
377 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
378 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
379 for (int x = pb->area.x0; x < pb->area.x1; x++) {
380 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
381 unsigned char const *s=spread::color_at(pos, rgr->vector);
382 compose::compose(pb, d, &spb, s);
383 d += compose::bpp;
384 gx += dx;
385 gy += dy;
386 }
387 }
389 nr_pixblock_release(&spb);
390 }
391 };
393 struct Radial {
394 template <typename compose, typename spread>
395 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
396 {
397 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
398 int const x0 = pb->area.x0;
399 int const y0 = pb->area.y0;
400 int const x1 = pb->area.x1;
401 int const y1 = pb->area.y1;
402 int const rs = pb->rs;
404 NRPixBlock spb;
405 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
406 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
407 (unsigned char *) rgr->vector,
408 4 * NR_GRADIENT_VECTOR_LENGTH,
409 0, 0);
411 for (int y = y0; y < y1; y++) {
412 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
413 NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
414 NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
415 NR::Coord const dx = rgr->px2gs.c[0];
416 NR::Coord const dy = rgr->px2gs.c[1];
417 for (int x = x0; x < x1; x++) {
418 NR::Coord const gx2 = gx * gx;
419 NR::Coord const gxy2 = gx2 + gy * gy;
420 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
421 /* INVARIANT: qgx2_4 >= 0.0 */
422 /* qgx2_4 = MAX(qgx2_4, 0.0); */
423 NR::Coord const pxgx = gx + sqrt(qgx2_4);
424 /* We can safely divide by 0 here */
425 /* If we are sure pxgx cannot be -0 */
426 NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
428 unsigned char const *s;
429 if (pos < (1U << 31)) {
430 s = spread::color_at(pos, rgr->vector);
431 } else {
432 s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
433 }
435 compose::compose(pb, d, &spb, s);
436 d += compose::bpp;
438 gx += dx;
439 gy += dy;
440 }
441 }
443 nr_pixblock_release(&spb);
444 }
445 };
447 }
449 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
451 NRRenderer *
452 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
453 unsigned char const *cv,
454 unsigned spread,
455 NRMatrix const *gs2px,
456 float cx, float cy,
457 float fx, float fy,
458 float r)
459 {
460 rgr->vector = cv;
461 rgr->spread = spread;
463 if (r < NR_EPSILON) {
464 rgr->render = nr_rgradient_render_block_end;
465 } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
466 NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
467 rgr->render = render<SymmetricRadial>;
469 nr_matrix_invert(&rgr->px2gs, gs2px);
470 rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
471 rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
472 rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
473 rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
474 rgr->px2gs.c[4] -= cx;
475 rgr->px2gs.c[5] -= cy;
476 rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
477 rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
479 rgr->cx = 0.0;
480 rgr->cy = 0.0;
481 rgr->fx = rgr->cx;
482 rgr->fy = rgr->cy;
483 rgr->r = 1.0;
484 } else {
485 rgr->render = render<Radial>;
487 NR::Coord const df = hypot(fx - cx, fy - cy);
488 if (df >= r) {
489 fx = cx + (fx - cx ) * r / (float) df;
490 fy = cy + (fy - cy ) * r / (float) df;
491 }
493 NRMatrix n2gs;
494 n2gs.c[0] = cx - fx;
495 n2gs.c[1] = cy - fy;
496 n2gs.c[2] = cy - fy;
497 n2gs.c[3] = fx - cx;
498 n2gs.c[4] = fx;
499 n2gs.c[5] = fy;
501 NRMatrix n2px;
502 nr_matrix_multiply(&n2px, &n2gs, gs2px);
503 nr_matrix_invert(&rgr->px2gs, &n2px);
505 rgr->cx = 1.0;
506 rgr->cy = 0.0;
507 rgr->fx = 0.0;
508 rgr->fy = 0.0;
509 rgr->r = r / (float) hypot(fx - cx, fy - cy);
510 rgr->C = 1.0F - rgr->r * rgr->r;
511 /* INVARIANT: C < 0 */
512 rgr->C = MIN(rgr->C, -NR_EPSILON);
513 }
515 return (NRRenderer *) rgr;
516 }
518 static void
519 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
520 {
521 unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
523 nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
524 }
526 /*
527 Local Variables:
528 mode:c++
529 c-file-style:"stroustrup"
530 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
531 indent-tabs-mode:nil
532 fill-column:99
533 End:
534 */
535 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :