1 #define __NR_GRADIENT_C__
3 /*
4 * Pixel buffer rendering library
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * This code is in public domain
10 */
12 #include <libnr/nr-pixops.h>
13 #include <libnr/nr-pixblock-pixel.h>
14 #include <libnr/nr-blit.h>
15 #include <libnr/nr-gradient.h>
17 #define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
18 #define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
20 /* Radial */
22 static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
23 static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
24 static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
25 static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
26 static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
28 NRRenderer *
29 nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
30 unsigned char const *cv,
31 unsigned spread,
32 NRMatrix const *gs2px,
33 float cx, float cy,
34 float fx, float fy,
35 float r)
36 {
37 rgr->vector = cv;
38 rgr->spread = spread;
40 if (r < NR_EPSILON) {
41 rgr->renderer.render = nr_rgradient_render_block_end;
42 } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
43 NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
44 rgr->renderer.render = nr_rgradient_render_block_symmetric;
46 nr_matrix_invert(&rgr->px2gs, gs2px);
47 rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
48 rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
49 rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
50 rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
51 rgr->px2gs.c[4] -= cx;
52 rgr->px2gs.c[5] -= cy;
53 rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
54 rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
56 rgr->cx = 0.0;
57 rgr->cy = 0.0;
58 rgr->fx = rgr->cx;
59 rgr->fy = rgr->cy;
60 rgr->r = 1.0;
61 } else {
62 rgr->renderer.render = nr_rgradient_render_block_optimized;
64 NR::Coord const df = hypot(fx - cx, fy - cy);
65 if (df >= r) {
66 fx = cx + (fx - cx ) * r / (float) df;
67 fy = cy + (fy - cy ) * r / (float) df;
68 }
70 NRMatrix n2gs;
71 n2gs.c[0] = cx - fx;
72 n2gs.c[1] = cy - fy;
73 n2gs.c[2] = cy - fy;
74 n2gs.c[3] = fx - cx;
75 n2gs.c[4] = fx;
76 n2gs.c[5] = fy;
78 NRMatrix n2px;
79 nr_matrix_multiply(&n2px, &n2gs, gs2px);
80 nr_matrix_invert(&rgr->px2gs, &n2px);
82 rgr->cx = 1.0;
83 rgr->cy = 0.0;
84 rgr->fx = 0.0;
85 rgr->fy = 0.0;
86 rgr->r = r / (float) hypot(fx - cx, fy - cy);
87 rgr->C = 1.0F - rgr->r * rgr->r;
88 /* INVARIANT: C < 0 */
89 rgr->C = MIN(rgr->C, -NR_EPSILON);
90 }
92 return (NRRenderer *) rgr;
93 }
95 static void
96 nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
97 {
98 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
99 nr_rgradient_render_generic_symmetric(rgr, pb);
100 }
102 static void
103 nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
104 {
105 NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
106 nr_rgradient_render_generic_optimized(rgr, pb);
107 }
109 static void
110 nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
111 {
112 unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
114 nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
115 }
117 /*
118 * The archetype is following
119 *
120 * gx gy - pixel coordinates
121 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
122 *
123 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
124 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
125 *
126 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
127 * (4) (gy - fy) / (gx - fx) = D
128 * (5) Py = D * Px - D * fx + fy
129 *
130 * (6) D * fx - fy + cy = N
131 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
132 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
133 *
134 * (9) A = D * D + 1
135 * (10) B = -2 * (cx + D * N)
136 * (11) C = cx * cx + N * N - r * r
137 *
138 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
139 */
141 static void
142 nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
143 {
144 NR::Coord const dx = rgr->px2gs.c[0];
145 NR::Coord const dy = rgr->px2gs.c[1];
147 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
148 for (int y = pb->area.y0; y < pb->area.y1; y++) {
149 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
150 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
151 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
152 for (int x = pb->area.x0; x < pb->area.x1; x++) {
153 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
154 int idx;
155 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
156 idx = (int) ((long long) pos & NRG_2MASK);
157 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
158 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
159 idx = (int) ((long long) pos & NRG_MASK);
160 } else {
161 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
162 }
163 unsigned char const *s = rgr->vector + 4 * idx;
164 d[0] = NR_COMPOSENPP_1111(s[0], s[3], d[0]);
165 d[1] = NR_COMPOSENPP_1111(s[1], s[3], d[1]);
166 d[2] = NR_COMPOSENPP_1111(s[2], s[3], d[2]);
167 d[3] = NR_COMPOSEA_111(s[3], d[3]);
168 d += 4;
169 gx += dx;
170 gy += dy;
171 }
172 }
173 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
174 for (int y = pb->area.y0; y < pb->area.y1; y++) {
175 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
176 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
177 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
178 for (int x = pb->area.x0; x < pb->area.x1; x++) {
179 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
180 int idx;
181 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
182 idx = (int) ((long long) pos & NRG_2MASK);
183 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
184 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
185 idx = (int) ((long long) pos & NRG_MASK);
186 } else {
187 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
188 }
189 unsigned char const *s = rgr->vector + 4 * idx;
190 if (s[3] == 255) {
191 d[0] = s[0];
192 d[1] = s[1];
193 d[2] = s[2];
194 d[3] = 255;
195 } else if (s[3] != 0) {
196 unsigned ca = NR_COMPOSEA_112(s[3], d[3]);
197 d[0] = NR_COMPOSENNN_111121(s[0], s[3], d[0], d[3], ca);
198 d[1] = NR_COMPOSENNN_111121(s[1], s[3], d[1], d[3], ca);
199 d[2] = NR_COMPOSENNN_111121(s[2], s[3], d[2], d[3], ca);
200 d[3] = NR_NORMALIZE_21(ca);
201 }
202 d += 4;
203 gx += dx;
204 gy += dy;
205 }
206 }
207 } else {
208 NRPixBlock spb;
209 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
210 (unsigned char *) rgr->vector,
211 4 * NR_GRADIENT_VECTOR_LENGTH,
212 0, 0);
213 int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
214 ? 1
215 : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
216 ? 3
217 : 4 );
219 for (int y = pb->area.y0; y < pb->area.y1; y++) {
220 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
221 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
222 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
223 for (int x = pb->area.x0; x < pb->area.x1; x++) {
224 NR::Coord const pos = sqrt(((gx*gx) + (gy*gy)));
225 int idx;
226 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
227 idx = (int) ((long long) pos & NRG_2MASK);
228 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
229 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
230 idx = (int) ((long long) pos & NRG_MASK);
231 } else {
232 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
233 }
234 unsigned char const *s = rgr->vector + 4 * idx;
235 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
236 d += bpp;
237 gx += dx;
238 gy += dy;
239 }
240 }
242 nr_pixblock_release(&spb);
243 }
244 }
246 static void
247 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
248 {
249 int const x0 = pb->area.x0;
250 int const y0 = pb->area.y0;
251 int const x1 = pb->area.x1;
252 int const y1 = pb->area.y1;
253 int const rs = pb->rs;
255 NRPixBlock spb;
256 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
257 (unsigned char *) rgr->vector,
258 4 * NR_GRADIENT_VECTOR_LENGTH,
259 0, 0);
260 int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
261 ? 1
262 : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
263 ? 3
264 : 4 );
266 for (int y = y0; y < y1; y++) {
267 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
268 NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
269 NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
270 NR::Coord const dx = rgr->px2gs.c[0];
271 NR::Coord const dy = rgr->px2gs.c[1];
272 for (int x = x0; x < x1; x++) {
273 NR::Coord const gx2 = gx * gx;
274 NR::Coord const gxy2 = gx2 + gy * gy;
275 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
276 /* INVARIANT: qgx2_4 >= 0.0 */
277 /* qgx2_4 = MAX(qgx2_4, 0.0); */
278 NR::Coord const pxgx = gx + sqrt(qgx2_4);
279 /* We can safely divide by 0 here */
280 /* If we are sure pxgx cannot be -0 */
281 NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
282 int idx;
283 if (pos < (1U << 31)) {
284 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
285 idx = (int) ((long long) pos & NRG_2MASK);
286 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
287 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
288 idx = (int) ((long long) pos & NRG_MASK);
289 } else {
290 idx = (int) CLAMP(pos, 0, (double) (NR_GRADIENT_VECTOR_LENGTH - 1));
291 }
292 } else {
293 idx = NR_GRADIENT_VECTOR_LENGTH - 1;
294 }
295 unsigned char const *s = rgr->vector + 4 * idx;
296 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
297 d += bpp;
299 gx += dx;
300 gy += dy;
301 }
302 }
304 nr_pixblock_release(&spb);
305 }
308 /*
309 Local Variables:
310 mode:c++
311 c-file-style:"stroustrup"
312 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
313 indent-tabs-mode:nil
314 fill-column:99
315 End:
316 */
317 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :