Move iterator initialization to the respective image files
[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 void
95 linear_get_scanline_32 (pixman_image_t *image,
96                         int             x,
97                         int             y,
98                         int             width,
99                         uint32_t *      buffer,
100                         const uint32_t *mask)
101 {
102     pixman_vector_t v, unit;
103     pixman_fixed_32_32_t l;
104     pixman_fixed_48_16_t dx, dy;
105     gradient_t *gradient = (gradient_t *)image;
106     linear_gradient_t *linear = (linear_gradient_t *)image;
107     uint32_t *end = buffer + width;
108     pixman_gradient_walker_t walker;
109
110     _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
111
112     /* reference point is the center of the pixel */
113     v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
114     v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
115     v.vector[2] = pixman_fixed_1;
116
117     if (image->common.transform)
118     {
119         if (!pixman_transform_point_3d (image->common.transform, &v))
120             return;
121
122         unit.vector[0] = image->common.transform->matrix[0][0];
123         unit.vector[1] = image->common.transform->matrix[1][0];
124         unit.vector[2] = image->common.transform->matrix[2][0];
125     }
126     else
127     {
128         unit.vector[0] = pixman_fixed_1;
129         unit.vector[1] = 0;
130         unit.vector[2] = 0;
131     }
132
133     dx = linear->p2.x - linear->p1.x;
134     dy = linear->p2.y - linear->p1.y;
135
136     l = dx * dx + dy * dy;
137
138     if (l == 0 || unit.vector[2] == 0)
139     {
140         /* affine transformation only */
141         pixman_fixed_32_32_t t, next_inc;
142         double inc;
143
144         if (l == 0 || v.vector[2] == 0)
145         {
146             t = 0;
147             inc = 0;
148         }
149         else
150         {
151             double invden, v2;
152
153             invden = pixman_fixed_1 * (double) pixman_fixed_1 /
154                 (l * (double) v.vector[2]);
155             v2 = v.vector[2] * (1. / pixman_fixed_1);
156             t = ((dx * v.vector[0] + dy * v.vector[1]) - 
157                  (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
158             inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
159         }
160         next_inc = 0;
161
162         if (((pixman_fixed_32_32_t )(inc * width)) == 0)
163         {
164             register uint32_t color;
165
166             color = _pixman_gradient_walker_pixel (&walker, t);
167             while (buffer < end)
168                 *buffer++ = color;
169         }
170         else
171         {
172             int i;
173
174             i = 0;
175             while (buffer < end)
176             {
177                 if (!mask || *mask++)
178                 {
179                     *buffer = _pixman_gradient_walker_pixel (&walker,
180                                                              t + next_inc);
181                 }
182                 i++;
183                 next_inc = inc * i;
184                 buffer++;
185             }
186         }
187     }
188     else
189     {
190         /* projective transformation */
191         double t;
192
193         t = 0;
194
195         while (buffer < end)
196         {
197             if (!mask || *mask++)
198             {
199                 if (v.vector[2] != 0)
200                 {
201                     double invden, v2;
202
203                     invden = pixman_fixed_1 * (double) pixman_fixed_1 /
204                         (l * (double) v.vector[2]);
205                     v2 = v.vector[2] * (1. / pixman_fixed_1);
206                     t = ((dx * v.vector[0] + dy * v.vector[1]) - 
207                          (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
208                 }
209
210                 *buffer = _pixman_gradient_walker_pixel (&walker, t);
211             }
212
213             ++buffer;
214
215             v.vector[0] += unit.vector[0];
216             v.vector[1] += unit.vector[1];
217             v.vector[2] += unit.vector[2];
218         }
219     }
220 }
221
222 static void
223 linear_gradient_property_changed (pixman_image_t *image)
224 {
225     image->common.get_scanline_32 = linear_get_scanline_32;
226     image->common.get_scanline_64 = _pixman_image_get_scanline_generic_64;
227 }
228
229 static uint32_t *
230 linear_get_scanline_narrow (pixman_iter_t  *iter,
231                             const uint32_t *mask)
232 {
233     pixman_image_t *image  = iter->image;
234     int             x      = iter->x;
235     int             y      = iter->y;
236     int             width  = iter->width;
237     uint32_t *      buffer = iter->buffer;
238
239     linear_get_scanline_32 (image, x, y, width, buffer, mask);
240
241     iter->y++;
242
243     return iter->buffer;
244 }
245
246 static uint32_t *
247 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
248 {
249     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
250
251     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
252
253     return buffer;
254 }
255
256 void
257 _pixman_linear_gradient_iter_init (pixman_image_t *image,
258                                    pixman_iter_t  *iter,
259                                    int             x,
260                                    int             y,
261                                    int             width,
262                                    int             height,
263                                    uint8_t        *buffer,
264                                    iter_flags_t    flags)
265 {
266     if (flags & ITER_NARROW)
267         iter->get_scanline = linear_get_scanline_narrow;
268     else
269         iter->get_scanline = linear_get_scanline_wide;
270 }
271
272 PIXMAN_EXPORT pixman_image_t *
273 pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
274                                      pixman_point_fixed_t *        p2,
275                                      const pixman_gradient_stop_t *stops,
276                                      int                           n_stops)
277 {
278     pixman_image_t *image;
279     linear_gradient_t *linear;
280
281     image = _pixman_image_allocate ();
282
283     if (!image)
284         return NULL;
285
286     linear = &image->linear;
287
288     if (!_pixman_init_gradient (&linear->common, stops, n_stops))
289     {
290         free (image);
291         return NULL;
292     }
293
294     linear->p1 = *p1;
295     linear->p2 = *p2;
296
297     image->type = LINEAR;
298     image->common.classify = linear_gradient_classify;
299     image->common.property_changed = linear_gradient_property_changed;
300
301     return image;
302 }
303