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
31void
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
53static void
54gradient_walker_reset (pixman_gradient_walker_t *walker,
55		       pixman_fixed_48_16_t      pos)
56{
57    int32_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 channel is scaled to be in the [0, 255] interval,
126     * and the red/green/blue channels are scaled to be in [0, 1].
127     * This ensures that after premultiplication all channels will
128     * be in the [0, 255] interval.
129     */
130    la = (left_c->alpha * (1.0f/257.0f));
131    lr = (left_c->red * (1.0f/257.0f));
132    lg = (left_c->green * (1.0f/257.0f));
133    lb = (left_c->blue * (1.0f/257.0f));
134
135    ra = (right_c->alpha * (1.0f/257.0f));
136    rr = (right_c->red * (1.0f/257.0f));
137    rg = (right_c->green * (1.0f/257.0f));
138    rb = (right_c->blue * (1.0f/257.0f));
139
140    lx = left_x * (1.0f/65536.0f);
141    rx = right_x * (1.0f/65536.0f);
142
143    if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX)
144    {
145	walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f;
146	walker->a_b = (la + ra) / 2.0f;
147	walker->r_b = (lr + rr) / 510.0f;
148	walker->g_b = (lg + rg) / 510.0f;
149	walker->b_b = (lb + rb) / 510.0f;
150    }
151    else
152    {
153	float w_rec = 1.0f / (rx - lx);
154
155	walker->a_b = (la * rx - ra * lx) * w_rec;
156	walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f);
157	walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f);
158	walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f);
159
160	walker->a_s = (ra - la) * w_rec;
161	walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f);
162	walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f);
163	walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f);
164    }
165
166    walker->left_x = left_x;
167    walker->right_x = right_x;
168
169    walker->need_reset = FALSE;
170}
171
172uint32_t
173_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
174                               pixman_fixed_48_16_t      x)
175{
176    float a, r, g, b;
177    uint8_t a8, r8, g8, b8;
178    uint32_t v;
179    float y;
180
181    if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
182        gradient_walker_reset (walker, x);
183
184    y = x * (1.0f / 65536.0f);
185
186    a = walker->a_s * y + walker->a_b;
187    r = a * (walker->r_s * y + walker->r_b);
188    g = a * (walker->g_s * y + walker->g_b);
189    b = a * (walker->b_s * y + walker->b_b);
190
191    a8 = a + 0.5f;
192    r8 = r + 0.5f;
193    g8 = g + 0.5f;
194    b8 = b + 0.5f;
195
196    v = ((a8 << 24) & 0xff000000) |
197        ((r8 << 16) & 0x00ff0000) |
198        ((g8 <<  8) & 0x0000ff00) |
199        ((b8 >>  0) & 0x000000ff);
200
201    return v;
202}
203