1 | /* |
---|
2 | * art_render_gradient.c: Gradient image source for modular rendering. |
---|
3 | * |
---|
4 | * Libart_LGPL - library of basic graphic primitives |
---|
5 | * Copyright (C) 2000 Raph Levien |
---|
6 | * |
---|
7 | * This library is free software; you can redistribute it and/or |
---|
8 | * modify it under the terms of the GNU Library General Public |
---|
9 | * License as published by the Free Software Foundation; either |
---|
10 | * version 2 of the License, or (at your option) any later version. |
---|
11 | * |
---|
12 | * This library is distributed in the hope that it will be useful, |
---|
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | * Library General Public License for more details. |
---|
16 | * |
---|
17 | * You should have received a copy of the GNU Library General Public |
---|
18 | * License along with this library; if not, write to the |
---|
19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
20 | * Boston, MA 02111-1307, USA. |
---|
21 | * |
---|
22 | * Authors: Raph Levien <raph@acm.org> |
---|
23 | * Alexander Larsson <alla@lysator.liu.se> |
---|
24 | */ |
---|
25 | |
---|
26 | #include "config.h" |
---|
27 | #include "art_render_gradient.h" |
---|
28 | |
---|
29 | #include <math.h> |
---|
30 | #include <stdlib.h> |
---|
31 | #include <stdio.h> |
---|
32 | #include <string.h> |
---|
33 | #include <assert.h> |
---|
34 | |
---|
35 | /* Hack to find out how to define alloca on different platforms. |
---|
36 | * Modified version of glib/galloca.h. |
---|
37 | */ |
---|
38 | |
---|
39 | #ifdef __GNUC__ |
---|
40 | /* GCC does the right thing */ |
---|
41 | # undef alloca |
---|
42 | # define alloca(size) __builtin_alloca (size) |
---|
43 | #elif defined (HAVE_ALLOCA_H) |
---|
44 | /* a native and working alloca.h is there */ |
---|
45 | # include <alloca.h> |
---|
46 | #else /* !__GNUC__ && !HAVE_ALLOCA_H */ |
---|
47 | # ifdef _MSC_VER |
---|
48 | # include <malloc.h> |
---|
49 | # define alloca _alloca |
---|
50 | # else /* !_MSC_VER */ |
---|
51 | # ifdef _AIX |
---|
52 | #pragma alloca |
---|
53 | # else /* !_AIX */ |
---|
54 | # ifndef alloca /* predefined by HP cc +Olibcalls */ |
---|
55 | char *alloca (); |
---|
56 | # endif /* !alloca */ |
---|
57 | # endif /* !_AIX */ |
---|
58 | # endif /* !_MSC_VER */ |
---|
59 | #endif /* !__GNUC__ && !HAVE_ALLOCA_H */ |
---|
60 | |
---|
61 | #undef DEBUG_SPEW |
---|
62 | |
---|
63 | typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin; |
---|
64 | typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad; |
---|
65 | |
---|
66 | /* The stops will be copied right after this structure */ |
---|
67 | struct _ArtImageSourceGradLin { |
---|
68 | ArtImageSource super; |
---|
69 | ArtGradientLinear gradient; |
---|
70 | ArtGradientStop stops[1]; |
---|
71 | }; |
---|
72 | |
---|
73 | /* The stops will be copied right after this structure */ |
---|
74 | struct _ArtImageSourceGradRad { |
---|
75 | ArtImageSource super; |
---|
76 | ArtGradientRadial gradient; |
---|
77 | double a; |
---|
78 | ArtGradientStop stops[1]; |
---|
79 | }; |
---|
80 | |
---|
81 | #define EPSILON 1e-6 |
---|
82 | |
---|
83 | #ifndef MAX |
---|
84 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) |
---|
85 | #endif /* MAX */ |
---|
86 | |
---|
87 | #ifndef MIN |
---|
88 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
---|
89 | #endif /* MIN */ |
---|
90 | |
---|
91 | static void |
---|
92 | art_rgba_gradient_run (art_u8 *buf, |
---|
93 | art_u8 *color1, |
---|
94 | art_u8 *color2, |
---|
95 | int len) |
---|
96 | { |
---|
97 | int i; |
---|
98 | int r, g, b, a; |
---|
99 | int dr, dg, db, da; |
---|
100 | |
---|
101 | #ifdef DEBUG_SPEW |
---|
102 | printf ("gradient run from %3d %3d %3d %3d to %3d %3d %3d %3d in %d pixels\n", |
---|
103 | color1[0], color1[1], color1[2], color1[3], |
---|
104 | color2[0], color2[1], color2[2], color2[3], |
---|
105 | len); |
---|
106 | #endif |
---|
107 | |
---|
108 | r = (color1[0] << 16) + 0x8000; |
---|
109 | g = (color1[1] << 16) + 0x8000; |
---|
110 | b = (color1[2] << 16) + 0x8000; |
---|
111 | a = (color1[3] << 16) + 0x8000; |
---|
112 | dr = ((color2[0] - color1[0]) << 16) / len; |
---|
113 | dg = ((color2[1] - color1[1]) << 16) / len; |
---|
114 | db = ((color2[2] - color1[2]) << 16) / len; |
---|
115 | da = ((color2[3] - color1[3]) << 16) / len; |
---|
116 | |
---|
117 | for (i = 0; i < len; i++) |
---|
118 | { |
---|
119 | *buf++ = (r>>16); |
---|
120 | *buf++ = (g>>16); |
---|
121 | *buf++ = (b>>16); |
---|
122 | *buf++ = (a>>16); |
---|
123 | |
---|
124 | r += dr; |
---|
125 | g += dg; |
---|
126 | b += db; |
---|
127 | a += da; |
---|
128 | } |
---|
129 | } |
---|
130 | |
---|
131 | static void |
---|
132 | calc_color_at (ArtGradientStop *stops, |
---|
133 | int n_stops, |
---|
134 | ArtGradientSpread spread, |
---|
135 | double offset, |
---|
136 | double offset_fraction, |
---|
137 | int favor_start, |
---|
138 | int ix, |
---|
139 | art_u8 *color) |
---|
140 | { |
---|
141 | double off0, off1; |
---|
142 | int j; |
---|
143 | |
---|
144 | if (spread == ART_GRADIENT_PAD) |
---|
145 | { |
---|
146 | if (offset < 0.0) |
---|
147 | { |
---|
148 | color[0] = ART_PIX_8_FROM_MAX (stops[0].color[0]); |
---|
149 | color[1] = ART_PIX_8_FROM_MAX (stops[0].color[1]); |
---|
150 | color[2] = ART_PIX_8_FROM_MAX (stops[0].color[2]); |
---|
151 | color[3] = ART_PIX_8_FROM_MAX (stops[0].color[3]); |
---|
152 | return; |
---|
153 | } |
---|
154 | if (offset >= 1.0) |
---|
155 | { |
---|
156 | color[0] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[0]); |
---|
157 | color[1] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[1]); |
---|
158 | color[2] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[2]); |
---|
159 | color[3] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[3]); |
---|
160 | return; |
---|
161 | } |
---|
162 | } |
---|
163 | |
---|
164 | if (ix > 0 && ix < n_stops) |
---|
165 | { |
---|
166 | off0 = stops[ix - 1].offset; |
---|
167 | off1 = stops[ix].offset; |
---|
168 | if (fabs (off1 - off0) > EPSILON) |
---|
169 | { |
---|
170 | double interp; |
---|
171 | double o; |
---|
172 | o = offset_fraction; |
---|
173 | |
---|
174 | if ((fabs (o) < EPSILON) && (!favor_start)) |
---|
175 | o = 1.0; |
---|
176 | else if ((fabs (o-1.0) < EPSILON) && (favor_start)) |
---|
177 | o = 0.0; |
---|
178 | |
---|
179 | /* |
---|
180 | if (offset_fraction == 0.0 && !favor_start) |
---|
181 | offset_fraction = 1.0; |
---|
182 | */ |
---|
183 | |
---|
184 | interp = (o - off0) / (off1 - off0); |
---|
185 | for (j = 0; j < 4; j++) |
---|
186 | { |
---|
187 | int z0, z1; |
---|
188 | int z; |
---|
189 | z0 = stops[ix - 1].color[j]; |
---|
190 | z1 = stops[ix].color[j]; |
---|
191 | z = floor (z0 + (z1 - z0) * interp + 0.5); |
---|
192 | color[j] = ART_PIX_8_FROM_MAX (z); |
---|
193 | } |
---|
194 | return; |
---|
195 | } |
---|
196 | /* If offsets are too close to safely do the division, just |
---|
197 | pick the ix color. */ |
---|
198 | color[0] = ART_PIX_8_FROM_MAX (stops[ix].color[0]); |
---|
199 | color[1] = ART_PIX_8_FROM_MAX (stops[ix].color[1]); |
---|
200 | color[2] = ART_PIX_8_FROM_MAX (stops[ix].color[2]); |
---|
201 | color[3] = ART_PIX_8_FROM_MAX (stops[ix].color[3]); |
---|
202 | return; |
---|
203 | } |
---|
204 | |
---|
205 | printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix); |
---|
206 | assert (0); |
---|
207 | } |
---|
208 | |
---|
209 | static void |
---|
210 | art_render_gradient_linear_render_8 (ArtRenderCallback *self, |
---|
211 | ArtRender *render, |
---|
212 | art_u8 *dest, int y) |
---|
213 | { |
---|
214 | ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; |
---|
215 | const ArtGradientLinear *gradient = &(z->gradient); |
---|
216 | int i; |
---|
217 | int width = render->x1 - render->x0; |
---|
218 | int len; |
---|
219 | double offset, d_offset; |
---|
220 | double offset_fraction; |
---|
221 | int next_stop; |
---|
222 | int ix; |
---|
223 | art_u8 color1[4], color2[4]; |
---|
224 | int n_stops = gradient->n_stops; |
---|
225 | int extra_stops; |
---|
226 | ArtGradientStop *stops = gradient->stops; |
---|
227 | ArtGradientStop *tmp_stops; |
---|
228 | art_u8 *bufp = render->image_buf; |
---|
229 | ArtGradientSpread spread = gradient->spread; |
---|
230 | |
---|
231 | #ifdef DEBUG_SPEW |
---|
232 | printf ("x1: %d, x2: %d, y: %d\n", render->x0, render->x1, y); |
---|
233 | printf ("spread: %d, stops:", gradient->spread); |
---|
234 | for (i=0;i<n_stops;i++) |
---|
235 | { |
---|
236 | printf ("%f, ", gradient->stops[i].offset); |
---|
237 | } |
---|
238 | printf ("\n"); |
---|
239 | printf ("a: %f, b: %f, c: %f\n", gradient->a, gradient->b, gradient->c); |
---|
240 | #endif |
---|
241 | |
---|
242 | offset = render->x0 * gradient->a + y * gradient->b + gradient->c; |
---|
243 | d_offset = gradient->a; |
---|
244 | |
---|
245 | /* We need to force the gradient to extend the whole 0..1 segment, |
---|
246 | because the rest of the code doesn't handle partial gradients |
---|
247 | correctly */ |
---|
248 | if ((gradient->stops[0].offset > EPSILON /* == 0.0 */) || |
---|
249 | (gradient->stops[n_stops-1].offset < (1.0 - EPSILON))) |
---|
250 | { |
---|
251 | extra_stops = 0; |
---|
252 | tmp_stops = stops = alloca (sizeof (ArtGradientStop) * (n_stops + 2)); |
---|
253 | if (gradient->stops[0].offset > EPSILON /* 0.0 */) |
---|
254 | { |
---|
255 | memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop)); |
---|
256 | tmp_stops[0].offset = 0.0; |
---|
257 | tmp_stops += 1; |
---|
258 | extra_stops++; |
---|
259 | } |
---|
260 | memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop) * n_stops); |
---|
261 | if (gradient->stops[n_stops-1].offset < (1.0 - EPSILON)) |
---|
262 | { |
---|
263 | tmp_stops += n_stops; |
---|
264 | memcpy (tmp_stops, &gradient->stops[n_stops-1], sizeof (ArtGradientStop)); |
---|
265 | tmp_stops[0].offset = 1.0; |
---|
266 | extra_stops++; |
---|
267 | } |
---|
268 | n_stops += extra_stops; |
---|
269 | |
---|
270 | |
---|
271 | #ifdef DEBUG_SPEW |
---|
272 | printf ("start/stop modified stops:"); |
---|
273 | for (i=0;i<n_stops;i++) |
---|
274 | { |
---|
275 | printf ("%f, ", stops[i].offset); |
---|
276 | } |
---|
277 | printf ("\n"); |
---|
278 | #endif |
---|
279 | |
---|
280 | } |
---|
281 | |
---|
282 | if (spread == ART_GRADIENT_REFLECT) |
---|
283 | { |
---|
284 | tmp_stops = stops; |
---|
285 | stops = alloca (sizeof (ArtGradientStop) * n_stops * 2); |
---|
286 | memcpy (stops, tmp_stops, sizeof (ArtGradientStop) * n_stops); |
---|
287 | |
---|
288 | for (i = 0; i< n_stops; i++) |
---|
289 | { |
---|
290 | stops[n_stops * 2 - 1 - i].offset = (1.0 - stops[i].offset / 2.0); |
---|
291 | memcpy (stops[n_stops * 2 - 1 - i].color, stops[i].color, sizeof (stops[i].color)); |
---|
292 | stops[i].offset = stops[i].offset / 2.0; |
---|
293 | } |
---|
294 | |
---|
295 | spread = ART_GRADIENT_REPEAT; |
---|
296 | offset = offset / 2.0; |
---|
297 | d_offset = d_offset / 2.0; |
---|
298 | |
---|
299 | n_stops = 2 * n_stops; |
---|
300 | |
---|
301 | #ifdef DEBUG_SPEW |
---|
302 | printf ("reflect modified stops:"); |
---|
303 | for (i=0;i<n_stops;i++) |
---|
304 | { |
---|
305 | printf ("%f, ", stops[i].offset); |
---|
306 | } |
---|
307 | printf ("\n"); |
---|
308 | #endif |
---|
309 | } |
---|
310 | |
---|
311 | offset_fraction = offset - floor (offset); |
---|
312 | #ifdef DEBUG_SPEW |
---|
313 | printf ("inital offset: %f, fraction: %f d_offset: %f\n", offset, offset_fraction, d_offset); |
---|
314 | #endif |
---|
315 | /* ix is selected so that offset_fraction is |
---|
316 | stops[ix-1] <= offset_fraction <= stops[ix] |
---|
317 | If offset_fraction is equal to one of the edges, ix |
---|
318 | is selected so the the section of the line extending |
---|
319 | in the same direction as d_offset is between ix-1 and ix. |
---|
320 | */ |
---|
321 | for (ix = 0; ix < n_stops; ix++) |
---|
322 | if (stops[ix].offset > offset_fraction || |
---|
323 | (d_offset < 0.0 && fabs (stops[ix].offset - offset_fraction) < EPSILON)) |
---|
324 | break; |
---|
325 | if (ix == 0) |
---|
326 | ix = n_stops - 1; |
---|
327 | else if (ix == n_stops) |
---|
328 | ix = n_stops - 1; |
---|
329 | |
---|
330 | #ifdef DEBUG_SPEW |
---|
331 | printf ("Initial ix: %d\n", ix); |
---|
332 | #endif |
---|
333 | |
---|
334 | assert (ix > 0); |
---|
335 | assert (ix < n_stops); |
---|
336 | assert ((stops[ix-1].offset <= offset_fraction + EPSILON) || |
---|
337 | ((stops[ix].offset > (1.0 - EPSILON)) && (offset_fraction < EPSILON /* == 0.0*/))); |
---|
338 | assert (offset_fraction <= stops[ix].offset); |
---|
339 | /* FIXME: These asserts may be broken, it is for now |
---|
340 | safer to not use them. Should be fixed! |
---|
341 | See bug #121850 |
---|
342 | assert ((offset_fraction != stops[ix-1].offset) || |
---|
343 | (d_offset >= 0.0)); |
---|
344 | assert ((offset_fraction != stops[ix].offset) || |
---|
345 | (d_offset <= 0.0)); |
---|
346 | */ |
---|
347 | |
---|
348 | while (width > 0) |
---|
349 | { |
---|
350 | #ifdef DEBUG_SPEW |
---|
351 | printf ("ix: %d\n", ix); |
---|
352 | printf ("start offset: %f\n", offset); |
---|
353 | #endif |
---|
354 | calc_color_at (stops, n_stops, |
---|
355 | spread, |
---|
356 | offset, |
---|
357 | offset_fraction, |
---|
358 | (d_offset > -EPSILON), |
---|
359 | ix, |
---|
360 | color1); |
---|
361 | |
---|
362 | if (d_offset > 0) |
---|
363 | next_stop = ix; |
---|
364 | else |
---|
365 | next_stop = ix-1; |
---|
366 | |
---|
367 | #ifdef DEBUG_SPEW |
---|
368 | printf ("next_stop: %d\n", next_stop); |
---|
369 | #endif |
---|
370 | if (fabs (d_offset) > EPSILON) |
---|
371 | { |
---|
372 | double o; |
---|
373 | o = offset_fraction; |
---|
374 | |
---|
375 | if ((fabs (o) <= EPSILON) && (ix == n_stops - 1)) |
---|
376 | o = 1.0; |
---|
377 | else if ((fabs (o-1.0) <= EPSILON) && (ix == 1)) |
---|
378 | o = 0.0; |
---|
379 | |
---|
380 | #ifdef DEBUG_SPEW |
---|
381 | printf ("o: %f\n", o); |
---|
382 | #endif |
---|
383 | len = (int)floor (fabs ((stops[next_stop].offset - o) / d_offset)) + 1; |
---|
384 | len = MAX (len, 0); |
---|
385 | len = MIN (len, width); |
---|
386 | } |
---|
387 | else |
---|
388 | { |
---|
389 | len = width; |
---|
390 | } |
---|
391 | #ifdef DEBUG_SPEW |
---|
392 | printf ("len: %d\n", len); |
---|
393 | #endif |
---|
394 | if (len > 0) |
---|
395 | { |
---|
396 | offset = offset + (len-1) * d_offset; |
---|
397 | offset_fraction = offset - floor (offset); |
---|
398 | #ifdef DEBUG_SPEW |
---|
399 | printf ("end offset: %f, fraction: %f\n", offset, offset_fraction); |
---|
400 | #endif |
---|
401 | calc_color_at (stops, n_stops, |
---|
402 | spread, |
---|
403 | offset, |
---|
404 | offset_fraction, |
---|
405 | (d_offset < EPSILON), |
---|
406 | ix, |
---|
407 | color2); |
---|
408 | |
---|
409 | art_rgba_gradient_run (bufp, |
---|
410 | color1, |
---|
411 | color2, |
---|
412 | len); |
---|
413 | offset += d_offset; |
---|
414 | offset_fraction = offset - floor (offset); |
---|
415 | } |
---|
416 | |
---|
417 | if (d_offset > 0) |
---|
418 | { |
---|
419 | do |
---|
420 | { |
---|
421 | ix++; |
---|
422 | if (ix == n_stops) |
---|
423 | ix = 1; |
---|
424 | /* Note: offset_fraction can actually be one here on x86 machines that |
---|
425 | does calculations with extended precision, but later rounds to 64bit. |
---|
426 | This happens if the 80bit offset_fraction is larger than the |
---|
427 | largest 64bit double that is less than one. |
---|
428 | */ |
---|
429 | } |
---|
430 | while (!((stops[ix-1].offset <= offset_fraction && |
---|
431 | offset_fraction < stops[ix].offset) || |
---|
432 | (ix == 1 && offset_fraction > (1.0 - EPSILON)))); |
---|
433 | } |
---|
434 | else |
---|
435 | { |
---|
436 | do |
---|
437 | { |
---|
438 | ix--; |
---|
439 | if (ix == 0) |
---|
440 | ix = n_stops - 1; |
---|
441 | } |
---|
442 | while (!((stops[ix-1].offset < offset_fraction && |
---|
443 | offset_fraction <= stops[ix].offset) || |
---|
444 | (ix == n_stops - 1 && offset_fraction < EPSILON /* == 0.0*/))); |
---|
445 | } |
---|
446 | |
---|
447 | bufp += 4*len; |
---|
448 | width -= len; |
---|
449 | } |
---|
450 | } |
---|
451 | |
---|
452 | |
---|
453 | /** |
---|
454 | * art_render_gradient_setpix: Set a gradient pixel. |
---|
455 | * @render: The render object. |
---|
456 | * @dst: Pointer to destination (where to store pixel). |
---|
457 | * @n_stops: Number of stops in @stops. |
---|
458 | * @stops: The stops for the gradient. |
---|
459 | * @offset: The offset. |
---|
460 | * |
---|
461 | * @n_stops must be > 0. |
---|
462 | * |
---|
463 | * Sets a gradient pixel, storing it at @dst. |
---|
464 | **/ |
---|
465 | static void |
---|
466 | art_render_gradient_setpix (ArtRender *render, |
---|
467 | art_u8 *dst, |
---|
468 | int n_stops, ArtGradientStop *stops, |
---|
469 | double offset) |
---|
470 | { |
---|
471 | int ix; |
---|
472 | int j; |
---|
473 | double off0, off1; |
---|
474 | int n_ch = render->n_chan + 1; |
---|
475 | |
---|
476 | for (ix = 0; ix < n_stops; ix++) |
---|
477 | if (stops[ix].offset > offset) |
---|
478 | break; |
---|
479 | /* stops[ix - 1].offset < offset < stops[ix].offset */ |
---|
480 | if (ix > 0 && ix < n_stops) |
---|
481 | { |
---|
482 | off0 = stops[ix - 1].offset; |
---|
483 | off1 = stops[ix].offset; |
---|
484 | if (fabs (off1 - off0) > EPSILON) |
---|
485 | { |
---|
486 | double interp; |
---|
487 | |
---|
488 | interp = (offset - off0) / (off1 - off0); |
---|
489 | for (j = 0; j < n_ch; j++) |
---|
490 | { |
---|
491 | int z0, z1; |
---|
492 | int z; |
---|
493 | z0 = stops[ix - 1].color[j]; |
---|
494 | z1 = stops[ix].color[j]; |
---|
495 | z = floor (z0 + (z1 - z0) * interp + 0.5); |
---|
496 | if (render->buf_depth == 8) |
---|
497 | dst[j] = ART_PIX_8_FROM_MAX (z); |
---|
498 | else /* (render->buf_depth == 16) */ |
---|
499 | ((art_u16 *)dst)[j] = z; |
---|
500 | } |
---|
501 | return; |
---|
502 | } |
---|
503 | } |
---|
504 | else if (ix == n_stops) |
---|
505 | ix--; |
---|
506 | |
---|
507 | for (j = 0; j < n_ch; j++) |
---|
508 | { |
---|
509 | int z; |
---|
510 | z = stops[ix].color[j]; |
---|
511 | if (render->buf_depth == 8) |
---|
512 | dst[j] = ART_PIX_8_FROM_MAX (z); |
---|
513 | else /* (render->buf_depth == 16) */ |
---|
514 | ((art_u16 *)dst)[j] = z; |
---|
515 | } |
---|
516 | } |
---|
517 | |
---|
518 | static void |
---|
519 | art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render) |
---|
520 | { |
---|
521 | art_free (self); |
---|
522 | } |
---|
523 | |
---|
524 | static void |
---|
525 | art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render, |
---|
526 | art_u8 *dest, int y) |
---|
527 | { |
---|
528 | ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; |
---|
529 | const ArtGradientLinear *gradient = &(z->gradient); |
---|
530 | int pixstride = (render->n_chan + 1) * (render->depth >> 3); |
---|
531 | int x; |
---|
532 | int width = render->x1 - render->x0; |
---|
533 | double offset, d_offset; |
---|
534 | double actual_offset; |
---|
535 | int n_stops = gradient->n_stops; |
---|
536 | ArtGradientStop *stops = gradient->stops; |
---|
537 | art_u8 *bufp = render->image_buf; |
---|
538 | ArtGradientSpread spread = gradient->spread; |
---|
539 | |
---|
540 | offset = render->x0 * gradient->a + y * gradient->b + gradient->c; |
---|
541 | d_offset = gradient->a; |
---|
542 | |
---|
543 | for (x = 0; x < width; x++) |
---|
544 | { |
---|
545 | if (spread == ART_GRADIENT_PAD) |
---|
546 | actual_offset = offset; |
---|
547 | else if (spread == ART_GRADIENT_REPEAT) |
---|
548 | actual_offset = offset - floor (offset); |
---|
549 | else /* (spread == ART_GRADIENT_REFLECT) */ |
---|
550 | { |
---|
551 | double tmp; |
---|
552 | |
---|
553 | tmp = offset - 2 * floor (0.5 * offset); |
---|
554 | actual_offset = tmp > 1 ? 2 - tmp : tmp; |
---|
555 | } |
---|
556 | art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset); |
---|
557 | offset += d_offset; |
---|
558 | bufp += pixstride; |
---|
559 | } |
---|
560 | } |
---|
561 | |
---|
562 | static void |
---|
563 | art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render, |
---|
564 | ArtImageSourceFlags *p_flags, |
---|
565 | int *p_buf_depth, ArtAlphaType *p_alpha) |
---|
566 | { |
---|
567 | if (render->depth == 8 && |
---|
568 | render->n_chan == 3) |
---|
569 | { |
---|
570 | self->super.render = art_render_gradient_linear_render_8; |
---|
571 | *p_flags = 0; |
---|
572 | *p_buf_depth = 8; |
---|
573 | *p_alpha = ART_ALPHA_PREMUL; |
---|
574 | return; |
---|
575 | } |
---|
576 | |
---|
577 | self->super.render = art_render_gradient_linear_render; |
---|
578 | *p_flags = 0; |
---|
579 | *p_buf_depth = render->depth; |
---|
580 | *p_alpha = ART_ALPHA_PREMUL; |
---|
581 | } |
---|
582 | |
---|
583 | /** |
---|
584 | * art_render_gradient_linear: Add a linear gradient image source. |
---|
585 | * @render: The render object. |
---|
586 | * @gradient: The linear gradient. |
---|
587 | * |
---|
588 | * Adds the linear gradient @gradient as the image source for rendering |
---|
589 | * in the render object @render. |
---|
590 | **/ |
---|
591 | void |
---|
592 | art_render_gradient_linear (ArtRender *render, |
---|
593 | const ArtGradientLinear *gradient, |
---|
594 | ArtFilterLevel level) |
---|
595 | { |
---|
596 | ArtImageSourceGradLin *image_source = art_alloc (sizeof (ArtImageSourceGradLin) + |
---|
597 | sizeof (ArtGradientStop) * (gradient->n_stops - 1)); |
---|
598 | |
---|
599 | image_source->super.super.render = NULL; |
---|
600 | image_source->super.super.done = art_render_gradient_linear_done; |
---|
601 | image_source->super.negotiate = art_render_gradient_linear_negotiate; |
---|
602 | |
---|
603 | /* copy the gradient into the structure */ |
---|
604 | image_source->gradient = *gradient; |
---|
605 | image_source->gradient.stops = image_source->stops; |
---|
606 | memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops); |
---|
607 | |
---|
608 | art_render_add_image_source (render, &image_source->super); |
---|
609 | } |
---|
610 | |
---|
611 | static void |
---|
612 | art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render) |
---|
613 | { |
---|
614 | art_free (self); |
---|
615 | } |
---|
616 | |
---|
617 | static void |
---|
618 | art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render, |
---|
619 | art_u8 *dest, int y) |
---|
620 | { |
---|
621 | ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self; |
---|
622 | const ArtGradientRadial *gradient = &(z->gradient); |
---|
623 | int pixstride = (render->n_chan + 1) * (render->depth >> 3); |
---|
624 | int x; |
---|
625 | int x0 = render->x0; |
---|
626 | int width = render->x1 - x0; |
---|
627 | int n_stops = gradient->n_stops; |
---|
628 | ArtGradientStop *stops = gradient->stops; |
---|
629 | art_u8 *bufp = render->image_buf; |
---|
630 | double fx = gradient->fx; |
---|
631 | double fy = gradient->fy; |
---|
632 | double dx, dy; |
---|
633 | double *affine = gradient->affine; |
---|
634 | double aff0 = affine[0]; |
---|
635 | double aff1 = affine[1]; |
---|
636 | const double a = z->a; |
---|
637 | const double arecip = 1.0 / a; |
---|
638 | double b, db; |
---|
639 | double c, dc, ddc; |
---|
640 | double b_a, db_a; |
---|
641 | double rad, drad, ddrad; |
---|
642 | |
---|
643 | dx = x0 * aff0 + y * affine[2] + affine[4] - fx; |
---|
644 | dy = x0 * aff1 + y * affine[3] + affine[5] - fy; |
---|
645 | b = dx * fx + dy * fy; |
---|
646 | db = aff0 * fx + aff1 * fy; |
---|
647 | c = dx * dx + dy * dy; |
---|
648 | dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1; |
---|
649 | ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1; |
---|
650 | |
---|
651 | b_a = b * arecip; |
---|
652 | db_a = db * arecip; |
---|
653 | |
---|
654 | rad = b_a * b_a + c * arecip; |
---|
655 | drad = 2 * b_a * db_a + db_a * db_a + dc * arecip; |
---|
656 | ddrad = 2 * db_a * db_a + ddc * arecip; |
---|
657 | |
---|
658 | for (x = 0; x < width; x++) |
---|
659 | { |
---|
660 | double z; |
---|
661 | |
---|
662 | if (rad > 0) |
---|
663 | z = b_a + sqrt (rad); |
---|
664 | else |
---|
665 | z = b_a; |
---|
666 | art_render_gradient_setpix (render, bufp, n_stops, stops, z); |
---|
667 | bufp += pixstride; |
---|
668 | b_a += db_a; |
---|
669 | rad += drad; |
---|
670 | drad += ddrad; |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|
674 | static void |
---|
675 | art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render, |
---|
676 | ArtImageSourceFlags *p_flags, |
---|
677 | int *p_buf_depth, ArtAlphaType *p_alpha) |
---|
678 | { |
---|
679 | self->super.render = art_render_gradient_radial_render; |
---|
680 | *p_flags = 0; |
---|
681 | *p_buf_depth = render->depth; |
---|
682 | *p_alpha = ART_ALPHA_PREMUL; |
---|
683 | } |
---|
684 | |
---|
685 | /** |
---|
686 | * art_render_gradient_radial: Add a radial gradient image source. |
---|
687 | * @render: The render object. |
---|
688 | * @gradient: The radial gradient. |
---|
689 | * |
---|
690 | * Adds the radial gradient @gradient as the image source for rendering |
---|
691 | * in the render object @render. |
---|
692 | **/ |
---|
693 | void |
---|
694 | art_render_gradient_radial (ArtRender *render, |
---|
695 | const ArtGradientRadial *gradient, |
---|
696 | ArtFilterLevel level) |
---|
697 | { |
---|
698 | ArtImageSourceGradRad *image_source = art_alloc (sizeof (ArtImageSourceGradRad) + |
---|
699 | sizeof (ArtGradientStop) * (gradient->n_stops - 1)); |
---|
700 | double fx = gradient->fx; |
---|
701 | double fy = gradient->fy; |
---|
702 | |
---|
703 | image_source->super.super.render = NULL; |
---|
704 | image_source->super.super.done = art_render_gradient_radial_done; |
---|
705 | image_source->super.negotiate = art_render_gradient_radial_negotiate; |
---|
706 | |
---|
707 | /* copy the gradient into the structure */ |
---|
708 | image_source->gradient = *gradient; |
---|
709 | image_source->gradient.stops = image_source->stops; |
---|
710 | memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops); |
---|
711 | |
---|
712 | /* todo: sanitycheck fx, fy? */ |
---|
713 | image_source->a = 1 - fx * fx - fy * fy; |
---|
714 | |
---|
715 | art_render_add_image_source (render, &image_source->super); |
---|
716 | } |
---|