Move the gradient walker code to its own file
[profile/ivi/pixman.git] / pixman / pixman-gradient-walker.c
1 /*
2  *
3  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
4  *             2005 Lars Knoll & Zack Rusin, Trolltech
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
21  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23  * SOFTWARE.
24  */
25
26 #include <config.h>
27 #include "pixman-private.h"
28
29 void
30 _pixman_gradient_walker_init (GradientWalker  *walker,
31                               gradient_t      *gradient,
32                               unsigned int     spread)
33 {
34     walker->num_stops = gradient->n_stops;
35     walker->stops     = gradient->stops;
36     walker->left_x    = 0;
37     walker->right_x   = 0x10000;
38     walker->stepper   = 0;
39     walker->left_ag   = 0;
40     walker->left_rb   = 0;
41     walker->right_ag  = 0;
42     walker->right_rb  = 0;
43     walker->spread    = spread;
44     
45     walker->need_reset = TRUE;
46 }
47
48 void
49 _pixman_gradient_walker_reset (GradientWalker       *walker,
50                                pixman_fixed_32_32_t  pos)
51 {
52     int32_t                  x, left_x, right_x;
53     pixman_color_t          *left_c, *right_c;
54     int                      n, count = walker->num_stops;
55     pixman_gradient_stop_t *      stops = walker->stops;
56     
57     static const pixman_color_t   transparent_black = { 0, 0, 0, 0 };
58     
59     switch (walker->spread)
60     {
61     case PIXMAN_REPEAT_NORMAL:
62         x = (int32_t)pos & 0xFFFF;
63         for (n = 0; n < count; n++)
64             if (x < stops[n].x)
65                 break;
66         if (n == 0) {
67             left_x =  stops[count-1].x - 0x10000;
68             left_c = &stops[count-1].color;
69         } else {
70             left_x =  stops[n-1].x;
71             left_c = &stops[n-1].color;
72         }
73         
74         if (n == count) {
75             right_x =  stops[0].x + 0x10000;
76             right_c = &stops[0].color;
77         } else {
78             right_x =  stops[n].x;
79             right_c = &stops[n].color;
80         }
81         left_x  += (pos - x);
82         right_x += (pos - x);
83         break;
84         
85     case PIXMAN_REPEAT_PAD:
86         for (n = 0; n < count; n++)
87             if (pos < stops[n].x)
88                 break;
89         
90         if (n == 0) {
91             left_x =  INT32_MIN;
92             left_c = &stops[0].color;
93         } else {
94             left_x =  stops[n-1].x;
95             left_c = &stops[n-1].color;
96         }
97         
98         if (n == count) {
99             right_x =  INT32_MAX;
100             right_c = &stops[n-1].color;
101         } else {
102             right_x =  stops[n].x;
103             right_c = &stops[n].color;
104         }
105         break;
106         
107     case PIXMAN_REPEAT_REFLECT:
108         x = (int32_t)pos & 0xFFFF;
109         if ((int32_t)pos & 0x10000)
110             x = 0x10000 - x;
111         for (n = 0; n < count; n++)
112             if (x < stops[n].x)
113                 break;
114         
115         if (n == 0) {
116             left_x =  -stops[0].x;
117             left_c = &stops[0].color;
118         } else {
119             left_x =  stops[n-1].x;
120             left_c = &stops[n-1].color;
121         }
122         
123         if (n == count) {
124             right_x = 0x20000 - stops[n-1].x;
125             right_c = &stops[n-1].color;
126         } else {
127             right_x =  stops[n].x;
128             right_c = &stops[n].color;
129         }
130         
131         if ((int32_t)pos & 0x10000) {
132             pixman_color_t  *tmp_c;
133             int32_t          tmp_x;
134             
135             tmp_x   = 0x10000 - right_x;
136             right_x = 0x10000 - left_x;
137             left_x  = tmp_x;
138             
139             tmp_c   = right_c;
140             right_c = left_c;
141             left_c  = tmp_c;
142             
143             x = 0x10000 - x;
144         }
145         left_x  += (pos - x);
146         right_x += (pos - x);
147         break;
148         
149     default:  /* RepeatNone */
150         for (n = 0; n < count; n++)
151             if (pos < stops[n].x)
152                 break;
153         
154         if (n == 0)
155         {
156             left_x  =  INT32_MIN;
157             right_x =  stops[0].x;
158             left_c  = right_c = (pixman_color_t*) &transparent_black;
159         }
160         else if (n == count)
161         {
162             left_x  = stops[n-1].x;
163             right_x = INT32_MAX;
164             left_c  = right_c = (pixman_color_t*) &transparent_black;
165         }
166         else
167         {
168             left_x  =  stops[n-1].x;
169             right_x =  stops[n].x;
170             left_c  = &stops[n-1].color;
171             right_c = &stops[n].color;
172         }
173     }
174     
175     walker->left_x   = left_x;
176     walker->right_x  = right_x;
177     walker->left_ag  = ((left_c->alpha >> 8) << 16)   | (left_c->green >> 8);
178     walker->left_rb  = ((left_c->red & 0xff00) << 8)  | (left_c->blue >> 8);
179     walker->right_ag = ((right_c->alpha >> 8) << 16)  | (right_c->green >> 8);
180     walker->right_rb = ((right_c->red & 0xff00) << 8) | (right_c->blue >> 8);
181     
182     if ( walker->left_x == walker->right_x                ||
183          ( walker->left_ag == walker->right_ag &&
184            walker->left_rb == walker->right_rb )   )
185     {
186         walker->stepper = 0;
187     }
188     else
189     {
190         int32_t width = right_x - left_x;
191         walker->stepper = ((1 << 24) + width/2)/width;
192     }
193     
194     walker->need_reset = FALSE;
195 }
196
197 #define  PIXMAN_GRADIENT_WALKER_NEED_RESET(w,x)                         \
198     ( (w)->need_reset || (x) < (w)->left_x || (x) >= (w)->right_x)
199
200
201 /* the following assumes that PIXMAN_GRADIENT_WALKER_NEED_RESET(w,x) is FALSE */
202 uint32_t
203 _pixman_gradient_walker_pixel (GradientWalker  *walker,
204                                pixman_fixed_32_32_t     x)
205 {
206     int  dist, idist;
207     uint32_t  t1, t2, a, color;
208     
209     if (PIXMAN_GRADIENT_WALKER_NEED_RESET (walker, x))
210         _pixman_gradient_walker_reset (walker, x);
211     
212     dist  = ((int)(x - walker->left_x)*walker->stepper) >> 16;
213     idist = 256 - dist;
214     
215     /* combined INTERPOLATE and premultiply */
216     t1 = walker->left_rb*idist + walker->right_rb*dist;
217     t1 = (t1 >> 8) & 0xff00ff;
218     
219     t2  = walker->left_ag*idist + walker->right_ag*dist;
220     t2 &= 0xff00ff00;
221     
222     color = t2 & 0xff000000;
223     a     = t2 >> 24;
224     
225     t1  = t1*a + 0x800080;
226     t1  = (t1 + ((t1 >> 8) & 0xff00ff)) >> 8;
227     
228     t2  = (t2 >> 8)*a + 0x800080;
229     t2  = (t2 + ((t2 >> 8) & 0xff00ff));
230     
231     return (color | (t1 & 0xff00ff) | (t2 & 0xff00));
232 }