1 | /* This file is adapted from art_render_gradient.c in Libart version 2.3.0 */ |
---|
2 | |
---|
3 | #include <libart_lgpl/libart-features.h> |
---|
4 | |
---|
5 | #if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 3 |
---|
6 | |
---|
7 | /* |
---|
8 | * art_render_gradient.c: Gradient image source for modular rendering. |
---|
9 | * |
---|
10 | * Libart_LGPL - library of basic graphic primitives |
---|
11 | * Copyright (C) 2000 Raph Levien |
---|
12 | * |
---|
13 | * This library is free software; you can redistribute it and/or |
---|
14 | * modify it under the terms of the GNU Library General Public |
---|
15 | * License as published by the Free Software Foundation; either |
---|
16 | * version 2 of the License, or (at your option) any later version. |
---|
17 | * |
---|
18 | * This library is distributed in the hope that it will be useful, |
---|
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
21 | * Library General Public License for more details. |
---|
22 | * |
---|
23 | * You should have received a copy of the GNU Library General Public |
---|
24 | * License along with this library; if not, write to the |
---|
25 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
26 | * Boston, MA 02111-1307, USA. |
---|
27 | * |
---|
28 | * Authors: Raph Levien <raph@acm.org> |
---|
29 | * Alexander Larsson <alla@lysator.liu.se> |
---|
30 | */ |
---|
31 | |
---|
32 | #include <math.h> |
---|
33 | |
---|
34 | #include <libart_lgpl/art_misc.h> |
---|
35 | #include <libart_lgpl/art_alphagamma.h> |
---|
36 | #include <libart_lgpl/art_filterlevel.h> |
---|
37 | |
---|
38 | #include "art_render.h" |
---|
39 | #include "art_render_gradient.h" |
---|
40 | |
---|
41 | typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin; |
---|
42 | typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad; |
---|
43 | |
---|
44 | struct _ArtImageSourceGradLin { |
---|
45 | ArtImageSource super; |
---|
46 | const ArtGradientLinear *gradient; |
---|
47 | }; |
---|
48 | |
---|
49 | struct _ArtImageSourceGradRad { |
---|
50 | ArtImageSource super; |
---|
51 | const ArtGradientRadial *gradient; |
---|
52 | double a; |
---|
53 | }; |
---|
54 | |
---|
55 | #define EPSILON 1e-6 |
---|
56 | |
---|
57 | /** |
---|
58 | * art_render_gradient_setpix: Set a gradient pixel. |
---|
59 | * @render: The render object. |
---|
60 | * @dst: Pointer to destination (where to store pixel). |
---|
61 | * @n_stops: Number of stops in @stops. |
---|
62 | * @stops: The stops for the gradient. |
---|
63 | * @offset: The offset. |
---|
64 | * |
---|
65 | * @n_stops must be > 0. |
---|
66 | * |
---|
67 | * Sets a gradient pixel, storing it at @dst. |
---|
68 | **/ |
---|
69 | static void |
---|
70 | art_render_gradient_setpix (ArtRender *render, |
---|
71 | art_u8 *dst, |
---|
72 | int n_stops, ArtGradientStop *stops, |
---|
73 | double offset) |
---|
74 | { |
---|
75 | int ix; |
---|
76 | int j; |
---|
77 | double off0, off1; |
---|
78 | int n_ch = render->n_chan + 1; |
---|
79 | |
---|
80 | for (ix = 0; ix < n_stops; ix++) |
---|
81 | if (stops[ix].offset > offset) |
---|
82 | break; |
---|
83 | /* stops[ix - 1].offset < offset < stops[ix].offset */ |
---|
84 | if (ix > 0 && ix < n_stops) |
---|
85 | { |
---|
86 | off0 = stops[ix - 1].offset; |
---|
87 | off1 = stops[ix].offset; |
---|
88 | if (fabs (off1 - off0) > EPSILON) |
---|
89 | { |
---|
90 | double interp; |
---|
91 | |
---|
92 | interp = (offset - off0) / (off1 - off0); |
---|
93 | for (j = 0; j < n_ch; j++) |
---|
94 | { |
---|
95 | int z0, z1; |
---|
96 | int z; |
---|
97 | z0 = stops[ix - 1].color[j]; |
---|
98 | z1 = stops[ix].color[j]; |
---|
99 | z = floor (z0 + (z1 - z0) * interp + 0.5); |
---|
100 | if (render->buf_depth == 8) |
---|
101 | dst[j] = ART_PIX_8_FROM_MAX (z); |
---|
102 | else /* (render->buf_depth == 16) */ |
---|
103 | ((art_u16 *)dst)[j] = z; |
---|
104 | } |
---|
105 | return; |
---|
106 | } |
---|
107 | } |
---|
108 | else if (ix == n_stops) |
---|
109 | ix--; |
---|
110 | |
---|
111 | for (j = 0; j < n_ch; j++) |
---|
112 | { |
---|
113 | int z; |
---|
114 | z = stops[ix].color[j]; |
---|
115 | if (render->buf_depth == 8) |
---|
116 | dst[j] = ART_PIX_8_FROM_MAX (z); |
---|
117 | else /* (render->buf_depth == 16) */ |
---|
118 | ((art_u16 *)dst)[j] = z; |
---|
119 | } |
---|
120 | } |
---|
121 | |
---|
122 | static void |
---|
123 | art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render) |
---|
124 | { |
---|
125 | art_free (self); |
---|
126 | } |
---|
127 | |
---|
128 | static void |
---|
129 | art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render, |
---|
130 | art_u8 *dest, int y) |
---|
131 | { |
---|
132 | ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self; |
---|
133 | const ArtGradientLinear *gradient = z->gradient; |
---|
134 | int pixstride = (render->n_chan + 1) * (render->depth >> 3); |
---|
135 | int x; |
---|
136 | int width = render->x1 - render->x0; |
---|
137 | double offset, d_offset; |
---|
138 | double actual_offset; |
---|
139 | int n_stops = gradient->n_stops; |
---|
140 | ArtGradientStop *stops = gradient->stops; |
---|
141 | art_u8 *bufp = render->image_buf; |
---|
142 | ArtGradientSpread spread = gradient->spread; |
---|
143 | |
---|
144 | offset = render->x0 * gradient->a + y * gradient->b + gradient->c; |
---|
145 | d_offset = gradient->a; |
---|
146 | |
---|
147 | for (x = 0; x < width; x++) |
---|
148 | { |
---|
149 | if (spread == ART_GRADIENT_PAD) |
---|
150 | actual_offset = offset; |
---|
151 | else if (spread == ART_GRADIENT_REPEAT) |
---|
152 | actual_offset = offset - floor (offset); |
---|
153 | else /* (spread == ART_GRADIENT_REFLECT) */ |
---|
154 | { |
---|
155 | double tmp; |
---|
156 | |
---|
157 | tmp = offset - 2 * floor (0.5 * offset); |
---|
158 | actual_offset = tmp > 1 ? 2 - tmp : tmp; |
---|
159 | } |
---|
160 | art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset); |
---|
161 | offset += d_offset; |
---|
162 | bufp += pixstride; |
---|
163 | } |
---|
164 | } |
---|
165 | |
---|
166 | static void |
---|
167 | art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render, |
---|
168 | ArtImageSourceFlags *p_flags, |
---|
169 | int *p_buf_depth, ArtAlphaType *p_alpha) |
---|
170 | { |
---|
171 | self->super.render = art_render_gradient_linear_render; |
---|
172 | *p_flags = 0; |
---|
173 | *p_buf_depth = render->depth; |
---|
174 | *p_alpha = ART_ALPHA_PREMUL; |
---|
175 | } |
---|
176 | |
---|
177 | /** |
---|
178 | * art_render_gradient_linear: Add a linear gradient image source. |
---|
179 | * @render: The render object. |
---|
180 | * @gradient: The linear gradient. |
---|
181 | * |
---|
182 | * Adds the linear gradient @gradient as the image source for rendering |
---|
183 | * in the render object @render. |
---|
184 | **/ |
---|
185 | void |
---|
186 | art_render_gradient_linear (ArtRender *render, |
---|
187 | const ArtGradientLinear *gradient, |
---|
188 | ArtFilterLevel level) |
---|
189 | { |
---|
190 | ArtImageSourceGradLin *image_source = art_new (ArtImageSourceGradLin, 1); |
---|
191 | |
---|
192 | image_source->super.super.render = NULL; |
---|
193 | image_source->super.super.done = art_render_gradient_linear_done; |
---|
194 | image_source->super.negotiate = art_render_gradient_linear_negotiate; |
---|
195 | |
---|
196 | image_source->gradient = gradient; |
---|
197 | |
---|
198 | art_render_add_image_source (render, &image_source->super); |
---|
199 | } |
---|
200 | |
---|
201 | static void |
---|
202 | art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render) |
---|
203 | { |
---|
204 | art_free (self); |
---|
205 | } |
---|
206 | |
---|
207 | static void |
---|
208 | art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render, |
---|
209 | art_u8 *dest, int y) |
---|
210 | { |
---|
211 | ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self; |
---|
212 | const ArtGradientRadial *gradient = z->gradient; |
---|
213 | int pixstride = (render->n_chan + 1) * (render->depth >> 3); |
---|
214 | int x; |
---|
215 | int x0 = render->x0; |
---|
216 | int width = render->x1 - x0; |
---|
217 | int n_stops = gradient->n_stops; |
---|
218 | ArtGradientStop *stops = gradient->stops; |
---|
219 | art_u8 *bufp = render->image_buf; |
---|
220 | double fx = gradient->fx; |
---|
221 | double fy = gradient->fy; |
---|
222 | double dx, dy; |
---|
223 | double *affine = gradient->affine; |
---|
224 | double aff0 = affine[0]; |
---|
225 | double aff1 = affine[1]; |
---|
226 | const double a = z->a; |
---|
227 | const double arecip = 1.0 / a; |
---|
228 | double b, db; |
---|
229 | double c, dc, ddc; |
---|
230 | double b_a, db_a; |
---|
231 | double rad, drad, ddrad; |
---|
232 | |
---|
233 | dx = x0 * aff0 + y * affine[2] + affine[4] - fx; |
---|
234 | dy = x0 * aff1 + y * affine[3] + affine[5] - fy; |
---|
235 | b = dx * fx + dy * fy; |
---|
236 | db = aff0 * fx + aff1 * fy; |
---|
237 | c = dx * dx + dy * dy; |
---|
238 | dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1; |
---|
239 | ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1; |
---|
240 | |
---|
241 | b_a = b * arecip; |
---|
242 | db_a = db * arecip; |
---|
243 | |
---|
244 | rad = b_a * b_a + c * arecip; |
---|
245 | drad = 2 * b_a * db_a + db_a * db_a + dc * arecip; |
---|
246 | ddrad = 2 * db_a * db_a + ddc * arecip; |
---|
247 | |
---|
248 | for (x = 0; x < width; x++) |
---|
249 | { |
---|
250 | double z; |
---|
251 | |
---|
252 | if (rad > 0) |
---|
253 | z = b_a + sqrt (rad); |
---|
254 | else |
---|
255 | z = b_a; |
---|
256 | art_render_gradient_setpix (render, bufp, n_stops, stops, z); |
---|
257 | bufp += pixstride; |
---|
258 | b_a += db_a; |
---|
259 | rad += drad; |
---|
260 | drad += ddrad; |
---|
261 | } |
---|
262 | } |
---|
263 | |
---|
264 | static void |
---|
265 | art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render, |
---|
266 | ArtImageSourceFlags *p_flags, |
---|
267 | int *p_buf_depth, ArtAlphaType *p_alpha) |
---|
268 | { |
---|
269 | self->super.render = art_render_gradient_radial_render; |
---|
270 | *p_flags = 0; |
---|
271 | *p_buf_depth = render->depth; |
---|
272 | *p_alpha = ART_ALPHA_PREMUL; |
---|
273 | } |
---|
274 | |
---|
275 | /** |
---|
276 | * art_render_gradient_radial: Add a radial gradient image source. |
---|
277 | * @render: The render object. |
---|
278 | * @gradient: The radial gradient. |
---|
279 | * |
---|
280 | * Adds the radial gradient @gradient as the image source for rendering |
---|
281 | * in the render object @render. |
---|
282 | **/ |
---|
283 | void |
---|
284 | art_render_gradient_radial (ArtRender *render, |
---|
285 | const ArtGradientRadial *gradient, |
---|
286 | ArtFilterLevel level) |
---|
287 | { |
---|
288 | ArtImageSourceGradRad *image_source = art_new (ArtImageSourceGradRad, 1); |
---|
289 | double fx = gradient->fx; |
---|
290 | double fy = gradient->fy; |
---|
291 | |
---|
292 | image_source->super.super.render = NULL; |
---|
293 | image_source->super.super.done = art_render_gradient_radial_done; |
---|
294 | image_source->super.negotiate = art_render_gradient_radial_negotiate; |
---|
295 | |
---|
296 | image_source->gradient = gradient; |
---|
297 | /* todo: sanitycheck fx, fy? */ |
---|
298 | image_source->a = 1 - fx * fx - fy * fy; |
---|
299 | |
---|
300 | art_render_add_image_source (render, &image_source->super); |
---|
301 | } |
---|
302 | |
---|
303 | #endif |
---|