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 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 #include "pixman-private.h"
30 
31 void
_pixman_gradient_walker_init(pixman_gradient_walker_t * walker,gradient_t * gradient,pixman_repeat_t repeat)32 _pixman_gradient_walker_init (pixman_gradient_walker_t *walker,
33                               gradient_t *              gradient,
34                               pixman_repeat_t		repeat)
35 {
36     walker->num_stops = gradient->n_stops;
37     walker->stops     = gradient->stops;
38     walker->left_x    = 0;
39     walker->right_x   = 0x10000;
40     walker->a_s       = 0.0f;
41     walker->a_b       = 0.0f;
42     walker->r_s       = 0.0f;
43     walker->r_b       = 0.0f;
44     walker->g_s       = 0.0f;
45     walker->g_b       = 0.0f;
46     walker->b_s       = 0.0f;
47     walker->b_b       = 0.0f;
48     walker->repeat    = repeat;
49 
50     walker->need_reset = TRUE;
51 }
52 
53 static void
gradient_walker_reset(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t pos)54 gradient_walker_reset (pixman_gradient_walker_t *walker,
55 		       pixman_fixed_48_16_t      pos)
56 {
57     int64_t x, left_x, right_x;
58     pixman_color_t *left_c, *right_c;
59     int n, count = walker->num_stops;
60     pixman_gradient_stop_t *stops = walker->stops;
61     float la, lr, lg, lb;
62     float ra, rr, rg, rb;
63     float lx, rx;
64 
65     if (walker->repeat == PIXMAN_REPEAT_NORMAL)
66     {
67 	x = (int32_t)pos & 0xffff;
68     }
69     else if (walker->repeat == PIXMAN_REPEAT_REFLECT)
70     {
71 	x = (int32_t)pos & 0xffff;
72 	if ((int32_t)pos & 0x10000)
73 	    x = 0x10000 - x;
74     }
75     else
76     {
77 	x = pos;
78     }
79 
80     for (n = 0; n < count; n++)
81     {
82 	if (x < stops[n].x)
83 	    break;
84     }
85 
86     left_x =  stops[n - 1].x;
87     left_c = &stops[n - 1].color;
88 
89     right_x =  stops[n].x;
90     right_c = &stops[n].color;
91 
92     if (walker->repeat == PIXMAN_REPEAT_NORMAL)
93     {
94 	left_x  += (pos - x);
95 	right_x += (pos - x);
96     }
97     else if (walker->repeat == PIXMAN_REPEAT_REFLECT)
98     {
99 	if ((int32_t)pos & 0x10000)
100 	{
101 	    pixman_color_t  *tmp_c;
102 	    int32_t tmp_x;
103 
104 	    tmp_x   = 0x10000 - right_x;
105 	    right_x = 0x10000 - left_x;
106 	    left_x  = tmp_x;
107 
108 	    tmp_c   = right_c;
109 	    right_c = left_c;
110 	    left_c  = tmp_c;
111 
112 	    x = 0x10000 - x;
113 	}
114 	left_x  += (pos - x);
115 	right_x += (pos - x);
116     }
117     else if (walker->repeat == PIXMAN_REPEAT_NONE)
118     {
119 	if (n == 0)
120 	    right_c = left_c;
121 	else if (n == count)
122 	    left_c = right_c;
123     }
124 
125     /* The alpha/red/green/blue channels are scaled to be in [0, 1].
126      * This ensures that after premultiplication all channels will
127      * be in the [0, 1] interval.
128      */
129     la = (left_c->alpha * (1.0f/257.0f));
130     lr = (left_c->red * (1.0f/257.0f));
131     lg = (left_c->green * (1.0f/257.0f));
132     lb = (left_c->blue * (1.0f/257.0f));
133 
134     ra = (right_c->alpha * (1.0f/257.0f));
135     rr = (right_c->red * (1.0f/257.0f));
136     rg = (right_c->green * (1.0f/257.0f));
137     rb = (right_c->blue * (1.0f/257.0f));
138 
139     lx = left_x * (1.0f/65536.0f);
140     rx = right_x * (1.0f/65536.0f);
141 
142     if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX)
143     {
144 	walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f;
145 	walker->a_b = (la + ra) / 510.0f;
146 	walker->r_b = (lr + rr) / 510.0f;
147 	walker->g_b = (lg + rg) / 510.0f;
148 	walker->b_b = (lb + rb) / 510.0f;
149     }
150     else
151     {
152 	float w_rec = 1.0f / (rx - lx);
153 
154 	walker->a_b = (la * rx - ra * lx) * w_rec * (1.0f/255.0f);
155 	walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f);
156 	walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f);
157 	walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f);
158 
159 	walker->a_s = (ra - la) * w_rec * (1.0f/255.0f);
160 	walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f);
161 	walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f);
162 	walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f);
163     }
164 
165     walker->left_x = left_x;
166     walker->right_x = right_x;
167 
168     walker->need_reset = FALSE;
169 }
170 
171 static argb_t
pixman_gradient_walker_pixel_float(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x)172 pixman_gradient_walker_pixel_float (pixman_gradient_walker_t *walker,
173 				    pixman_fixed_48_16_t      x)
174 {
175     argb_t f;
176     float y;
177 
178     if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
179 	gradient_walker_reset (walker, x);
180 
181     y = x * (1.0f / 65536.0f);
182 
183     f.a = walker->a_s * y + walker->a_b;
184     f.r = f.a * (walker->r_s * y + walker->r_b);
185     f.g = f.a * (walker->g_s * y + walker->g_b);
186     f.b = f.a * (walker->b_s * y + walker->b_b);
187 
188     return f;
189 }
190 
191 static uint32_t
pixman_gradient_walker_pixel_32(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x)192 pixman_gradient_walker_pixel_32 (pixman_gradient_walker_t *walker,
193 				 pixman_fixed_48_16_t      x)
194 {
195     argb_t f;
196     float y;
197 
198     if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
199 	gradient_walker_reset (walker, x);
200 
201     y = x * (1.0f / 65536.0f);
202 
203     /* Instead of [0...1] for ARGB, we want [0...255],
204      * multiply alpha with 255 and the color channels
205      * also get multiplied by the alpha multiplier.
206      *
207      * We don't use pixman_contract_from_float because it causes a 2x
208      * slowdown to do so, and the values are already normalized,
209      * so we don't have to worry about values < 0.f or > 1.f
210      */
211     f.a = 255.f * (walker->a_s * y + walker->a_b);
212     f.r = f.a * (walker->r_s * y + walker->r_b);
213     f.g = f.a * (walker->g_s * y + walker->g_b);
214     f.b = f.a * (walker->b_s * y + walker->b_b);
215 
216     return (((uint32_t)(f.a + .5f) << 24) & 0xff000000) |
217            (((uint32_t)(f.r + .5f) << 16) & 0x00ff0000) |
218            (((uint32_t)(f.g + .5f) <<  8) & 0x0000ff00) |
219            (((uint32_t)(f.b + .5f) >>  0) & 0x000000ff);
220 }
221 
222 void
_pixman_gradient_walker_write_narrow(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x,uint32_t * buffer)223 _pixman_gradient_walker_write_narrow (pixman_gradient_walker_t *walker,
224 				      pixman_fixed_48_16_t      x,
225 				      uint32_t                 *buffer)
226 {
227     *buffer = pixman_gradient_walker_pixel_32 (walker, x);
228 }
229 
230 void
_pixman_gradient_walker_write_wide(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x,uint32_t * buffer)231 _pixman_gradient_walker_write_wide (pixman_gradient_walker_t *walker,
232 				    pixman_fixed_48_16_t      x,
233 				    uint32_t                 *buffer)
234 {
235     *(argb_t *)buffer = pixman_gradient_walker_pixel_float (walker, x);
236 }
237 
238 void
_pixman_gradient_walker_fill_narrow(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x,uint32_t * buffer,uint32_t * end)239 _pixman_gradient_walker_fill_narrow (pixman_gradient_walker_t *walker,
240 				     pixman_fixed_48_16_t      x,
241 				     uint32_t                 *buffer,
242 				     uint32_t                 *end)
243 {
244     register uint32_t color;
245 
246     color = pixman_gradient_walker_pixel_32 (walker, x);
247     while (buffer < end)
248 	*buffer++ = color;
249 }
250 
251 void
_pixman_gradient_walker_fill_wide(pixman_gradient_walker_t * walker,pixman_fixed_48_16_t x,uint32_t * buffer,uint32_t * end)252 _pixman_gradient_walker_fill_wide (pixman_gradient_walker_t *walker,
253 				   pixman_fixed_48_16_t      x,
254 				   uint32_t                 *buffer,
255 				   uint32_t                 *end)
256 {
257     register argb_t color;
258     argb_t *buffer_wide = (argb_t *)buffer;
259     argb_t *end_wide    = (argb_t *)end;
260 
261     color = pixman_gradient_walker_pixel_float (walker, x);
262     while (buffer_wide < end_wide)
263 	*buffer_wide++ = color;
264 }
265