Eliminate mask_bits from all the scanline fetchers.
[profile/ivi/pixman.git] / pixman / pixman-radial-gradient.c
1 /*
2  *
3  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
4  * Copyright © 2000 SuSE, Inc.
5  *             2005 Lars Knoll & Zack Rusin, Trolltech
6  * Copyright © 2007 Red Hat, Inc.
7  *
8  *
9  * Permission to use, copy, modify, distribute, and sell this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation, and that the name of Keith Packard not be used in
14  * advertising or publicity pertaining to distribution of the software without
15  * specific, written prior permission.  Keith Packard makes no
16  * representations about the suitability of this software for any purpose.  It
17  * is provided "as is" without express or implied warranty.
18  *
19  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
20  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
22  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
24  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
25  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26  * SOFTWARE.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 #include <stdlib.h>
33 #include <math.h>
34 #include "pixman-private.h"
35
36 static void
37 radial_gradient_get_scanline_32 (pixman_image_t *image,
38                                  int             x,
39                                  int             y,
40                                  int             width,
41                                  uint32_t *      buffer,
42                                  const uint32_t *mask)
43 {
44     /*
45      * In the radial gradient problem we are given two circles (c₁,r₁) and
46      * (c₂,r₂) that define the gradient itself. Then, for any point p, we
47      * must compute the value(s) of t within [0.0, 1.0] representing the
48      * circle(s) that would color the point.
49      *
50      * There are potentially two values of t since the point p can be
51      * colored by both sides of the circle, (which happens whenever one
52      * circle is not entirely contained within the other).
53      *
54      * If we solve for a value of t that is outside of [0.0, 1.0] then we
55      * use the extend mode (NONE, REPEAT, REFLECT, or PAD) to map to a
56      * value within [0.0, 1.0].
57      *
58      * Here is an illustration of the problem:
59      *
60      *              p₂
61      *           p  •
62      *           •   ╲
63      *        ·       ╲r₂
64      *  p₁ ·           ╲
65      *  •              θ╲
66      *   ╲             ╌╌•
67      *    ╲r₁        ·   c₂
68      *    θ╲    ·
69      *    ╌╌•
70      *      c₁
71      *
72      * Given (c₁,r₁), (c₂,r₂) and p, we must find an angle θ such that two
73      * points p₁ and p₂ on the two circles are collinear with p. Then, the
74      * desired value of t is the ratio of the length of p₁p to the length
75      * of p₁p₂.
76      *
77      * So, we have six unknown values: (p₁x, p₁y), (p₂x, p₂y), θ and t.
78      * We can also write six equations that constrain the problem:
79      *
80      * Point p₁ is a distance r₁ from c₁ at an angle of θ:
81      *
82      *  1. p₁x = c₁x + r₁·cos θ
83      *  2. p₁y = c₁y + r₁·sin θ
84      *
85      * Point p₂ is a distance r₂ from c₂ at an angle of θ:
86      *
87      *  3. p₂x = c₂x + r2·cos θ
88      *  4. p₂y = c₂y + r2·sin θ
89      *
90      * Point p lies at a fraction t along the line segment p₁p₂:
91      *
92      *  5. px = t·p₂x + (1-t)·p₁x
93      *  6. py = t·p₂y + (1-t)·p₁y
94      *
95      * To solve, first subtitute 1-4 into 5 and 6:
96      *
97      * px = t·(c₂x + r₂·cos θ) + (1-t)·(c₁x + r₁·cos θ)
98      * py = t·(c₂y + r₂·sin θ) + (1-t)·(c₁y + r₁·sin θ)
99      *
100      * Then solve each for cos θ and sin θ expressed as a function of t:
101      *
102      * cos θ = (-(c₂x - c₁x)·t + (px - c₁x)) / ((r₂-r₁)·t + r₁)
103      * sin θ = (-(c₂y - c₁y)·t + (py - c₁y)) / ((r₂-r₁)·t + r₁)
104      *
105      * To simplify this a bit, we define new variables for several of the
106      * common terms as shown below:
107      *
108      *              p₂
109      *           p  •
110      *           •   ╲
111      *        ·  ┆    ╲r₂
112      *  p₁ ·     ┆     ╲
113      *  •     pdy┆      ╲
114      *   ╲       ┆       •c₂
115      *    ╲r₁    ┆   ·   ┆
116      *     ╲    ·┆       ┆cdy
117      *      •╌╌╌╌┴╌╌╌╌╌╌╌┘
118      *    c₁  pdx   cdx
119      *
120      * cdx = (c₂x - c₁x)
121      * cdy = (c₂y - c₁y)
122      *  dr =  r₂-r₁
123      * pdx =  px - c₁x
124      * pdy =  py - c₁y
125      *
126      * Note that cdx, cdy, and dr do not depend on point p at all, so can
127      * be pre-computed for the entire gradient. The simplifed equations
128      * are now:
129      *
130      * cos θ = (-cdx·t + pdx) / (dr·t + r₁)
131      * sin θ = (-cdy·t + pdy) / (dr·t + r₁)
132      *
133      * Finally, to get a single function of t and eliminate the last
134      * unknown θ, we use the identity sin²θ + cos²θ = 1. First, square
135      * each equation, (we knew a quadratic was coming since it must be
136      * possible to obtain two solutions in some cases):
137      *
138      * cos²θ = (cdx²t² - 2·cdx·pdx·t + pdx²) / (dr²·t² + 2·r₁·dr·t + r₁²)
139      * sin²θ = (cdy²t² - 2·cdy·pdy·t + pdy²) / (dr²·t² + 2·r₁·dr·t + r₁²)
140      *
141      * Then add both together, set the result equal to 1, and express as a
142      * standard quadratic equation in t of the form At² + Bt + C = 0
143      *
144      * (cdx² + cdy² - dr²)·t² - 2·(cdx·pdx + cdy·pdy + r₁·dr)·t + (pdx² + pdy² - r₁²) = 0
145      *
146      * In other words:
147      *
148      * A = cdx² + cdy² - dr²
149      * B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
150      * C = pdx² + pdy² - r₁²
151      *
152      * And again, notice that A does not depend on p, so can be
153      * precomputed. From here we just use the quadratic formula to solve
154      * for t:
155      *
156      * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
157      */
158
159     gradient_t *gradient = (gradient_t *)image;
160     source_image_t *source = (source_image_t *)image;
161     radial_gradient_t *radial = (radial_gradient_t *)image;
162     uint32_t *end = buffer + width;
163     pixman_gradient_walker_t walker;
164     pixman_bool_t affine = TRUE;
165     double cx = 1.;
166     double cy = 0.;
167     double cz = 0.;
168     double rx = x + 0.5;
169     double ry = y + 0.5;
170     double rz = 1.;
171
172     _pixman_gradient_walker_init (&walker, gradient, source->common.repeat);
173
174     if (source->common.transform)
175     {
176         pixman_vector_t v;
177         /* reference point is the center of the pixel */
178         v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
179         v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
180         v.vector[2] = pixman_fixed_1;
181         
182         if (!pixman_transform_point_3d (source->common.transform, &v))
183             return;
184
185         cx = source->common.transform->matrix[0][0] / 65536.;
186         cy = source->common.transform->matrix[1][0] / 65536.;
187         cz = source->common.transform->matrix[2][0] / 65536.;
188         
189         rx = v.vector[0] / 65536.;
190         ry = v.vector[1] / 65536.;
191         rz = v.vector[2] / 65536.;
192
193         affine =
194             source->common.transform->matrix[2][0] == 0 &&
195             v.vector[2] == pixman_fixed_1;
196     }
197
198     if (affine)
199     {
200         /* When computing t over a scanline, we notice that some expressions
201          * are constant so we can compute them just once. Given:
202          *
203          * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
204          *
205          * where
206          *
207          * A = cdx² + cdy² - dr² [precomputed as radial->A]
208          * B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
209          * C = pdx² + pdy² - r₁²
210          *
211          * Since we have an affine transformation, we know that (pdx, pdy)
212          * increase linearly with each pixel,
213          *
214          * pdx = pdx₀ + n·cx,
215          * pdy = pdy₀ + n·cy,
216          *
217          * we can then express B in terms of an linear increment along
218          * the scanline:
219          *
220          * B = B₀ + n·cB, with
221          * B₀ = -2·(pdx₀·cdx + pdy₀·cdy + r₁·dr) and
222          * cB = -2·(cx·cdx + cy·cdy)
223          *
224          * Thus we can replace the full evaluation of B per-pixel (4 multiplies,
225          * 2 additions) with a single addition.
226          */
227         double r1   = radial->c1.radius / 65536.;
228         double r1sq = r1 * r1;
229         double pdx  = rx - radial->c1.x / 65536.;
230         double pdy  = ry - radial->c1.y / 65536.;
231         double A = radial->A;
232         double invA = -65536. / (2. * A);
233         double A4 = -4. * A;
234         double B  = -2. * (pdx*radial->cdx + pdy*radial->cdy + r1*radial->dr);
235         double cB = -2. *  (cx*radial->cdx +  cy*radial->cdy);
236         pixman_bool_t invert = A * radial->dr < 0;
237
238         while (buffer < end)
239         {
240             if (!mask || *mask++)
241             {
242                 pixman_fixed_48_16_t t;
243                 double det = B * B + A4 * (pdx * pdx + pdy * pdy - r1sq);
244                 if (det <= 0.)
245                     t = (pixman_fixed_48_16_t) (B * invA);
246                 else if (invert)
247                     t = (pixman_fixed_48_16_t) ((B + sqrt (det)) * invA);
248                 else
249                     t = (pixman_fixed_48_16_t) ((B - sqrt (det)) * invA);
250
251                 *buffer = _pixman_gradient_walker_pixel (&walker, t);
252             }
253             ++buffer;
254
255             pdx += cx;
256             pdy += cy;
257             B += cB;
258         }
259     }
260     else
261     {
262         /* projective */
263         while (buffer < end)
264         {
265             if (!mask || *mask++)
266             {
267                 double pdx, pdy;
268                 double B, C;
269                 double det;
270                 double c1x = radial->c1.x / 65536.0;
271                 double c1y = radial->c1.y / 65536.0;
272                 double r1  = radial->c1.radius / 65536.0;
273                 pixman_fixed_48_16_t t;
274                 double x, y;
275
276                 if (rz != 0)
277                 {
278                     x = rx / rz;
279                     y = ry / rz;
280                 }
281                 else
282                 {
283                     x = y = 0.;
284                 }
285
286                 pdx = x - c1x;
287                 pdy = y - c1y;
288
289                 B = -2 * (pdx * radial->cdx +
290                           pdy * radial->cdy +
291                           r1 * radial->dr);
292                 C = (pdx * pdx + pdy * pdy - r1 * r1);
293
294                 det = (B * B) - (4 * radial->A * C);
295                 if (det < 0.0)
296                     det = 0.0;
297
298                 if (radial->A * radial->dr < 0)
299                     t = (pixman_fixed_48_16_t) ((-B - sqrt (det)) / (2.0 * radial->A) * 65536);
300                 else
301                     t = (pixman_fixed_48_16_t) ((-B + sqrt (det)) / (2.0 * radial->A) * 65536);
302
303                 *buffer = _pixman_gradient_walker_pixel (&walker, t);
304             }
305             
306             ++buffer;
307
308             rx += cx;
309             ry += cy;
310             rz += cz;
311         }
312     }
313 }
314
315 static void
316 radial_gradient_property_changed (pixman_image_t *image)
317 {
318     image->common.get_scanline_32 = radial_gradient_get_scanline_32;
319     image->common.get_scanline_64 = _pixman_image_get_scanline_generic_64;
320 }
321
322 PIXMAN_EXPORT pixman_image_t *
323 pixman_image_create_radial_gradient (pixman_point_fixed_t *        inner,
324                                      pixman_point_fixed_t *        outer,
325                                      pixman_fixed_t                inner_radius,
326                                      pixman_fixed_t                outer_radius,
327                                      const pixman_gradient_stop_t *stops,
328                                      int                           n_stops)
329 {
330     pixman_image_t *image;
331     radial_gradient_t *radial;
332
333     return_val_if_fail (n_stops >= 2, NULL);
334
335     image = _pixman_image_allocate ();
336
337     if (!image)
338         return NULL;
339
340     radial = &image->radial;
341
342     if (!_pixman_init_gradient (&radial->common, stops, n_stops))
343     {
344         free (image);
345         return NULL;
346     }
347
348     image->type = RADIAL;
349
350     radial->c1.x = inner->x;
351     radial->c1.y = inner->y;
352     radial->c1.radius = inner_radius;
353     radial->c2.x = outer->x;
354     radial->c2.y = outer->y;
355     radial->c2.radius = outer_radius;
356     radial->cdx = pixman_fixed_to_double (radial->c2.x - radial->c1.x);
357     radial->cdy = pixman_fixed_to_double (radial->c2.y - radial->c1.y);
358     radial->dr = pixman_fixed_to_double (radial->c2.radius - radial->c1.radius);
359     radial->A = (radial->cdx * radial->cdx +
360                  radial->cdy * radial->cdy -
361                  radial->dr  * radial->dr);
362
363     image->common.property_changed = radial_gradient_property_changed;
364
365     return image;
366 }
367