66d37ab978134677ac866097908352767f467b1e
[profile/ivi/pixman.git] / pixman / pixman-linear-gradient.c
1 /* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */
2 /*
3  * Copyright © 2000 SuSE, Inc.
4  * Copyright © 2007 Red Hat, Inc.
5  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
6  *             2005 Lars Knoll & Zack Rusin, Trolltech
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation, and that the name of Keith Packard not be used in
13  * advertising or publicity pertaining to distribution of the software without
14  * specific, written prior permission.  Keith Packard makes no
15  * representations about the suitability of this software for any purpose.  It
16  * is provided "as is" without express or implied warranty.
17  *
18  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
24  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25  * SOFTWARE.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <stdlib.h>
32 #include "pixman-private.h"
33
34 static source_image_class_t
35 linear_gradient_classify (pixman_image_t *image,
36                           int             x,
37                           int             y,
38                           int             width,
39                           int             height)
40 {
41     linear_gradient_t *linear = (linear_gradient_t *)image;
42     pixman_vector_t v;
43     pixman_fixed_32_32_t l;
44     pixman_fixed_48_16_t dx, dy;
45     double inc;
46     source_image_class_t class;
47
48     class = SOURCE_IMAGE_CLASS_UNKNOWN;
49
50     if (image->common.transform)
51     {
52         /* projective transformation */
53         if (image->common.transform->matrix[2][0] != 0 ||
54             image->common.transform->matrix[2][1] != 0 ||
55             image->common.transform->matrix[2][2] == 0)
56         {
57             return class;
58         }
59
60         v.vector[0] = image->common.transform->matrix[0][1];
61         v.vector[1] = image->common.transform->matrix[1][1];
62         v.vector[2] = image->common.transform->matrix[2][2];
63     }
64     else
65     {
66         v.vector[0] = 0;
67         v.vector[1] = pixman_fixed_1;
68         v.vector[2] = pixman_fixed_1;
69     }
70
71     dx = linear->p2.x - linear->p1.x;
72     dy = linear->p2.y - linear->p1.y;
73
74     l = dx * dx + dy * dy;
75
76     if (l == 0)
77         return class;   
78
79     /*
80      * compute how much the input of the gradient walked changes
81      * when moving vertically through the whole image
82      */
83     inc = height * (double) pixman_fixed_1 * pixman_fixed_1 *
84         (dx * v.vector[0] + dy * v.vector[1]) /
85         (v.vector[2] * (double) l);
86
87     /* check that casting to integer would result in 0 */
88     if (-1 < inc && inc < 1)
89         class = SOURCE_IMAGE_CLASS_HORIZONTAL;
90
91     return class;
92 }
93
94 static uint32_t *
95 linear_get_scanline_narrow (pixman_iter_t  *iter,
96                             const uint32_t *mask)
97 {
98     pixman_image_t *image  = iter->image;
99     int             x      = iter->x;
100     int             y      = iter->y;
101     int             width  = iter->width;
102     uint32_t *      buffer = iter->buffer;
103
104     pixman_vector_t v, unit;
105     pixman_fixed_32_32_t l;
106     pixman_fixed_48_16_t dx, dy;
107     gradient_t *gradient = (gradient_t *)image;
108     linear_gradient_t *linear = (linear_gradient_t *)image;
109     uint32_t *end = buffer + width;
110     pixman_gradient_walker_t walker;
111
112     _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
113
114     /* reference point is the center of the pixel */
115     v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
116     v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
117     v.vector[2] = pixman_fixed_1;
118
119     if (image->common.transform)
120     {
121         if (!pixman_transform_point_3d (image->common.transform, &v))
122             return iter->buffer;
123
124         unit.vector[0] = image->common.transform->matrix[0][0];
125         unit.vector[1] = image->common.transform->matrix[1][0];
126         unit.vector[2] = image->common.transform->matrix[2][0];
127     }
128     else
129     {
130         unit.vector[0] = pixman_fixed_1;
131         unit.vector[1] = 0;
132         unit.vector[2] = 0;
133     }
134
135     dx = linear->p2.x - linear->p1.x;
136     dy = linear->p2.y - linear->p1.y;
137
138     l = dx * dx + dy * dy;
139
140     if (l == 0 || unit.vector[2] == 0)
141     {
142         /* affine transformation only */
143         pixman_fixed_32_32_t t, next_inc;
144         double inc;
145
146         if (l == 0 || v.vector[2] == 0)
147         {
148             t = 0;
149             inc = 0;
150         }
151         else
152         {
153             double invden, v2;
154
155             invden = pixman_fixed_1 * (double) pixman_fixed_1 /
156                 (l * (double) v.vector[2]);
157             v2 = v.vector[2] * (1. / pixman_fixed_1);
158             t = ((dx * v.vector[0] + dy * v.vector[1]) - 
159                  (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
160             inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
161         }
162         next_inc = 0;
163
164         if (((pixman_fixed_32_32_t )(inc * width)) == 0)
165         {
166             register uint32_t color;
167
168             color = _pixman_gradient_walker_pixel (&walker, t);
169             while (buffer < end)
170                 *buffer++ = color;
171         }
172         else
173         {
174             int i;
175
176             i = 0;
177             while (buffer < end)
178             {
179                 if (!mask || *mask++)
180                 {
181                     *buffer = _pixman_gradient_walker_pixel (&walker,
182                                                              t + next_inc);
183                 }
184                 i++;
185                 next_inc = inc * i;
186                 buffer++;
187             }
188         }
189     }
190     else
191     {
192         /* projective transformation */
193         double t;
194
195         t = 0;
196
197         while (buffer < end)
198         {
199             if (!mask || *mask++)
200             {
201                 if (v.vector[2] != 0)
202                 {
203                     double invden, v2;
204
205                     invden = pixman_fixed_1 * (double) pixman_fixed_1 /
206                         (l * (double) v.vector[2]);
207                     v2 = v.vector[2] * (1. / pixman_fixed_1);
208                     t = ((dx * v.vector[0] + dy * v.vector[1]) - 
209                          (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
210                 }
211
212                 *buffer = _pixman_gradient_walker_pixel (&walker, t);
213             }
214
215             ++buffer;
216
217             v.vector[0] += unit.vector[0];
218             v.vector[1] += unit.vector[1];
219             v.vector[2] += unit.vector[2];
220         }
221     }
222
223     iter->y++;
224
225     return iter->buffer;
226 }
227
228 static uint32_t *
229 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
230 {
231     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
232
233     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
234
235     return buffer;
236 }
237
238 void
239 _pixman_linear_gradient_iter_init (pixman_image_t *image,
240                                    pixman_iter_t  *iter,
241                                    int             x,
242                                    int             y,
243                                    int             width,
244                                    int             height,
245                                    uint8_t        *buffer,
246                                    iter_flags_t    flags)
247 {
248     if (linear_gradient_classify (image, x, y, width, height) ==
249         SOURCE_IMAGE_CLASS_HORIZONTAL)
250     {
251         if (flags & ITER_NARROW)
252             linear_get_scanline_narrow (iter, NULL);
253         else
254             linear_get_scanline_wide (iter, NULL);
255
256         iter->get_scanline = _pixman_iter_get_scanline_noop;
257     }
258     else
259     {
260         if (flags & ITER_NARROW)
261             iter->get_scanline = linear_get_scanline_narrow;
262         else
263             iter->get_scanline = linear_get_scanline_wide;
264     }
265 }
266
267 PIXMAN_EXPORT pixman_image_t *
268 pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
269                                      pixman_point_fixed_t *        p2,
270                                      const pixman_gradient_stop_t *stops,
271                                      int                           n_stops)
272 {
273     pixman_image_t *image;
274     linear_gradient_t *linear;
275
276     image = _pixman_image_allocate ();
277
278     if (!image)
279         return NULL;
280
281     linear = &image->linear;
282
283     if (!_pixman_init_gradient (&linear->common, stops, n_stops))
284     {
285         free (image);
286         return NULL;
287     }
288
289     linear->p1 = *p1;
290     linear->p2 = *p2;
291
292     image->type = LINEAR;
293     image->common.classify = linear_gradient_classify;
294
295     return image;
296 }
297