Rev 1891 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1891 | Rev 3931 | ||
---|---|---|---|
1 | /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ |
1 | /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ |
2 | /* |
2 | /* |
3 | * |
3 | * |
4 | * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. |
4 | * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. |
5 | * Copyright © 2000 SuSE, Inc. |
5 | * Copyright © 2000 SuSE, Inc. |
6 | * 2005 Lars Knoll & Zack Rusin, Trolltech |
6 | * 2005 Lars Knoll & Zack Rusin, Trolltech |
7 | * Copyright © 2007 Red Hat, Inc. |
7 | * Copyright © 2007 Red Hat, Inc. |
8 | * |
8 | * |
9 | * |
9 | * |
10 | * Permission to use, copy, modify, distribute, and sell this software and its |
10 | * Permission to use, copy, modify, distribute, and sell this software and its |
11 | * documentation for any purpose is hereby granted without fee, provided that |
11 | * documentation for any purpose is hereby granted without fee, provided that |
12 | * the above copyright notice appear in all copies and that both that |
12 | * the above copyright notice appear in all copies and that both that |
13 | * copyright notice and this permission notice appear in supporting |
13 | * copyright notice and this permission notice appear in supporting |
14 | * documentation, and that the name of Keith Packard not be used in |
14 | * documentation, and that the name of Keith Packard not be used in |
15 | * advertising or publicity pertaining to distribution of the software without |
15 | * advertising or publicity pertaining to distribution of the software without |
16 | * specific, written prior permission. Keith Packard makes no |
16 | * specific, written prior permission. Keith Packard makes no |
17 | * representations about the suitability of this software for any purpose. It |
17 | * representations about the suitability of this software for any purpose. It |
18 | * is provided "as is" without express or implied warranty. |
18 | * is provided "as is" without express or implied warranty. |
19 | * |
19 | * |
20 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS |
20 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS |
21 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
21 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
22 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
22 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY |
23 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
23 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
24 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
24 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
25 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
25 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
26 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
26 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
27 | * SOFTWARE. |
27 | * SOFTWARE. |
28 | */ |
28 | */ |
29 | 29 | ||
30 | #ifdef HAVE_CONFIG_H |
30 | #ifdef HAVE_CONFIG_H |
31 | #include |
31 | #include |
32 | #endif |
32 | #endif |
33 | #include |
33 | #include |
34 | #include |
34 | #include |
35 | #include "pixman-private.h" |
35 | #include "pixman-private.h" |
36 | 36 | ||
37 | static inline pixman_fixed_32_32_t |
37 | static inline pixman_fixed_32_32_t |
38 | dot (pixman_fixed_48_16_t x1, |
38 | dot (pixman_fixed_48_16_t x1, |
39 | pixman_fixed_48_16_t y1, |
39 | pixman_fixed_48_16_t y1, |
40 | pixman_fixed_48_16_t z1, |
40 | pixman_fixed_48_16_t z1, |
41 | pixman_fixed_48_16_t x2, |
41 | pixman_fixed_48_16_t x2, |
42 | pixman_fixed_48_16_t y2, |
42 | pixman_fixed_48_16_t y2, |
43 | pixman_fixed_48_16_t z2) |
43 | pixman_fixed_48_16_t z2) |
44 | { |
44 | { |
45 | /* |
45 | /* |
46 | * Exact computation, assuming that the input values can |
46 | * Exact computation, assuming that the input values can |
47 | * be represented as pixman_fixed_16_16_t |
47 | * be represented as pixman_fixed_16_16_t |
48 | */ |
48 | */ |
49 | return x1 * x2 + y1 * y2 + z1 * z2; |
49 | return x1 * x2 + y1 * y2 + z1 * z2; |
50 | } |
50 | } |
51 | 51 | ||
52 | static inline double |
52 | static inline double |
53 | fdot (double x1, |
53 | fdot (double x1, |
54 | double y1, |
54 | double y1, |
55 | double z1, |
55 | double z1, |
56 | double x2, |
56 | double x2, |
57 | double y2, |
57 | double y2, |
58 | double z2) |
58 | double z2) |
59 | { |
59 | { |
60 | /* |
60 | /* |
61 | * Error can be unbound in some special cases. |
61 | * Error can be unbound in some special cases. |
62 | * Using clever dot product algorithms (for example compensated |
62 | * Using clever dot product algorithms (for example compensated |
63 | * dot product) would improve this but make the code much less |
63 | * dot product) would improve this but make the code much less |
64 | * obvious |
64 | * obvious |
65 | */ |
65 | */ |
66 | return x1 * x2 + y1 * y2 + z1 * z2; |
66 | return x1 * x2 + y1 * y2 + z1 * z2; |
67 | } |
67 | } |
68 | 68 | ||
69 | static uint32_t |
69 | static uint32_t |
70 | radial_compute_color (double a, |
70 | radial_compute_color (double a, |
71 | double b, |
71 | double b, |
72 | double c, |
72 | double c, |
73 | double inva, |
73 | double inva, |
74 | double dr, |
74 | double dr, |
75 | double mindr, |
75 | double mindr, |
76 | pixman_gradient_walker_t *walker, |
76 | pixman_gradient_walker_t *walker, |
77 | pixman_repeat_t repeat) |
77 | pixman_repeat_t repeat) |
78 | { |
78 | { |
79 | /* |
79 | /* |
80 | * In this function error propagation can lead to bad results: |
80 | * In this function error propagation can lead to bad results: |
81 | * - det can have an unbound error (if b*b-a*c is very small), |
81 | * - discr can have an unbound error (if b*b-a*c is very small), |
82 | * potentially making it the opposite sign of what it should have been |
82 | * potentially making it the opposite sign of what it should have been |
83 | * (thus clearing a pixel that would have been colored or vice-versa) |
83 | * (thus clearing a pixel that would have been colored or vice-versa) |
84 | * or propagating the error to sqrtdet; |
84 | * or propagating the error to sqrtdiscr; |
85 | * if det has the wrong sign or b is very small, this can lead to bad |
85 | * if discr has the wrong sign or b is very small, this can lead to bad |
86 | * results |
86 | * results |
87 | * |
87 | * |
88 | * - the algorithm used to compute the solutions of the quadratic |
88 | * - the algorithm used to compute the solutions of the quadratic |
89 | * equation is not numerically stable (but saves one division compared |
89 | * equation is not numerically stable (but saves one division compared |
90 | * to the numerically stable one); |
90 | * to the numerically stable one); |
91 | * this can be a problem if a*c is much smaller than b*b |
91 | * this can be a problem if a*c is much smaller than b*b |
92 | * |
92 | * |
93 | * - the above problems are worse if a is small (as inva becomes bigger) |
93 | * - the above problems are worse if a is small (as inva becomes bigger) |
94 | */ |
94 | */ |
95 | double det; |
95 | double discr; |
96 | 96 | ||
97 | if (a == 0) |
97 | if (a == 0) |
98 | { |
98 | { |
99 | double t; |
99 | double t; |
100 | 100 | ||
101 | if (b == 0) |
101 | if (b == 0) |
102 | return 0; |
102 | return 0; |
103 | 103 | ||
104 | t = pixman_fixed_1 / 2 * c / b; |
104 | t = pixman_fixed_1 / 2 * c / b; |
105 | if (repeat == PIXMAN_REPEAT_NONE) |
105 | if (repeat == PIXMAN_REPEAT_NONE) |
106 | { |
106 | { |
107 | if (0 <= t && t <= pixman_fixed_1) |
107 | if (0 <= t && t <= pixman_fixed_1) |
108 | return _pixman_gradient_walker_pixel (walker, t); |
108 | return _pixman_gradient_walker_pixel (walker, t); |
109 | } |
109 | } |
110 | else |
110 | else |
111 | { |
111 | { |
112 | if (t * dr > mindr) |
112 | if (t * dr >= mindr) |
113 | return _pixman_gradient_walker_pixel (walker, t); |
113 | return _pixman_gradient_walker_pixel (walker, t); |
114 | } |
114 | } |
115 | 115 | ||
116 | return 0; |
116 | return 0; |
117 | } |
117 | } |
118 | 118 | ||
119 | det = fdot (b, a, 0, b, -c, 0); |
119 | discr = fdot (b, a, 0, b, -c, 0); |
120 | if (det >= 0) |
120 | if (discr >= 0) |
121 | { |
121 | { |
122 | double sqrtdet, t0, t1; |
122 | double sqrtdiscr, t0, t1; |
123 | 123 | ||
124 | sqrtdet = sqrt (det); |
124 | sqrtdiscr = sqrt (discr); |
- | 125 | t0 = (b + sqrtdiscr) * inva; |
|
- | 126 | t1 = (b - sqrtdiscr) * inva; |
|
- | 127 | ||
- | 128 | /* |
|
- | 129 | * The root that must be used is the biggest one that belongs |
|
- | 130 | * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any |
|
- | 131 | * solution that results in a positive radius otherwise). |
|
- | 132 | * |
|
- | 133 | * If a > 0, t0 is the biggest solution, so if it is valid, it |
|
- | 134 | * is the correct result. |
|
- | 135 | * |
|
125 | t0 = (b + sqrtdet) * inva; |
136 | * If a < 0, only one of the solutions can be valid, so the |
126 | t1 = (b - sqrtdet) * inva; |
137 | * order in which they are tested is not important. |
127 | 138 | */ |
|
128 | if (repeat == PIXMAN_REPEAT_NONE) |
139 | if (repeat == PIXMAN_REPEAT_NONE) |
129 | { |
140 | { |
130 | if (0 <= t0 && t0 <= pixman_fixed_1) |
141 | if (0 <= t0 && t0 <= pixman_fixed_1) |
131 | return _pixman_gradient_walker_pixel (walker, t0); |
142 | return _pixman_gradient_walker_pixel (walker, t0); |
132 | else if (0 <= t1 && t1 <= pixman_fixed_1) |
143 | else if (0 <= t1 && t1 <= pixman_fixed_1) |
133 | return _pixman_gradient_walker_pixel (walker, t1); |
144 | return _pixman_gradient_walker_pixel (walker, t1); |
134 | } |
145 | } |
135 | else |
146 | else |
136 | { |
147 | { |
137 | if (t0 * dr > mindr) |
148 | if (t0 * dr >= mindr) |
138 | return _pixman_gradient_walker_pixel (walker, t0); |
149 | return _pixman_gradient_walker_pixel (walker, t0); |
139 | else if (t1 * dr > mindr) |
150 | else if (t1 * dr >= mindr) |
140 | return _pixman_gradient_walker_pixel (walker, t1); |
151 | return _pixman_gradient_walker_pixel (walker, t1); |
141 | } |
152 | } |
142 | } |
153 | } |
143 | 154 | ||
144 | return 0; |
155 | return 0; |
145 | } |
156 | } |
146 | 157 | ||
147 | static void |
158 | static uint32_t * |
148 | radial_gradient_get_scanline_32 (pixman_image_t *image, |
- | |
149 | int x, |
- | |
150 | int y, |
- | |
151 | int width, |
- | |
152 | uint32_t * buffer, |
- | |
153 | const uint32_t *mask) |
159 | radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) |
154 | { |
160 | { |
155 | /* |
161 | /* |
156 | * Implementation of radial gradients following the PDF specification. |
162 | * Implementation of radial gradients following the PDF specification. |
157 | * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference |
163 | * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference |
158 | * Manual (PDF 32000-1:2008 at the time of this writing). |
164 | * Manual (PDF 32000-1:2008 at the time of this writing). |
159 | * |
165 | * |
160 | * In the radial gradient problem we are given two circles (c₁,r₁) and |
166 | * In the radial gradient problem we are given two circles (c₁,r₁) and |
161 | * (c₂,r₂) that define the gradient itself. |
167 | * (c₂,r₂) that define the gradient itself. |
162 | * |
168 | * |
163 | * Mathematically the gradient can be defined as the family of circles |
169 | * Mathematically the gradient can be defined as the family of circles |
164 | * |
170 | * |
165 | * ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂) |
171 | * ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂) |
166 | * |
172 | * |
167 | * excluding those circles whose radius would be < 0. When a point |
173 | * excluding those circles whose radius would be < 0. When a point |
168 | * belongs to more than one circle, the one with a bigger t is the only |
174 | * belongs to more than one circle, the one with a bigger t is the only |
169 | * one that contributes to its color. When a point does not belong |
175 | * one that contributes to its color. When a point does not belong |
170 | * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). |
176 | * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). |
171 | * Further limitations on the range of values for t are imposed when |
177 | * Further limitations on the range of values for t are imposed when |
172 | * the gradient is not repeated, namely t must belong to [0,1]. |
178 | * the gradient is not repeated, namely t must belong to [0,1]. |
173 | * |
179 | * |
174 | * The graphical result is the same as drawing the valid (radius > 0) |
180 | * The graphical result is the same as drawing the valid (radius > 0) |
175 | * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient |
181 | * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient |
176 | * is not repeated) using SOURCE operatior composition. |
182 | * is not repeated) using SOURCE operator composition. |
177 | * |
183 | * |
178 | * It looks like a cone pointing towards the viewer if the ending circle |
184 | * It looks like a cone pointing towards the viewer if the ending circle |
179 | * is smaller than the starting one, a cone pointing inside the page if |
185 | * is smaller than the starting one, a cone pointing inside the page if |
180 | * the starting circle is the smaller one and like a cylinder if they |
186 | * the starting circle is the smaller one and like a cylinder if they |
181 | * have the same radius. |
187 | * have the same radius. |
182 | * |
188 | * |
183 | * What we actually do is, given the point whose color we are interested |
189 | * What we actually do is, given the point whose color we are interested |
184 | * in, compute the t values for that point, solving for t in: |
190 | * in, compute the t values for that point, solving for t in: |
185 | * |
191 | * |
186 | * length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂ |
192 | * length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂ |
187 | * |
193 | * |
188 | * Let's rewrite it in a simpler way, by defining some auxiliary |
194 | * Let's rewrite it in a simpler way, by defining some auxiliary |
189 | * variables: |
195 | * variables: |
190 | * |
196 | * |
191 | * cd = c₂ - c₁ |
197 | * cd = c₂ - c₁ |
192 | * pd = p - c₁ |
198 | * pd = p - c₁ |
193 | * dr = r₂ - r₁ |
199 | * dr = r₂ - r₁ |
194 | * lenght(t·cd - pd) = r₁ + t·dr |
200 | * length(t·cd - pd) = r₁ + t·dr |
195 | * |
201 | * |
196 | * which actually means |
202 | * which actually means |
197 | * |
203 | * |
198 | * hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr |
204 | * hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr |
199 | * |
205 | * |
200 | * or |
206 | * or |
201 | * |
207 | * |
202 | * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr. |
208 | * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr. |
203 | * |
209 | * |
204 | * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes: |
210 | * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes: |
205 | * |
211 | * |
206 | * (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)² |
212 | * (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)² |
207 | * |
213 | * |
208 | * where we can actually expand the squares and solve for t: |
214 | * where we can actually expand the squares and solve for t: |
209 | * |
215 | * |
210 | * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = |
216 | * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = |
211 | * = r₁² + 2·r₁·t·dr + t²·dr² |
217 | * = r₁² + 2·r₁·t·dr + t²·dr² |
212 | * |
218 | * |
213 | * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t + |
219 | * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t + |
214 | * (pdx² + pdy² - r₁²) = 0 |
220 | * (pdx² + pdy² - r₁²) = 0 |
215 | * |
221 | * |
216 | * A = cdx² + cdy² - dr² |
222 | * A = cdx² + cdy² - dr² |
217 | * B = pdx·cdx + pdy·cdy + r₁·dr |
223 | * B = pdx·cdx + pdy·cdy + r₁·dr |
218 | * C = pdx² + pdy² - r₁² |
224 | * C = pdx² + pdy² - r₁² |
219 | * At² - 2Bt + C = 0 |
225 | * At² - 2Bt + C = 0 |
220 | * |
226 | * |
221 | * The solutions (unless the equation degenerates because of A = 0) are: |
227 | * The solutions (unless the equation degenerates because of A = 0) are: |
222 | * |
228 | * |
223 | * t = (B ± ⎷(B² - A·C)) / A |
229 | * t = (B ± ⎷(B² - A·C)) / A |
224 | * |
230 | * |
225 | * The solution we are going to prefer is the bigger one, unless the |
231 | * The solution we are going to prefer is the bigger one, unless the |
226 | * radius associated to it is negative (or it falls outside the valid t |
232 | * radius associated to it is negative (or it falls outside the valid t |
227 | * range). |
233 | * range). |
228 | * |
234 | * |
229 | * Additional observations (useful for optimizations): |
235 | * Additional observations (useful for optimizations): |
230 | * A does not depend on p |
236 | * A does not depend on p |
231 | * |
237 | * |
232 | * A < 0 <=> one of the two circles completely contains the other one |
238 | * A < 0 <=> one of the two circles completely contains the other one |
233 | * <=> for every p, the radiuses associated with the two t solutions |
239 | * <=> for every p, the radiuses associated with the two t solutions |
234 | * have opposite sign |
240 | * have opposite sign |
235 | */ |
241 | */ |
- | 242 | pixman_image_t *image = iter->image; |
|
- | 243 | int x = iter->x; |
|
- | 244 | int y = iter->y; |
|
- | 245 | int width = iter->width; |
|
- | 246 | uint32_t *buffer = iter->buffer; |
|
236 | 247 | ||
237 | gradient_t *gradient = (gradient_t *)image; |
- | |
238 | source_image_t *source = (source_image_t *)image; |
248 | gradient_t *gradient = (gradient_t *)image; |
239 | radial_gradient_t *radial = (radial_gradient_t *)image; |
249 | radial_gradient_t *radial = (radial_gradient_t *)image; |
240 | uint32_t *end = buffer + width; |
250 | uint32_t *end = buffer + width; |
241 | pixman_gradient_walker_t walker; |
251 | pixman_gradient_walker_t walker; |
242 | pixman_vector_t v, unit; |
252 | pixman_vector_t v, unit; |
243 | 253 | ||
244 | /* reference point is the center of the pixel */ |
254 | /* reference point is the center of the pixel */ |
245 | v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; |
255 | v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; |
246 | v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; |
256 | v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; |
247 | v.vector[2] = pixman_fixed_1; |
257 | v.vector[2] = pixman_fixed_1; |
248 | 258 | ||
249 | _pixman_gradient_walker_init (&walker, gradient, source->common.repeat); |
259 | _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); |
250 | 260 | ||
251 | if (source->common.transform) |
261 | if (image->common.transform) |
252 | { |
262 | { |
253 | if (!pixman_transform_point_3d (source->common.transform, &v)) |
263 | if (!pixman_transform_point_3d (image->common.transform, &v)) |
254 | return; |
264 | return iter->buffer; |
255 | 265 | ||
256 | unit.vector[0] = source->common.transform->matrix[0][0]; |
266 | unit.vector[0] = image->common.transform->matrix[0][0]; |
257 | unit.vector[1] = source->common.transform->matrix[1][0]; |
267 | unit.vector[1] = image->common.transform->matrix[1][0]; |
258 | unit.vector[2] = source->common.transform->matrix[2][0]; |
268 | unit.vector[2] = image->common.transform->matrix[2][0]; |
259 | } |
269 | } |
260 | else |
270 | else |
261 | { |
271 | { |
262 | unit.vector[0] = pixman_fixed_1; |
272 | unit.vector[0] = pixman_fixed_1; |
263 | unit.vector[1] = 0; |
273 | unit.vector[1] = 0; |
264 | unit.vector[2] = 0; |
274 | unit.vector[2] = 0; |
265 | } |
275 | } |
266 | 276 | ||
267 | if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) |
277 | if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) |
268 | { |
278 | { |
269 | /* |
279 | /* |
270 | * Given: |
280 | * Given: |
271 | * |
281 | * |
272 | * t = (B ± ⎷(B² - A·C)) / A |
282 | * t = (B ± ⎷(B² - A·C)) / A |
273 | * |
283 | * |
274 | * where |
284 | * where |
275 | * |
285 | * |
276 | * A = cdx² + cdy² - dr² |
286 | * A = cdx² + cdy² - dr² |
277 | * B = pdx·cdx + pdy·cdy + r₁·dr |
287 | * B = pdx·cdx + pdy·cdy + r₁·dr |
278 | * C = pdx² + pdy² - r₁² |
288 | * C = pdx² + pdy² - r₁² |
279 | * det = B² - A·C |
289 | * det = B² - A·C |
280 | * |
290 | * |
281 | * Since we have an affine transformation, we know that (pdx, pdy) |
291 | * Since we have an affine transformation, we know that (pdx, pdy) |
282 | * increase linearly with each pixel, |
292 | * increase linearly with each pixel, |
283 | * |
293 | * |
284 | * pdx = pdx₀ + n·ux, |
294 | * pdx = pdx₀ + n·ux, |
285 | * pdy = pdy₀ + n·uy, |
295 | * pdy = pdy₀ + n·uy, |
286 | * |
296 | * |
287 | * we can then express B, C and det through multiple differentiation. |
297 | * we can then express B, C and det through multiple differentiation. |
288 | */ |
298 | */ |
289 | pixman_fixed_32_32_t b, db, c, dc, ddc; |
299 | pixman_fixed_32_32_t b, db, c, dc, ddc; |
290 | 300 | ||
291 | /* warning: this computation may overflow */ |
301 | /* warning: this computation may overflow */ |
292 | v.vector[0] -= radial->c1.x; |
302 | v.vector[0] -= radial->c1.x; |
293 | v.vector[1] -= radial->c1.y; |
303 | v.vector[1] -= radial->c1.y; |
294 | 304 | ||
295 | /* |
305 | /* |
296 | * B and C are computed and updated exactly. |
306 | * B and C are computed and updated exactly. |
297 | * If fdot was used instead of dot, in the worst case it would |
307 | * If fdot was used instead of dot, in the worst case it would |
298 | * lose 11 bits of precision in each of the multiplication and |
308 | * lose 11 bits of precision in each of the multiplication and |
299 | * summing up would zero out all the bit that were preserved, |
309 | * summing up would zero out all the bit that were preserved, |
300 | * thus making the result 0 instead of the correct one. |
310 | * thus making the result 0 instead of the correct one. |
301 | * This would mean a worst case of unbound relative error or |
311 | * This would mean a worst case of unbound relative error or |
302 | * about 2^10 absolute error |
312 | * about 2^10 absolute error |
303 | */ |
313 | */ |
304 | b = dot (v.vector[0], v.vector[1], radial->c1.radius, |
314 | b = dot (v.vector[0], v.vector[1], radial->c1.radius, |
305 | radial->delta.x, radial->delta.y, radial->delta.radius); |
315 | radial->delta.x, radial->delta.y, radial->delta.radius); |
306 | db = dot (unit.vector[0], unit.vector[1], 0, |
316 | db = dot (unit.vector[0], unit.vector[1], 0, |
307 | radial->delta.x, radial->delta.y, 0); |
317 | radial->delta.x, radial->delta.y, 0); |
308 | 318 | ||
309 | c = dot (v.vector[0], v.vector[1], |
319 | c = dot (v.vector[0], v.vector[1], |
310 | -((pixman_fixed_48_16_t) radial->c1.radius), |
320 | -((pixman_fixed_48_16_t) radial->c1.radius), |
311 | v.vector[0], v.vector[1], radial->c1.radius); |
321 | v.vector[0], v.vector[1], radial->c1.radius); |
312 | dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], |
322 | dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], |
313 | 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], |
323 | 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], |
314 | 0, |
324 | 0, |
315 | unit.vector[0], unit.vector[1], 0); |
325 | unit.vector[0], unit.vector[1], 0); |
316 | ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, |
326 | ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, |
317 | unit.vector[0], unit.vector[1], 0); |
327 | unit.vector[0], unit.vector[1], 0); |
318 | 328 | ||
319 | while (buffer < end) |
329 | while (buffer < end) |
320 | { |
330 | { |
321 | if (!mask || *mask++) |
331 | if (!mask || *mask++) |
322 | { |
332 | { |
323 | *buffer = radial_compute_color (radial->a, b, c, |
333 | *buffer = radial_compute_color (radial->a, b, c, |
324 | radial->inva, |
334 | radial->inva, |
325 | radial->delta.radius, |
335 | radial->delta.radius, |
326 | radial->mindr, |
336 | radial->mindr, |
327 | &walker, |
337 | &walker, |
328 | source->common.repeat); |
338 | image->common.repeat); |
329 | } |
339 | } |
330 | 340 | ||
331 | b += db; |
341 | b += db; |
332 | c += dc; |
342 | c += dc; |
333 | dc += ddc; |
343 | dc += ddc; |
334 | ++buffer; |
344 | ++buffer; |
335 | } |
345 | } |
336 | } |
346 | } |
337 | else |
347 | else |
338 | { |
348 | { |
339 | /* projective */ |
349 | /* projective */ |
340 | /* Warning: |
350 | /* Warning: |
341 | * error propagation guarantees are much looser than in the affine case |
351 | * error propagation guarantees are much looser than in the affine case |
342 | */ |
352 | */ |
343 | while (buffer < end) |
353 | while (buffer < end) |
344 | { |
354 | { |
345 | if (!mask || *mask++) |
355 | if (!mask || *mask++) |
346 | { |
356 | { |
347 | if (v.vector[2] != 0) |
357 | if (v.vector[2] != 0) |
348 | { |
358 | { |
349 | double pdx, pdy, invv2, b, c; |
359 | double pdx, pdy, invv2, b, c; |
350 | 360 | ||
351 | invv2 = 1. * pixman_fixed_1 / v.vector[2]; |
361 | invv2 = 1. * pixman_fixed_1 / v.vector[2]; |
352 | 362 | ||
353 | pdx = v.vector[0] * invv2 - radial->c1.x; |
363 | pdx = v.vector[0] * invv2 - radial->c1.x; |
354 | /* / pixman_fixed_1 */ |
364 | /* / pixman_fixed_1 */ |
355 | 365 | ||
356 | pdy = v.vector[1] * invv2 - radial->c1.y; |
366 | pdy = v.vector[1] * invv2 - radial->c1.y; |
357 | /* / pixman_fixed_1 */ |
367 | /* / pixman_fixed_1 */ |
358 | 368 | ||
359 | b = fdot (pdx, pdy, radial->c1.radius, |
369 | b = fdot (pdx, pdy, radial->c1.radius, |
360 | radial->delta.x, radial->delta.y, |
370 | radial->delta.x, radial->delta.y, |
361 | radial->delta.radius); |
371 | radial->delta.radius); |
362 | /* / pixman_fixed_1 / pixman_fixed_1 */ |
372 | /* / pixman_fixed_1 / pixman_fixed_1 */ |
363 | 373 | ||
364 | c = fdot (pdx, pdy, -radial->c1.radius, |
374 | c = fdot (pdx, pdy, -radial->c1.radius, |
365 | pdx, pdy, radial->c1.radius); |
375 | pdx, pdy, radial->c1.radius); |
366 | /* / pixman_fixed_1 / pixman_fixed_1 */ |
376 | /* / pixman_fixed_1 / pixman_fixed_1 */ |
367 | 377 | ||
368 | *buffer = radial_compute_color (radial->a, b, c, |
378 | *buffer = radial_compute_color (radial->a, b, c, |
369 | radial->inva, |
379 | radial->inva, |
370 | radial->delta.radius, |
380 | radial->delta.radius, |
371 | radial->mindr, |
381 | radial->mindr, |
372 | &walker, |
382 | &walker, |
373 | source->common.repeat); |
383 | image->common.repeat); |
374 | } |
384 | } |
375 | else |
385 | else |
376 | { |
386 | { |
377 | *buffer = 0; |
387 | *buffer = 0; |
378 | } |
388 | } |
379 | } |
389 | } |
380 | 390 | ||
381 | ++buffer; |
391 | ++buffer; |
382 | 392 | ||
383 | v.vector[0] += unit.vector[0]; |
393 | v.vector[0] += unit.vector[0]; |
384 | v.vector[1] += unit.vector[1]; |
394 | v.vector[1] += unit.vector[1]; |
385 | v.vector[2] += unit.vector[2]; |
395 | v.vector[2] += unit.vector[2]; |
386 | } |
396 | } |
387 | } |
397 | } |
- | 398 | ||
- | 399 | iter->y++; |
|
- | 400 | return iter->buffer; |
|
388 | } |
401 | } |
389 | 402 | ||
390 | static void |
403 | static uint32_t * |
391 | radial_gradient_property_changed (pixman_image_t *image) |
404 | radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) |
- | 405 | { |
|
- | 406 | uint32_t *buffer = radial_get_scanline_narrow (iter, NULL); |
|
- | 407 | ||
- | 408 | pixman_expand_to_float ( |
|
- | 409 | (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); |
|
- | 410 | ||
- | 411 | return buffer; |
|
- | 412 | } |
|
- | 413 | ||
- | 414 | void |
|
- | 415 | _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) |
|
- | 416 | { |
|
392 | { |
417 | if (iter->iter_flags & ITER_NARROW) |
- | 418 | iter->get_scanline = radial_get_scanline_narrow; |
|
393 | image->common.get_scanline_32 = radial_gradient_get_scanline_32; |
419 | else |
394 | image->common.get_scanline_64 = _pixman_image_get_scanline_generic_64; |
420 | iter->get_scanline = radial_get_scanline_wide; |
395 | } |
421 | } |
396 | 422 | ||
397 | PIXMAN_EXPORT pixman_image_t * |
423 | PIXMAN_EXPORT pixman_image_t * |
398 | pixman_image_create_radial_gradient (pixman_point_fixed_t * inner, |
424 | pixman_image_create_radial_gradient (const pixman_point_fixed_t * inner, |
399 | pixman_point_fixed_t * outer, |
425 | const pixman_point_fixed_t * outer, |
400 | pixman_fixed_t inner_radius, |
426 | pixman_fixed_t inner_radius, |
401 | pixman_fixed_t outer_radius, |
427 | pixman_fixed_t outer_radius, |
402 | const pixman_gradient_stop_t *stops, |
428 | const pixman_gradient_stop_t *stops, |
403 | int n_stops) |
429 | int n_stops) |
404 | { |
430 | { |
405 | pixman_image_t *image; |
431 | pixman_image_t *image; |
406 | radial_gradient_t *radial; |
432 | radial_gradient_t *radial; |
407 | 433 | ||
408 | image = _pixman_image_allocate (); |
434 | image = _pixman_image_allocate (); |
409 | 435 | ||
410 | if (!image) |
436 | if (!image) |
411 | return NULL; |
437 | return NULL; |
412 | 438 | ||
413 | radial = &image->radial; |
439 | radial = &image->radial; |
414 | 440 | ||
415 | if (!_pixman_init_gradient (&radial->common, stops, n_stops)) |
441 | if (!_pixman_init_gradient (&radial->common, stops, n_stops)) |
416 | { |
442 | { |
417 | free (image); |
443 | free (image); |
418 | return NULL; |
444 | return NULL; |
419 | } |
445 | } |
420 | 446 | ||
421 | image->type = RADIAL; |
447 | image->type = RADIAL; |
422 | 448 | ||
423 | radial->c1.x = inner->x; |
449 | radial->c1.x = inner->x; |
424 | radial->c1.y = inner->y; |
450 | radial->c1.y = inner->y; |
425 | radial->c1.radius = inner_radius; |
451 | radial->c1.radius = inner_radius; |
426 | radial->c2.x = outer->x; |
452 | radial->c2.x = outer->x; |
427 | radial->c2.y = outer->y; |
453 | radial->c2.y = outer->y; |
428 | radial->c2.radius = outer_radius; |
454 | radial->c2.radius = outer_radius; |
429 | 455 | ||
430 | /* warning: this computations may overflow */ |
456 | /* warning: this computations may overflow */ |
431 | radial->delta.x = radial->c2.x - radial->c1.x; |
457 | radial->delta.x = radial->c2.x - radial->c1.x; |
432 | radial->delta.y = radial->c2.y - radial->c1.y; |
458 | radial->delta.y = radial->c2.y - radial->c1.y; |
433 | radial->delta.radius = radial->c2.radius - radial->c1.radius; |
459 | radial->delta.radius = radial->c2.radius - radial->c1.radius; |
434 | 460 | ||
435 | /* computed exactly, then cast to double -> every bit of the double |
461 | /* computed exactly, then cast to double -> every bit of the double |
436 | representation is correct (53 bits) */ |
462 | representation is correct (53 bits) */ |
437 | radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius, |
463 | radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius, |
438 | radial->delta.x, radial->delta.y, radial->delta.radius); |
464 | radial->delta.x, radial->delta.y, radial->delta.radius); |
439 | if (radial->a != 0) |
465 | if (radial->a != 0) |
440 | radial->inva = 1. * pixman_fixed_1 / radial->a; |
466 | radial->inva = 1. * pixman_fixed_1 / radial->a; |
441 | 467 | ||
442 | radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius; |
468 | radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius; |
443 | - | ||
444 | image->common.property_changed = radial_gradient_property_changed; |
- | |
445 | 469 | ||
446 | return image; |
470 | return image; |
447 | }>>=>=>>>=>=>=>=>=>=> |
- | |
- | 471 | }>>=>=>>>=>=>=>=>>=>=> |