2 * art_render_gradient.c: Gradient image source for modular rendering.
4 * Libart_LGPL - library of basic graphic primitives
5 * Copyright (C) 2000 Raph Levien
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.
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.
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.
22 * Authors: Raph Levien <raph@acm.org>
23 * Alexander Larsson <alla@lysator.liu.se>
27 #include "art_render_gradient.h"
35 /* Hack to find out how to define alloca on different platforms.
36 * Modified version of glib/galloca.h.
40 /* GCC does the right thing */
42 # define alloca(size) __builtin_alloca (size)
43 #elif defined (HAVE_ALLOCA_H)
44 /* a native and working alloca.h is there */
46 #else /* !__GNUC__ && !HAVE_ALLOCA_H */
49 # define alloca _alloca
50 # else /* !_MSC_VER */
54 # ifndef alloca /* predefined by HP cc +Olibcalls */
58 # endif /* !_MSC_VER */
59 #endif /* !__GNUC__ && !HAVE_ALLOCA_H */
63 typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin
;
64 typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad
;
66 /* The stops will be copied right after this structure */
67 struct _ArtImageSourceGradLin
{
69 ArtGradientLinear gradient
;
70 ArtGradientStop stops
[1];
73 /* The stops will be copied right after this structure */
74 struct _ArtImageSourceGradRad
{
76 ArtGradientRadial gradient
;
78 ArtGradientStop stops
[1];
84 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
88 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
92 art_rgba_gradient_run (art_u8
*buf
,
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],
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
;
117 for (i
= 0; i
< len
; i
++)
132 calc_color_at (ArtGradientStop
*stops
,
134 ArtGradientSpread spread
,
136 double offset_fraction
,
144 if (spread
== ART_GRADIENT_PAD
)
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]);
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]);
164 if (ix
> 0 && ix
< n_stops
)
166 off0
= stops
[ix
- 1].offset
;
167 off1
= stops
[ix
].offset
;
168 if (fabs (off1
- off0
) > EPSILON
)
174 if ((fabs (o
) < EPSILON
) && (!favor_start
))
176 else if ((fabs (o
-1.0) < EPSILON
) && (favor_start
))
180 if (offset_fraction == 0.0 && !favor_start)
181 offset_fraction = 1.0;
184 interp
= (o
- off0
) / (off1
- off0
);
185 for (j
= 0; j
< 4; j
++)
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
);
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]);
205 printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix
);
210 art_render_gradient_linear_render_8 (ArtRenderCallback
*self
,
214 ArtImageSourceGradLin
*z
= (ArtImageSourceGradLin
*)self
;
215 const ArtGradientLinear
*gradient
= &(z
->gradient
);
217 int width
= render
->x1
- render
->x0
;
219 double offset
, d_offset
;
220 double offset_fraction
;
223 art_u8 color1
[4], color2
[4];
224 int n_stops
= gradient
->n_stops
;
226 ArtGradientStop
*stops
= gradient
->stops
;
227 ArtGradientStop
*tmp_stops
;
228 art_u8
*bufp
= render
->image_buf
;
229 ArtGradientSpread spread
= gradient
->spread
;
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
++)
236 printf ("%f, ", gradient
->stops
[i
].offset
);
239 printf ("a: %f, b: %f, c: %f\n", gradient
->a
, gradient
->b
, gradient
->c
);
242 offset
= render
->x0
* gradient
->a
+ y
* gradient
->b
+ gradient
->c
;
243 d_offset
= gradient
->a
;
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
248 if ((gradient
->stops
[0].offset
> EPSILON
/* == 0.0 */) ||
249 (gradient
->stops
[n_stops
-1].offset
< (1.0 - EPSILON
)))
252 tmp_stops
= stops
= (ArtGradientStop
*)alloca (sizeof (ArtGradientStop
) * (n_stops
+ 2));
253 if (gradient
->stops
[0].offset
> EPSILON
/* 0.0 */)
255 memcpy (tmp_stops
, gradient
->stops
, sizeof (ArtGradientStop
));
256 tmp_stops
[0].offset
= 0.0;
260 memcpy (tmp_stops
, gradient
->stops
, sizeof (ArtGradientStop
) * n_stops
);
261 if (gradient
->stops
[n_stops
-1].offset
< (1.0 - EPSILON
))
263 tmp_stops
+= n_stops
;
264 memcpy (tmp_stops
, &gradient
->stops
[n_stops
-1], sizeof (ArtGradientStop
));
265 tmp_stops
[0].offset
= 1.0;
268 n_stops
+= extra_stops
;
272 printf ("start/stop modified stops:");
273 for (i
=0;i
<n_stops
;i
++)
275 printf ("%f, ", stops
[i
].offset
);
282 if (spread
== ART_GRADIENT_REFLECT
)
285 stops
= (ArtGradientStop
*)alloca (sizeof (ArtGradientStop
) * n_stops
* 2);
286 memcpy (stops
, tmp_stops
, sizeof (ArtGradientStop
) * n_stops
);
288 for (i
= 0; i
< n_stops
; i
++)
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;
295 spread
= ART_GRADIENT_REPEAT
;
296 offset
= offset
/ 2.0;
297 d_offset
= d_offset
/ 2.0;
299 n_stops
= 2 * n_stops
;
302 printf ("reflect modified stops:");
303 for (i
=0;i
<n_stops
;i
++)
305 printf ("%f, ", stops
[i
].offset
);
311 offset_fraction
= offset
- floor (offset
);
313 printf ("inital offset: %f, fraction: %f d_offset: %f\n", offset
, offset_fraction
, d_offset
);
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.
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
))
327 else if (ix
== n_stops
)
331 printf ("Initial ix: %d\n", ix
);
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!
342 assert ((offset_fraction != stops[ix-1].offset) ||
344 assert ((offset_fraction != stops[ix].offset) ||
351 printf ("ix: %d\n", ix
);
352 printf ("start offset: %f\n", offset
);
354 calc_color_at (stops
, n_stops
,
358 (d_offset
> -EPSILON
),
368 printf ("next_stop: %d\n", next_stop
);
370 if (fabs (d_offset
) > EPSILON
)
375 if ((fabs (o
) <= EPSILON
) && (ix
== n_stops
- 1))
377 else if ((fabs (o
-1.0) <= EPSILON
) && (ix
== 1))
381 printf ("o: %f\n", o
);
383 len
= (int)floor (fabs ((stops
[next_stop
].offset
- o
) / d_offset
)) + 1;
385 len
= MIN (len
, width
);
392 printf ("len: %d\n", len
);
396 offset
= offset
+ (len
-1) * d_offset
;
397 offset_fraction
= offset
- floor (offset
);
399 printf ("end offset: %f, fraction: %f\n", offset
, offset_fraction
);
401 calc_color_at (stops
, n_stops
,
405 (d_offset
< EPSILON
),
409 art_rgba_gradient_run (bufp
,
414 offset_fraction
= offset
- floor (offset
);
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.
430 while (!((stops
[ix
-1].offset
<= offset_fraction
&&
431 offset_fraction
< stops
[ix
].offset
) ||
432 (ix
== 1 && offset_fraction
> (1.0 - EPSILON
))));
442 while (!((stops
[ix
-1].offset
< offset_fraction
&&
443 offset_fraction
<= stops
[ix
].offset
) ||
444 (ix
== n_stops
- 1 && offset_fraction
< EPSILON
/* == 0.0*/)));
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.
461 * @n_stops must be > 0.
463 * Sets a gradient pixel, storing it at @dst.
466 art_render_gradient_setpix (ArtRender
*render
,
468 int n_stops
, ArtGradientStop
*stops
,
474 int n_ch
= render
->n_chan
+ 1;
476 for (ix
= 0; ix
< n_stops
; ix
++)
477 if (stops
[ix
].offset
> offset
)
479 /* stops[ix - 1].offset < offset < stops[ix].offset */
480 if (ix
> 0 && ix
< n_stops
)
482 off0
= stops
[ix
- 1].offset
;
483 off1
= stops
[ix
].offset
;
484 if (fabs (off1
- off0
) > EPSILON
)
488 interp
= (offset
- off0
) / (off1
- off0
);
489 for (j
= 0; j
< n_ch
; j
++)
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
;
504 else if (ix
== n_stops
)
507 for (j
= 0; j
< n_ch
; j
++)
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
;
519 art_render_gradient_linear_done (ArtRenderCallback
*self
, ArtRender
*render
)
525 art_render_gradient_linear_render (ArtRenderCallback
*self
, ArtRender
*render
,
528 ArtImageSourceGradLin
*z
= (ArtImageSourceGradLin
*)self
;
529 const ArtGradientLinear
*gradient
= &(z
->gradient
);
530 int pixstride
= (render
->n_chan
+ 1) * (render
->depth
>> 3);
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
;
540 offset
= render
->x0
* gradient
->a
+ y
* gradient
->b
+ gradient
->c
;
541 d_offset
= gradient
->a
;
543 for (x
= 0; x
< width
; x
++)
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) */
553 tmp
= offset
- 2 * floor (0.5 * offset
);
554 actual_offset
= tmp
> 1 ? 2 - tmp
: tmp
;
556 art_render_gradient_setpix (render
, bufp
, n_stops
, stops
, actual_offset
);
563 art_render_gradient_linear_negotiate (ArtImageSource
*self
, ArtRender
*render
,
565 int *p_buf_depth
, ArtAlphaType
*p_alpha
)
567 if (render
->depth
== 8 &&
570 self
->super
.render
= art_render_gradient_linear_render_8
;
573 *p_alpha
= ART_ALPHA_PREMUL
;
577 self
->super
.render
= art_render_gradient_linear_render
;
579 *p_buf_depth
= render
->depth
;
580 *p_alpha
= ART_ALPHA_PREMUL
;
584 * art_render_gradient_linear: Add a linear gradient image source.
585 * @render: The render object.
586 * @gradient: The linear gradient.
588 * Adds the linear gradient @gradient as the image source for rendering
589 * in the render object @render.
592 art_render_gradient_linear (ArtRender
*render
,
593 const ArtGradientLinear
*gradient
,
594 ArtFilterLevel level
)
596 ArtImageSourceGradLin
*image_source
= (ArtImageSourceGradLin
*)art_alloc (sizeof (ArtImageSourceGradLin
) +
597 sizeof (ArtGradientStop
) * (gradient
->n_stops
- 1));
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
;
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
);
608 art_render_add_image_source (render
, &image_source
->super
);
612 art_render_gradient_radial_done (ArtRenderCallback
*self
, ArtRender
*render
)
618 art_render_gradient_radial_render (ArtRenderCallback
*self
, ArtRender
*render
,
621 ArtImageSourceGradRad
*z
= (ArtImageSourceGradRad
*)self
;
622 const ArtGradientRadial
*gradient
= &(z
->gradient
);
623 int pixstride
= (render
->n_chan
+ 1) * (render
->depth
>> 3);
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
;
633 double *affine
= (double*)&gradient
->affine
[0];
634 double aff0
= affine
[0];
635 double aff1
= affine
[1];
636 const double a
= z
->a
;
637 const double arecip
= 1.0 / a
;
641 double rad
, drad
, ddrad
;
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
;
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
;
658 for (x
= 0; x
< width
; x
++)
663 z
= b_a
+ sqrt (rad
);
666 art_render_gradient_setpix (render
, bufp
, n_stops
, stops
, z
);
675 art_render_gradient_radial_negotiate (ArtImageSource
*self
, ArtRender
*render
,
677 int *p_buf_depth
, ArtAlphaType
*p_alpha
)
679 self
->super
.render
= art_render_gradient_radial_render
;
681 *p_buf_depth
= render
->depth
;
682 *p_alpha
= ART_ALPHA_PREMUL
;
686 * art_render_gradient_radial: Add a radial gradient image source.
687 * @render: The render object.
688 * @gradient: The radial gradient.
690 * Adds the radial gradient @gradient as the image source for rendering
691 * in the render object @render.
694 art_render_gradient_radial (ArtRender
*render
,
695 const ArtGradientRadial
*gradient
,
696 ArtFilterLevel level
)
698 ArtImageSourceGradRad
*image_source
= (ArtImageSourceGradRad
*)art_alloc (sizeof (ArtImageSourceGradRad
) +
699 sizeof (ArtGradientStop
) * (gradient
->n_stops
- 1));
700 double fx
= gradient
->fx
;
701 double fy
= gradient
->fy
;
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
;
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
);
712 /* todo: sanitycheck fx, fy? */
713 image_source
->a
= 1 - fx
* fx
- fy
* fy
;
715 art_render_add_image_source (render
, &image_source
->super
);