Line data Source code
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 : #include "pixman-dither.h"
35 :
36 : static pixman_bool_t
37 0 : linear_gradient_is_horizontal (pixman_image_t *image,
38 : int x,
39 : int y,
40 : int width,
41 : int height)
42 : {
43 0 : linear_gradient_t *linear = (linear_gradient_t *)image;
44 : pixman_vector_t v;
45 : pixman_fixed_32_32_t l;
46 : pixman_fixed_48_16_t dx, dy;
47 : double inc;
48 :
49 0 : if (image->common.transform)
50 : {
51 : /* projective transformation */
52 0 : if (image->common.transform->matrix[2][0] != 0 ||
53 0 : image->common.transform->matrix[2][1] != 0 ||
54 0 : image->common.transform->matrix[2][2] == 0)
55 : {
56 0 : return FALSE;
57 : }
58 :
59 0 : v.vector[0] = image->common.transform->matrix[0][1];
60 0 : v.vector[1] = image->common.transform->matrix[1][1];
61 0 : v.vector[2] = image->common.transform->matrix[2][2];
62 : }
63 : else
64 : {
65 0 : v.vector[0] = 0;
66 0 : v.vector[1] = pixman_fixed_1;
67 0 : v.vector[2] = pixman_fixed_1;
68 : }
69 :
70 0 : dx = linear->p2.x - linear->p1.x;
71 0 : dy = linear->p2.y - linear->p1.y;
72 :
73 0 : l = dx * dx + dy * dy;
74 :
75 0 : if (l == 0)
76 0 : return FALSE;
77 :
78 : /*
79 : * compute how much the input of the gradient walked changes
80 : * when moving vertically through the whole image
81 : */
82 0 : inc = height * (double) pixman_fixed_1 * pixman_fixed_1 *
83 0 : (dx * v.vector[0] + dy * v.vector[1]) /
84 0 : (v.vector[2] * (double) l);
85 :
86 : /* check that casting to integer would result in 0 */
87 0 : if (-1 < inc && inc < 1)
88 0 : return TRUE;
89 :
90 0 : return FALSE;
91 : }
92 :
93 : static uint32_t *
94 0 : linear_get_scanline_narrow (pixman_iter_t *iter,
95 : const uint32_t *mask)
96 : {
97 0 : pixman_image_t *image = iter->image;
98 0 : int x = iter->x;
99 0 : int y = iter->y;
100 0 : int width = iter->width;
101 0 : uint32_t * buffer = iter->buffer;
102 :
103 : pixman_vector_t v, unit;
104 : pixman_fixed_32_32_t l;
105 : pixman_fixed_48_16_t dx, dy;
106 0 : gradient_t *gradient = (gradient_t *)image;
107 0 : linear_gradient_t *linear = (linear_gradient_t *)image;
108 0 : uint32_t *end = buffer + width;
109 : pixman_gradient_walker_t walker;
110 :
111 0 : _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
112 :
113 : /* reference point is the center of the pixel */
114 0 : v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
115 0 : v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
116 0 : v.vector[2] = pixman_fixed_1;
117 :
118 0 : if (image->common.transform)
119 : {
120 0 : if (!pixman_transform_point_3d (image->common.transform, &v))
121 0 : return iter->buffer;
122 :
123 0 : unit.vector[0] = image->common.transform->matrix[0][0];
124 0 : unit.vector[1] = image->common.transform->matrix[1][0];
125 0 : unit.vector[2] = image->common.transform->matrix[2][0];
126 : }
127 : else
128 : {
129 0 : unit.vector[0] = pixman_fixed_1;
130 0 : unit.vector[1] = 0;
131 0 : unit.vector[2] = 0;
132 : }
133 :
134 0 : dx = linear->p2.x - linear->p1.x;
135 0 : dy = linear->p2.y - linear->p1.y;
136 :
137 0 : l = dx * dx + dy * dy;
138 :
139 0 : if (l == 0 || unit.vector[2] == 0)
140 0 : {
141 : /* affine transformation only */
142 : pixman_fixed_32_32_t t, next_inc;
143 : double inc;
144 :
145 0 : if (l == 0 || v.vector[2] == 0)
146 : {
147 0 : t = 0;
148 0 : inc = 0;
149 : }
150 : else
151 : {
152 : double invden, v2;
153 :
154 0 : invden = pixman_fixed_1 * (double) pixman_fixed_1 /
155 0 : (l * (double) v.vector[2]);
156 0 : v2 = v.vector[2] * (1. / pixman_fixed_1);
157 0 : t = ((dx * v.vector[0] + dy * v.vector[1]) -
158 0 : (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
159 0 : inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
160 : }
161 0 : next_inc = 0;
162 :
163 0 : if (((pixman_fixed_32_32_t )(inc * width)) == 0)
164 : {
165 : register uint32_t color;
166 :
167 0 : color = _pixman_gradient_walker_pixel (&walker, t);
168 0 : while (buffer < end)
169 0 : *buffer++ = color;
170 : }
171 : else
172 : {
173 : int i;
174 :
175 0 : i = 0;
176 0 : while (buffer < end)
177 : {
178 0 : if (!mask || *mask++)
179 : {
180 0 : *buffer = _pixman_gradient_walker_pixel (&walker,
181 : t + next_inc);
182 : }
183 0 : i++;
184 0 : next_inc = inc * i;
185 0 : buffer++;
186 : }
187 : }
188 : }
189 : else
190 : {
191 : /* projective transformation */
192 : double t;
193 :
194 0 : t = 0;
195 :
196 0 : while (buffer < end)
197 : {
198 0 : if (!mask || *mask++)
199 : {
200 0 : if (v.vector[2] != 0)
201 : {
202 : double invden, v2;
203 :
204 0 : invden = pixman_fixed_1 * (double) pixman_fixed_1 /
205 0 : (l * (double) v.vector[2]);
206 0 : v2 = v.vector[2] * (1. / pixman_fixed_1);
207 0 : t = ((dx * v.vector[0] + dy * v.vector[1]) -
208 0 : (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
209 : }
210 :
211 0 : *buffer = _pixman_gradient_walker_pixel (&walker, t);
212 : }
213 :
214 0 : ++buffer;
215 :
216 0 : v.vector[0] += unit.vector[0];
217 0 : v.vector[1] += unit.vector[1];
218 0 : v.vector[2] += unit.vector[2];
219 : }
220 : }
221 :
222 0 : iter->y++;
223 :
224 0 : return iter->buffer;
225 : }
226 :
227 : static uint32_t *
228 0 : linear_get_scanline_16 (pixman_iter_t *iter,
229 : const uint32_t *mask)
230 : {
231 0 : pixman_image_t *image = iter->image;
232 0 : int x = iter->x;
233 0 : int y = iter->y;
234 0 : int width = iter->width;
235 0 : uint16_t * buffer = (uint16_t*)iter->buffer;
236 0 : pixman_bool_t toggle = ((x ^ y) & 1);
237 :
238 : pixman_vector_t v, unit;
239 : pixman_fixed_32_32_t l;
240 : pixman_fixed_48_16_t dx, dy;
241 0 : gradient_t *gradient = (gradient_t *)image;
242 0 : linear_gradient_t *linear = (linear_gradient_t *)image;
243 0 : uint16_t *end = buffer + width;
244 : pixman_gradient_walker_t walker;
245 :
246 0 : _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
247 :
248 : /* reference point is the center of the pixel */
249 0 : v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
250 0 : v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
251 0 : v.vector[2] = pixman_fixed_1;
252 :
253 0 : if (image->common.transform)
254 : {
255 0 : if (!pixman_transform_point_3d (image->common.transform, &v))
256 0 : return iter->buffer;
257 :
258 0 : unit.vector[0] = image->common.transform->matrix[0][0];
259 0 : unit.vector[1] = image->common.transform->matrix[1][0];
260 0 : unit.vector[2] = image->common.transform->matrix[2][0];
261 : }
262 : else
263 : {
264 0 : unit.vector[0] = pixman_fixed_1;
265 0 : unit.vector[1] = 0;
266 0 : unit.vector[2] = 0;
267 : }
268 :
269 0 : dx = linear->p2.x - linear->p1.x;
270 0 : dy = linear->p2.y - linear->p1.y;
271 :
272 0 : l = dx * dx + dy * dy;
273 :
274 0 : if (l == 0 || unit.vector[2] == 0)
275 0 : {
276 : /* affine transformation only */
277 : pixman_fixed_32_32_t t, next_inc;
278 : double inc;
279 :
280 0 : if (l == 0 || v.vector[2] == 0)
281 : {
282 0 : t = 0;
283 0 : inc = 0;
284 : }
285 : else
286 : {
287 : double invden, v2;
288 :
289 0 : invden = pixman_fixed_1 * (double) pixman_fixed_1 /
290 0 : (l * (double) v.vector[2]);
291 0 : v2 = v.vector[2] * (1. / pixman_fixed_1);
292 0 : t = ((dx * v.vector[0] + dy * v.vector[1]) -
293 0 : (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
294 0 : inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
295 : }
296 0 : next_inc = 0;
297 :
298 0 : if (((pixman_fixed_32_32_t )(inc * width)) == 0)
299 : {
300 : register uint32_t color;
301 : uint16_t dither_diff;
302 : uint16_t color16;
303 : uint16_t color16b;
304 :
305 0 : color = _pixman_gradient_walker_pixel (&walker, t);
306 0 : color16 = dither_8888_to_0565(color, toggle);
307 0 : color16b = dither_8888_to_0565(color, toggle^1);
308 : // compute the difference
309 0 : dither_diff = color16 ^ color16b;
310 0 : while (buffer < end) {
311 0 : *buffer++ = color16;
312 : // use dither_diff to toggle between color16 and color16b
313 0 : color16 ^= dither_diff;
314 0 : toggle ^= 1;
315 : }
316 : }
317 : else
318 : {
319 : int i;
320 :
321 0 : i = 0;
322 0 : while (buffer < end)
323 : {
324 0 : if (!mask || *mask++)
325 : {
326 0 : *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
327 : t + next_inc),
328 : toggle);
329 : }
330 0 : toggle ^= 1;
331 0 : i++;
332 0 : next_inc = inc * i;
333 0 : buffer++;
334 : }
335 : }
336 : }
337 : else
338 : {
339 : /* projective transformation */
340 : double t;
341 :
342 0 : t = 0;
343 :
344 0 : while (buffer < end)
345 : {
346 0 : if (!mask || *mask++)
347 : {
348 0 : if (v.vector[2] != 0)
349 : {
350 : double invden, v2;
351 :
352 0 : invden = pixman_fixed_1 * (double) pixman_fixed_1 /
353 0 : (l * (double) v.vector[2]);
354 0 : v2 = v.vector[2] * (1. / pixman_fixed_1);
355 0 : t = ((dx * v.vector[0] + dy * v.vector[1]) -
356 0 : (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
357 : }
358 :
359 0 : *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t),
360 : toggle);
361 : }
362 0 : toggle ^= 1;
363 :
364 0 : ++buffer;
365 :
366 0 : v.vector[0] += unit.vector[0];
367 0 : v.vector[1] += unit.vector[1];
368 0 : v.vector[2] += unit.vector[2];
369 : }
370 : }
371 :
372 0 : iter->y++;
373 :
374 0 : return iter->buffer;
375 : }
376 :
377 : static uint32_t *
378 0 : linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
379 : {
380 0 : uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
381 :
382 0 : pixman_expand_to_float (
383 : (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
384 :
385 0 : return buffer;
386 : }
387 :
388 : void
389 0 : _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
390 : {
391 : // XXX: we can't use this optimization when dithering
392 : if (0 && linear_gradient_is_horizontal (
393 : iter->image, iter->x, iter->y, iter->width, iter->height))
394 : {
395 : if (iter->iter_flags & ITER_16)
396 : linear_get_scanline_16 (iter, NULL);
397 : else if (iter->iter_flags & ITER_NARROW)
398 : linear_get_scanline_narrow (iter, NULL);
399 : else
400 : linear_get_scanline_wide (iter, NULL);
401 :
402 : iter->get_scanline = _pixman_iter_get_scanline_noop;
403 : }
404 : else
405 : {
406 0 : if (iter->iter_flags & ITER_16)
407 0 : iter->get_scanline = linear_get_scanline_16;
408 0 : else if (iter->iter_flags & ITER_NARROW)
409 0 : iter->get_scanline = linear_get_scanline_narrow;
410 : else
411 0 : iter->get_scanline = linear_get_scanline_wide;
412 : }
413 0 : }
414 :
415 : PIXMAN_EXPORT pixman_image_t *
416 0 : pixman_image_create_linear_gradient (const pixman_point_fixed_t * p1,
417 : const pixman_point_fixed_t * p2,
418 : const pixman_gradient_stop_t *stops,
419 : int n_stops)
420 : {
421 : pixman_image_t *image;
422 : linear_gradient_t *linear;
423 :
424 0 : image = _pixman_image_allocate ();
425 :
426 0 : if (!image)
427 0 : return NULL;
428 :
429 0 : linear = &image->linear;
430 :
431 0 : if (!_pixman_init_gradient (&linear->common, stops, n_stops))
432 : {
433 0 : free (image);
434 0 : return NULL;
435 : }
436 :
437 0 : linear->p1 = *p1;
438 0 : linear->p2 = *p2;
439 :
440 0 : image->type = LINEAR;
441 :
442 0 : return image;
443 : }
444 :
|