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 <libnr/nr-pixops.h>
22 #include <libnr/nr-pixblock-pixel.h>
23 #include <libnr/nr-blit.h>
24 #include <libnr/nr-gradient.h>
25 #include <glib/gtypes.h>
26 #include <stdio.h>
28 /* Common */
30 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
31 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
33 namespace {
34 inline unsigned char const *vector_index(int idx,
35 unsigned char const *vector)
36 {
37 return vector + 4 * idx;
38 }
40 template <NRGradientSpread spread> struct Spread;
42 template <>
43 struct Spread<NR_GRADIENT_SPREAD_PAD> {
44 static unsigned char const *color_at(NR::Coord r,
45 unsigned char const *vector)
46 {
47 return vector_index((int)CLAMP(r, 0, (double)(NR_GRADIENT_VECTOR_LENGTH - 1)), vector);
48 }
49 };
51 template <>
52 struct Spread<NR_GRADIENT_SPREAD_REPEAT> {
53 static unsigned char const *color_at(NR::Coord r,
54 unsigned char const *vector)
55 {
56 return vector_index((int)((long long)r & NRG_MASK), vector);
57 }
58 };
60 template <>
61 struct Spread<NR_GRADIENT_SPREAD_REFLECT> {
62 static unsigned char const *color_at(NR::Coord r,
63 unsigned char const *vector)
64 {
65 int idx = (int) ((long long)r & NRG_2MASK);
66 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
67 return vector_index(idx, vector);
68 }
69 };
71 template <NR_PIXBLOCK_MODE mode, bool empty=false>
72 struct Compose {
73 static void compose(NRPixBlock *pb, unsigned char *dest,
74 NRPixBlock *spb, unsigned char const *src)
75 {
76 nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
77 }
78 };
80 template <>
81 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> {
82 static void compose(NRPixBlock *pb, unsigned char *dest,
83 NRPixBlock *spb, unsigned char const *src)
84 {
85 dest[0] = src[0];
86 dest[1] = src[1];
87 dest[2] = src[2];
88 dest[3] = src[3];
89 }
90 };
92 template <>
93 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> {
94 static void compose(NRPixBlock *pb, unsigned char *dest,
95 NRPixBlock *spb, unsigned char const *src)
96 {
97 unsigned int ca;
98 ca = NR_COMPOSEA_112(src[3], dest[3]);
99 dest[0] = NR_COMPOSENNN_111121(src[0], src[3], dest[0], dest[3], ca);
100 dest[1] = NR_COMPOSENNN_111121(src[1], src[3], dest[1], dest[3], ca);
101 dest[2] = NR_COMPOSENNN_111121(src[2], src[3], dest[2], dest[3], ca);
102 dest[3] = NR_NORMALIZE_21(ca);
103 }
104 };
106 template <>
107 struct Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> {
108 static void compose(NRPixBlock *pb, unsigned char *dest,
109 NRPixBlock *spb, unsigned char const *src)
110 {
111 dest[0] = NR_COMPOSENPP_1111(src[0], src[3], dest[0]);
112 dest[1] = NR_COMPOSENPP_1111(src[1], src[3], dest[1]);
113 dest[2] = NR_COMPOSENPP_1111(src[2], src[3], dest[2]);
114 dest[3] = NR_COMPOSEA_111(src[3], dest[3]);
115 }
116 };
118 template <>
119 struct Compose<NR_PIXBLOCK_MODE_R8G8B8, false> {
120 static void compose(NRPixBlock *pb, unsigned char *dest,
121 NRPixBlock *spb, unsigned char const *src)
122 {
123 dest[0] = NR_COMPOSEN11_1111(src[0], src[3], dest[0]);
124 dest[1] = NR_COMPOSEN11_1111(src[1], src[3], dest[1]);
125 dest[2] = NR_COMPOSEN11_1111(src[2], src[3], dest[2]);
126 }
127 };
129 template <typename Subtype, typename spread>
130 static void
131 render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
132 {
133 switch (pb->mode) {
134 case NR_PIXBLOCK_MODE_R8G8B8A8N:
135 if (pb->empty) {
136 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, true> compose;
137 Subtype::template render<compose, spread, 4>(gr, pb);
138 } else {
139 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8N, false> compose;
140 Subtype::template render<compose, spread, 4>(gr, pb);
141 }
142 break;
143 case NR_PIXBLOCK_MODE_R8G8B8A8P:
144 if (pb->empty) {
145 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, true> compose;
146 Subtype::template render<compose, spread, 4>(gr, pb);
147 } else {
148 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8A8P, false> compose;
149 Subtype::template render<compose, spread, 4>(gr, pb);
150 }
151 break;
152 case NR_PIXBLOCK_MODE_R8G8B8:
153 if (pb->empty) {
154 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, true> compose;
155 Subtype::template render<compose, spread, 3>(gr, pb);
156 } else {
157 typedef Compose<NR_PIXBLOCK_MODE_R8G8B8, false> compose;
158 Subtype::template render<compose, spread, 3>(gr, pb);
159 }
160 break;
161 case NR_PIXBLOCK_MODE_A8:
162 if (pb->empty) {
163 typedef Compose<NR_PIXBLOCK_MODE_A8, true> compose;
164 Subtype::template render<compose, spread, 1>(gr, pb);
165 } else {
166 typedef Compose<NR_PIXBLOCK_MODE_A8, false> compose;
167 Subtype::template render<compose, spread, 1>(gr, pb);
168 }
169 break;
170 }
171 }
173 template <typename Subtype>
174 static void
175 render(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
176 {
177 NRGradientRenderer *gr;
179 gr = static_cast<NRGradientRenderer *>(r);
181 switch (gr->spread) {
182 case NR_GRADIENT_SPREAD_REPEAT:
183 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
184 break;
185 case NR_GRADIENT_SPREAD_REFLECT:
186 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
187 break;
188 case NR_GRADIENT_SPREAD_PAD:
189 default:
190 render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
191 }
192 }
193 }
195 /* Linear */
197 namespace {
199 struct Linear {
200 template <typename compose, typename spread, unsigned bpp>
201 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
202 NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
204 int x, y;
205 unsigned char *d;
206 double pos;
207 NRPixBlock spb;
208 int x0, y0, width, height, rs;
210 x0 = pb->area.x0;
211 y0 = pb->area.y0;
212 width = pb->area.x1 - pb->area.x0;
213 height = pb->area.y1 - pb->area.y0;
214 rs = pb->rs;
216 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
217 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
218 (unsigned char *) lgr->vector,
219 4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
221 for (y = 0; y < height; y++) {
222 d = NR_PIXBLOCK_PX(pb) + y * rs;
223 pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
224 for (x = 0; x < width; x++) {
225 unsigned char const *s=spread::color_at(pos, lgr->vector);
226 compose::compose(pb, d, &spb, s);
227 d += bpp;
228 pos += lgr->dx;
229 }
230 }
232 nr_pixblock_release(&spb);
233 }
234 };
236 }
238 NRRenderer *
239 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
240 const unsigned char *cv,
241 unsigned int spread,
242 const NRMatrix *gs2px,
243 float x0, float y0,
244 float x1, float y1)
245 {
246 NRMatrix n2gs, n2px, px2n;
248 lgr->render = &render<Linear>;
250 lgr->vector = cv;
251 lgr->spread = spread;
253 n2gs.c[0] = x1 - x0;
254 n2gs.c[1] = y1 - y0;
255 n2gs.c[2] = y1 - y0;
256 n2gs.c[3] = x0 - x1;
257 n2gs.c[4] = x0;
258 n2gs.c[5] = y0;
260 nr_matrix_multiply (&n2px, &n2gs, gs2px);
261 nr_matrix_invert (&px2n, &n2px);
263 lgr->x0 = n2px.c[4] - 0.5;
264 lgr->y0 = n2px.c[5] - 0.5;
265 lgr->dx = px2n.c[0] * NR_GRADIENT_VECTOR_LENGTH;
266 lgr->dy = px2n.c[2] * NR_GRADIENT_VECTOR_LENGTH;
268 return (NRRenderer *) lgr;
269 }
271 /* Radial */
273 /*
274 * The archetype is following
275 *
276 * gx gy - pixel coordinates
277 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
278 *
279 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
280 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
281 *
282 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
283 * (4) (gy - fy) / (gx - fx) = D
284 * (5) Py = D * Px - D * fx + fy
285 *
286 * (6) D * fx - fy + cy = N
287 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
288 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
289 *
290 * (9) A = D * D + 1
291 * (10) B = -2 * (cx + D * N)
292 * (11) C = cx * cx + N * N - r * r
293 *
294 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
295 */
297 namespace {
299 struct SymmetricRadial {
300 template <typename compose, typename spread, unsigned bpp>
301 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
302 {
303 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
305 NR::Coord const dx = rgr->px2gs.c[0];
306 NR::Coord const dy = rgr->px2gs.c[1];
308 NRPixBlock spb;
309 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
310 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
311 (unsigned char *) rgr->vector,
312 4 * NR_GRADIENT_VECTOR_LENGTH,
313 0, 0);
315 for (int y = pb->area.y0; y < pb->area.y1; y++) {
316 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
317 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
318 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
319 for (int x = pb->area.x0; x < pb->area.x1; x++) {
320 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
321 unsigned char const *s=spread::color_at(pos, rgr->vector);
322 compose::compose(pb, d, &spb, s);
323 d += bpp;
324 gx += dx;
325 gy += dy;
326 }
327 }
329 nr_pixblock_release(&spb);
330 }
331 };
333 struct Radial {
334 template <typename compose, typename spread, unsigned bpp>
335 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
336 {
337 NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
338 int const x0 = pb->area.x0;
339 int const y0 = pb->area.y0;
340 int const x1 = pb->area.x1;
341 int const y1 = pb->area.y1;
342 int const rs = pb->rs;
344 NRPixBlock spb;
345 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
346 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
347 (unsigned char *) rgr->vector,
348 4 * NR_GRADIENT_VECTOR_LENGTH,
349 0, 0);
351 for (int y = y0; y < y1; y++) {
352 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
353 NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
354 NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
355 NR::Coord const dx = rgr->px2gs.c[0];
356 NR::Coord const dy = rgr->px2gs.c[1];
357 for (int x = x0; x < x1; x++) {
358 NR::Coord const gx2 = gx * gx;
359 NR::Coord const gxy2 = gx2 + gy * gy;
360 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
361 /* INVARIANT: qgx2_4 >= 0.0 */
362 /* qgx2_4 = MAX(qgx2_4, 0.0); */
363 NR::Coord const pxgx = gx + sqrt(qgx2_4);
364 /* We can safely divide by 0 here */
365 /* If we are sure pxgx cannot be -0 */
366 NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
368 unsigned char const *s;
369 if (pos < (1U << 31)) {
370 s = spread::color_at(pos, rgr->vector);
371 } else {
372 s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
373 }
375 compose::compose(pb, d, &spb, s);
377 d += bpp;
379 gx += dx;
380 gy += dy;
381 }
382 }
384 nr_pixblock_release(&spb);
385 }
386 };
388 }
390 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
392 NRRenderer *
393 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
394 unsigned char const *cv,
395 unsigned spread,
396 NRMatrix const *gs2px,
397 float cx, float cy,
398 float fx, float fy,
399 float r)
400 {
401 rgr->vector = cv;
402 rgr->spread = spread;
404 if (r < NR_EPSILON) {
405 rgr->render = nr_rgradient_render_block_end;
406 } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
407 NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
408 rgr->render = render<SymmetricRadial>;
410 nr_matrix_invert(&rgr->px2gs, gs2px);
411 rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
412 rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
413 rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
414 rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
415 rgr->px2gs.c[4] -= cx;
416 rgr->px2gs.c[5] -= cy;
417 rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
418 rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
420 rgr->cx = 0.0;
421 rgr->cy = 0.0;
422 rgr->fx = rgr->cx;
423 rgr->fy = rgr->cy;
424 rgr->r = 1.0;
425 } else {
426 rgr->render = render<Radial>;
428 NR::Coord const df = hypot(fx - cx, fy - cy);
429 if (df >= r) {
430 fx = cx + (fx - cx ) * r / (float) df;
431 fy = cy + (fy - cy ) * r / (float) df;
432 }
434 NRMatrix n2gs;
435 n2gs.c[0] = cx - fx;
436 n2gs.c[1] = cy - fy;
437 n2gs.c[2] = cy - fy;
438 n2gs.c[3] = fx - cx;
439 n2gs.c[4] = fx;
440 n2gs.c[5] = fy;
442 NRMatrix n2px;
443 nr_matrix_multiply(&n2px, &n2gs, gs2px);
444 nr_matrix_invert(&rgr->px2gs, &n2px);
446 rgr->cx = 1.0;
447 rgr->cy = 0.0;
448 rgr->fx = 0.0;
449 rgr->fy = 0.0;
450 rgr->r = r / (float) hypot(fx - cx, fy - cy);
451 rgr->C = 1.0F - rgr->r * rgr->r;
452 /* INVARIANT: C < 0 */
453 rgr->C = MIN(rgr->C, -NR_EPSILON);
454 }
456 return (NRRenderer *) rgr;
457 }
459 static void
460 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
461 {
462 unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
464 nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
465 }
467 /*
468 Local Variables:
469 mode:c++
470 c-file-style:"stroustrup"
471 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
472 indent-tabs-mode:nil
473 fill-column:99
474 End:
475 */
476 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :