9e9360147298475572fbc0ebe0c7a2514a5a2b43
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 float fast_sqrt (float in)
118 {
119 float x = in;
120 float xhalf = 0.5f*x;
121 int i = *(int*)&x;
122 i = 0x5f3759df - (i >> 1); // This line hides a LOT of math!
123 x = *(float*)&i;
124 x = x*(1.5f - xhalf*x*x); // repeat this statement for a better approximation
125 return x * in;
126 }
128 /*
129 * The archetype is following
130 *
131 * gx gy - pixel coordinates
132 * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
133 *
134 * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
135 * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
136 *
137 * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
138 * (4) (gy - fy) / (gx - fx) = D
139 * (5) Py = D * Px - D * fx + fy
140 *
141 * (6) D * fx - fy + cy = N
142 * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
143 * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
144 *
145 * (9) A = D * D + 1
146 * (10) B = -2 * (cx + D * N)
147 * (11) C = cx * cx + N * N - r * r
148 *
149 * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
150 */
152 static void
153 nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
154 {
155 NR::Coord const dx = rgr->px2gs.c[0];
156 NR::Coord const dy = rgr->px2gs.c[1];
158 if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
159 for (int y = pb->area.y0; y < pb->area.y1; y++) {
160 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
161 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
162 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
163 for (int x = pb->area.x0; x < pb->area.x1; x++) {
164 float const pos = fast_sqrt((gx*gx) + (gy*gy));
165 int idx;
166 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
167 idx = (int) ((long long) pos & NRG_2MASK);
168 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
169 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
170 idx = (int) ((long long) pos & NRG_MASK);
171 } else {
172 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
173 }
174 unsigned char const *s = rgr->vector + 4 * idx;
175 d[0] = NR_COMPOSENPP(s[0], s[3], d[0], d[3]);
176 d[1] = NR_COMPOSENPP(s[1], s[3], d[1], d[3]);
177 d[2] = NR_COMPOSENPP(s[2], s[3], d[2], d[3]);
178 d[3] = (255*255 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
179 d += 4;
180 gx += dx;
181 gy += dy;
182 }
183 }
184 } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
185 for (int y = pb->area.y0; y < pb->area.y1; y++) {
186 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
187 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
188 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
189 for (int x = pb->area.x0; x < pb->area.x1; x++) {
190 float pos = fast_sqrt((gx*gx) + (gy*gy));
191 int idx;
192 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
193 idx = (int) ((long long) pos & NRG_2MASK);
194 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
195 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
196 idx = (int) ((long long) pos & NRG_MASK);
197 } else {
198 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
199 }
200 unsigned char const *s = rgr->vector + 4 * idx;
201 if (s[3] == 255) {
202 d[0] = s[0];
203 d[1] = s[1];
204 d[2] = s[2];
205 d[3] = 255;
206 } else if (s[3] != 0) {
207 unsigned ca = 255*255 - (255 - s[3]) * (255 - d[3]);
208 d[0] = NR_COMPOSENNN_A7(s[0], s[3], d[0], d[3], ca);
209 d[1] = NR_COMPOSENNN_A7(s[1], s[3], d[1], d[3], ca);
210 d[2] = NR_COMPOSENNN_A7(s[2], s[3], d[2], d[3], ca);
211 d[3] = (ca + 127) / 255;
212 }
213 d += 4;
214 gx += dx;
215 gy += dy;
216 }
217 }
218 } else {
219 NRPixBlock spb;
220 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
221 (unsigned char *) rgr->vector,
222 4 * NR_GRADIENT_VECTOR_LENGTH,
223 0, 0);
224 int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
225 ? 1
226 : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
227 ? 3
228 : 4 );
230 for (int y = pb->area.y0; y < pb->area.y1; y++) {
231 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
232 NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
233 NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
234 for (int x = pb->area.x0; x < pb->area.x1; x++) {
235 NR::Coord const pos = fast_sqrt((gx*gx) + (gy*gy));
236 int idx;
237 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
238 idx = (int) ((long long) pos & NRG_2MASK);
239 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
240 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
241 idx = (int) ((long long) pos & NRG_MASK);
242 } else {
243 idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
244 }
245 unsigned char const *s = rgr->vector + 4 * idx;
246 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
247 d += bpp;
248 gx += dx;
249 gy += dy;
250 }
251 }
253 nr_pixblock_release(&spb);
254 }
255 }
257 static void
258 nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
259 {
260 int const x0 = pb->area.x0;
261 int const y0 = pb->area.y0;
262 int const x1 = pb->area.x1;
263 int const y1 = pb->area.y1;
264 int const rs = pb->rs;
266 NRPixBlock spb;
267 nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
268 (unsigned char *) rgr->vector,
269 4 * NR_GRADIENT_VECTOR_LENGTH,
270 0, 0);
271 int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
272 ? 1
273 : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
274 ? 3
275 : 4 );
277 for (int y = y0; y < y1; y++) {
278 unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
279 NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
280 NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
281 NR::Coord const dx = rgr->px2gs.c[0];
282 NR::Coord const dy = rgr->px2gs.c[1];
283 for (int x = x0; x < x1; x++) {
284 NR::Coord const gx2 = gx * gx;
285 NR::Coord const gxy2 = gx2 + gy * gy;
286 NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
287 /* INVARIANT: qgx2_4 >= 0.0 */
288 /* qgx2_4 = MAX(qgx2_4, 0.0); */
289 NR::Coord const pxgx = gx + sqrt(qgx2_4);
290 /* We can safely divide by 0 here */
291 /* If we are sure pxgx cannot be -0 */
292 NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
293 int idx;
294 if (pos < (1U << 31)) {
295 if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
296 idx = (int) ((long long) pos & NRG_2MASK);
297 if (idx > NRG_MASK) idx = NRG_2MASK - idx;
298 } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
299 idx = (int) ((long long) pos & NRG_MASK);
300 } else {
301 idx = (int) CLAMP(pos, 0, (double) (NR_GRADIENT_VECTOR_LENGTH - 1));
302 }
303 } else {
304 idx = NR_GRADIENT_VECTOR_LENGTH - 1;
305 }
306 unsigned char const *s = rgr->vector + 4 * idx;
307 nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
308 d += bpp;
310 gx += dx;
311 gy += dy;
312 }
313 }
315 nr_pixblock_release(&spb);
316 }
319 /*
320 Local Variables:
321 mode:c++
322 c-file-style:"stroustrup"
323 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
324 indent-tabs-mode:nil
325 fill-column:99
326 End:
327 */
328 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :