Code

57ba651f42c745c48eb489093d2c65a226c2dd8c
[inkscape.git] / src / libnr / nr-gradient.cpp
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)
101     nr_compose_pixblock_pixblock_pixel(pb, dest, spb, src);
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)
111     std::memcpy(dest, src, 4);
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)
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];
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)
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);
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)
146     dest[0] = src[3];
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)
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);
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)
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]);
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)
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]);
188 };
190 template <typename Subtype, typename spread>
191 static void
192 render_spread(NRGradientRenderer *gr, NRPixBlock *pb)
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     }
234 template <typename Subtype>
235 static void
236 render(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
238     NRGradientRenderer *gr;
240     gr = static_cast<NRGradientRenderer *>(r);
242     switch (gr->spread) {
243     case NR_GRADIENT_SPREAD_REPEAT:
244         render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REPEAT> >(gr, pb);
245         break;
246     case NR_GRADIENT_SPREAD_REFLECT:
247         render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_REFLECT> >(gr, pb);
248         break;
249     case NR_GRADIENT_SPREAD_PAD:
250     default:
251         render_spread<Subtype, Spread<NR_GRADIENT_SPREAD_PAD> >(gr, pb);
252     }
256 /* Linear */
258 namespace {
260 struct Linear {
261 template <typename compose, typename spread>
262 static void render(NRGradientRenderer *gr, NRPixBlock *pb) {
263     NRLGradientRenderer *lgr = static_cast<NRLGradientRenderer *>(gr);
265     int x, y;
266     unsigned char *d;
267     double pos;
268     NRPixBlock spb;
269     int x0, y0, width, height, rs;
271     x0 = pb->area.x0;
272     y0 = pb->area.y0;
273     width = pb->area.x1 - pb->area.x0;
274     height = pb->area.y1 - pb->area.y0;
275     rs = pb->rs;
277     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
278                              0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
279                              (unsigned char *) lgr->vector,
280                              4 * NR_GRADIENT_VECTOR_LENGTH, 0, 0);
282     for (y = 0; y < height; y++) {
283         d = NR_PIXBLOCK_PX(pb) + y * rs;
284         pos = (y + y0 - lgr->y0) * lgr->dy + (0 + x0 - lgr->x0) * lgr->dx;
285         for (x = 0; x < width; x++) {
286             unsigned char const *s=spread::color_at(pos, lgr->vector);
287             compose::compose(pb, d, &spb, s);
288             d += compose::bpp;
289             pos += lgr->dx;
290         }
291     }
293     nr_pixblock_release(&spb);
295 };
299 NRRenderer *
300 nr_lgradient_renderer_setup (NRLGradientRenderer *lgr,
301                              const unsigned char *cv, 
302                              unsigned int spread, 
303                              const NR::Matrix *gs2px,
304                              float x0, float y0,
305                              float x1, float y1)
307         NR::Matrix n2gs, n2px, px2n;
309         lgr->render = &render<Linear>;
311         lgr->vector = cv;
312         lgr->spread = spread;
314         n2gs[0] = x1 - x0;
315         n2gs[1] = y1 - y0;
316         n2gs[2] = y1 - y0;
317         n2gs[3] = x0 - x1;
318         n2gs[4] = x0;
319         n2gs[5] = y0;
321         n2px = n2gs * (*gs2px);
322         px2n = n2px.inverse();
324         lgr->x0 = n2px[4] - 0.5;
325         lgr->y0 = n2px[5] - 0.5;
326         lgr->dx = px2n[0] * NR_GRADIENT_VECTOR_LENGTH;
327         lgr->dy = px2n[2] * NR_GRADIENT_VECTOR_LENGTH;
329         return (NRRenderer *) lgr;
332 /* Radial */
334 /*
335  * The archetype is following
336  *
337  * gx gy - pixel coordinates
338  * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
339  *
340  * (1)  (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
341  * (2)  (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
342  *
343  * (3)   Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
344  * (4)  (gy - fy) / (gx - fx) = D
345  * (5)   Py = D * Px - D * fx + fy
346  *
347  * (6)   D * fx - fy + cy = N
348  * (7)   Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
349  * (8)  (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
350  *
351  * (9)   A = D * D + 1
352  * (10)  B = -2 * (cx + D * N)
353  * (11)  C = cx * cx + N * N - r * r
354  *
355  * (12)  Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
356  */
358 namespace {
360 struct SymmetricRadial {
361 template <typename compose, typename spread>
362 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
364     NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
366     NR::Coord const dx = rgr->px2gs[0];
367     NR::Coord const dy = rgr->px2gs[1];
369     NRPixBlock spb;
370     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
371                              0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
372                              (unsigned char *) rgr->vector,
373                              4 * NR_GRADIENT_VECTOR_LENGTH,
374                              0, 0);
376     for (int y = pb->area.y0; y < pb->area.y1; y++) {
377         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
378         NR::Coord gx = rgr->px2gs[0] * pb->area.x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
379         NR::Coord gy = rgr->px2gs[1] * pb->area.x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
380         for (int x = pb->area.x0; x < pb->area.x1; x++) {
381             NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
382             unsigned char const *s=spread::color_at(pos, rgr->vector);
383             compose::compose(pb, d, &spb, s);
384             d += compose::bpp;
385             gx += dx;
386             gy += dy;
387         }
388     }
390     nr_pixblock_release(&spb);
392 };
394 struct Radial {
395 template <typename compose, typename spread>
396 static void render(NRGradientRenderer *gr, NRPixBlock *pb)
398     NRRGradientRenderer *rgr = static_cast<NRRGradientRenderer *>(gr);
399     int const x0 = pb->area.x0;
400     int const y0 = pb->area.y0;
401     int const x1 = pb->area.x1;
402     int const y1 = pb->area.y1;
403     int const rs = pb->rs;
405     NRPixBlock spb;
406     nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N,
407                              0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
408                              (unsigned char *) rgr->vector,
409                              4 * NR_GRADIENT_VECTOR_LENGTH,
410                              0, 0);
412     for (int y = y0; y < y1; y++) {
413         unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
414         NR::Coord gx = rgr->px2gs[0] * x0 + rgr->px2gs[2] * y + rgr->px2gs[4];
415         NR::Coord gy = rgr->px2gs[1] * x0 + rgr->px2gs[3] * y + rgr->px2gs[5];
416         NR::Coord const dx = rgr->px2gs[0];
417         NR::Coord const dy = rgr->px2gs[1];
418         for (int x = x0; x < x1; x++) {
419             NR::Coord const gx2 = gx * gx;
420             NR::Coord const gxy2 = gx2 + gy * gy;
421             NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
422             /* INVARIANT: qgx2_4 >= 0.0 */
423             /* qgx2_4 = MAX(qgx2_4, 0.0); */
424             NR::Coord const pxgx = gx + sqrt(qgx2_4);
425             /* We can safely divide by 0 here */
426             /* If we are sure pxgx cannot be -0 */
427             NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
429             unsigned char const *s;
430             if (pos < (1U << 31)) {
431                 s = spread::color_at(pos, rgr->vector);
432             } else {
433                 s = vector_index(NR_GRADIENT_VECTOR_LENGTH - 1, rgr->vector);
434             }
435             
436             compose::compose(pb, d, &spb, s);
437             d += compose::bpp;
439             gx += dx;
440             gy += dy;
441         }
442     }
444     nr_pixblock_release(&spb);
446 };
450 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
452 NRRenderer *
453 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
454                             unsigned char const *cv,
455                             unsigned spread,
456                             NR::Matrix const *gs2px,
457                             float cx, float cy,
458                             float fx, float fy,
459                             float r)
461     rgr->vector = cv;
462     rgr->spread = spread;
464     if (r < NR_EPSILON) {
465         rgr->render = nr_rgradient_render_block_end;
466     } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
467                NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
468         rgr->render = render<SymmetricRadial>;
470         rgr->px2gs = gs2px->inverse();
471         rgr->px2gs[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
472         rgr->px2gs[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
473         rgr->px2gs[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
474         rgr->px2gs[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
475         rgr->px2gs[4] -= cx;
476         rgr->px2gs[5] -= cy;
477         rgr->px2gs[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
478         rgr->px2gs[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
480         rgr->cx = 0.0;
481         rgr->cy = 0.0;
482         rgr->fx = rgr->cx;
483         rgr->fy = rgr->cy;
484         rgr->r = 1.0;
485     } else {
486         rgr->render = render<Radial>;
488         NR::Coord const df = hypot(fx - cx, fy - cy);
489         if (df >= r) {
490             fx = cx + (fx - cx ) * r / (float) df;
491             fy = cy + (fy - cy ) * r / (float) df;
492         }
494         NR::Matrix n2gs;
495         n2gs[0] = cx - fx;
496         n2gs[1] = cy - fy;
497         n2gs[2] = cy - fy;
498         n2gs[3] = fx - cx;
499         n2gs[4] = fx;
500         n2gs[5] = fy;
502         NR::Matrix n2px;
503         n2px = n2gs * (*gs2px);
504         rgr->px2gs = n2px.inverse();
506         rgr->cx = 1.0;
507         rgr->cy = 0.0;
508         rgr->fx = 0.0;
509         rgr->fy = 0.0;
510         rgr->r = r / (float) hypot(fx - cx, fy - cy);
511         rgr->C = 1.0F - rgr->r * rgr->r;
512         /* INVARIANT: C < 0 */
513         rgr->C = MIN(rgr->C, -NR_EPSILON);
514     }
516     return (NRRenderer *) rgr;
519 static void
520 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
522     unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
524     nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
527 /*
528   Local Variables:
529   mode:c++
530   c-file-style:"stroustrup"
531   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
532   indent-tabs-mode:nil
533   fill-column:99
534   End:
535 */
536 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :