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