|
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 linear_gradient_is_horizontal (pixman_image_t *image, |
|
38 int x, |
|
39 int y, |
|
40 int width, |
|
41 int height) |
|
42 { |
|
43 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 if (image->common.transform) |
|
50 { |
|
51 /* projective transformation */ |
|
52 if (image->common.transform->matrix[2][0] != 0 || |
|
53 image->common.transform->matrix[2][1] != 0 || |
|
54 image->common.transform->matrix[2][2] == 0) |
|
55 { |
|
56 return FALSE; |
|
57 } |
|
58 |
|
59 v.vector[0] = image->common.transform->matrix[0][1]; |
|
60 v.vector[1] = image->common.transform->matrix[1][1]; |
|
61 v.vector[2] = image->common.transform->matrix[2][2]; |
|
62 } |
|
63 else |
|
64 { |
|
65 v.vector[0] = 0; |
|
66 v.vector[1] = pixman_fixed_1; |
|
67 v.vector[2] = pixman_fixed_1; |
|
68 } |
|
69 |
|
70 dx = linear->p2.x - linear->p1.x; |
|
71 dy = linear->p2.y - linear->p1.y; |
|
72 |
|
73 l = dx * dx + dy * dy; |
|
74 |
|
75 if (l == 0) |
|
76 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 inc = height * (double) pixman_fixed_1 * pixman_fixed_1 * |
|
83 (dx * v.vector[0] + dy * v.vector[1]) / |
|
84 (v.vector[2] * (double) l); |
|
85 |
|
86 /* check that casting to integer would result in 0 */ |
|
87 if (-1 < inc && inc < 1) |
|
88 return TRUE; |
|
89 |
|
90 return FALSE; |
|
91 } |
|
92 |
|
93 static uint32_t * |
|
94 linear_get_scanline_narrow (pixman_iter_t *iter, |
|
95 const uint32_t *mask) |
|
96 { |
|
97 pixman_image_t *image = iter->image; |
|
98 int x = iter->x; |
|
99 int y = iter->y; |
|
100 int width = iter->width; |
|
101 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 gradient_t *gradient = (gradient_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, image->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 (image->common.transform) |
|
119 { |
|
120 if (!pixman_transform_point_3d (image->common.transform, &v)) |
|
121 return iter->buffer; |
|
122 |
|
123 unit.vector[0] = image->common.transform->matrix[0][0]; |
|
124 unit.vector[1] = image->common.transform->matrix[1][0]; |
|
125 unit.vector[2] = image->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 || unit.vector[2] == 0) |
|
140 { |
|
141 /* affine transformation only */ |
|
142 pixman_fixed_32_32_t t, next_inc; |
|
143 double inc; |
|
144 |
|
145 if (l == 0 || v.vector[2] == 0) |
|
146 { |
|
147 t = 0; |
|
148 inc = 0; |
|
149 } |
|
150 else |
|
151 { |
|
152 double invden, v2; |
|
153 |
|
154 invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
|
155 (l * (double) v.vector[2]); |
|
156 v2 = v.vector[2] * (1. / pixman_fixed_1); |
|
157 t = ((dx * v.vector[0] + dy * v.vector[1]) - |
|
158 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
|
159 inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; |
|
160 } |
|
161 next_inc = 0; |
|
162 |
|
163 if (((pixman_fixed_32_32_t )(inc * width)) == 0) |
|
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 int i; |
|
174 |
|
175 i = 0; |
|
176 while (buffer < end) |
|
177 { |
|
178 if (!mask || *mask++) |
|
179 { |
|
180 *buffer = _pixman_gradient_walker_pixel (&walker, |
|
181 t + next_inc); |
|
182 } |
|
183 i++; |
|
184 next_inc = inc * i; |
|
185 buffer++; |
|
186 } |
|
187 } |
|
188 } |
|
189 else |
|
190 { |
|
191 /* projective transformation */ |
|
192 double t; |
|
193 |
|
194 t = 0; |
|
195 |
|
196 while (buffer < end) |
|
197 { |
|
198 if (!mask || *mask++) |
|
199 { |
|
200 if (v.vector[2] != 0) |
|
201 { |
|
202 double invden, v2; |
|
203 |
|
204 invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
|
205 (l * (double) v.vector[2]); |
|
206 v2 = v.vector[2] * (1. / pixman_fixed_1); |
|
207 t = ((dx * v.vector[0] + dy * v.vector[1]) - |
|
208 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
|
209 } |
|
210 |
|
211 *buffer = _pixman_gradient_walker_pixel (&walker, t); |
|
212 } |
|
213 |
|
214 ++buffer; |
|
215 |
|
216 v.vector[0] += unit.vector[0]; |
|
217 v.vector[1] += unit.vector[1]; |
|
218 v.vector[2] += unit.vector[2]; |
|
219 } |
|
220 } |
|
221 |
|
222 iter->y++; |
|
223 |
|
224 return iter->buffer; |
|
225 } |
|
226 |
|
227 static uint32_t * |
|
228 linear_get_scanline_16 (pixman_iter_t *iter, |
|
229 const uint32_t *mask) |
|
230 { |
|
231 pixman_image_t *image = iter->image; |
|
232 int x = iter->x; |
|
233 int y = iter->y; |
|
234 int width = iter->width; |
|
235 uint16_t * buffer = (uint16_t*)iter->buffer; |
|
236 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 gradient_t *gradient = (gradient_t *)image; |
|
242 linear_gradient_t *linear = (linear_gradient_t *)image; |
|
243 uint16_t *end = buffer + width; |
|
244 pixman_gradient_walker_t walker; |
|
245 |
|
246 _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); |
|
247 |
|
248 /* reference point is the center of the pixel */ |
|
249 v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; |
|
250 v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; |
|
251 v.vector[2] = pixman_fixed_1; |
|
252 |
|
253 if (image->common.transform) |
|
254 { |
|
255 if (!pixman_transform_point_3d (image->common.transform, &v)) |
|
256 return iter->buffer; |
|
257 |
|
258 unit.vector[0] = image->common.transform->matrix[0][0]; |
|
259 unit.vector[1] = image->common.transform->matrix[1][0]; |
|
260 unit.vector[2] = image->common.transform->matrix[2][0]; |
|
261 } |
|
262 else |
|
263 { |
|
264 unit.vector[0] = pixman_fixed_1; |
|
265 unit.vector[1] = 0; |
|
266 unit.vector[2] = 0; |
|
267 } |
|
268 |
|
269 dx = linear->p2.x - linear->p1.x; |
|
270 dy = linear->p2.y - linear->p1.y; |
|
271 |
|
272 l = dx * dx + dy * dy; |
|
273 |
|
274 if (l == 0 || unit.vector[2] == 0) |
|
275 { |
|
276 /* affine transformation only */ |
|
277 pixman_fixed_32_32_t t, next_inc; |
|
278 double inc; |
|
279 |
|
280 if (l == 0 || v.vector[2] == 0) |
|
281 { |
|
282 t = 0; |
|
283 inc = 0; |
|
284 } |
|
285 else |
|
286 { |
|
287 double invden, v2; |
|
288 |
|
289 invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
|
290 (l * (double) v.vector[2]); |
|
291 v2 = v.vector[2] * (1. / pixman_fixed_1); |
|
292 t = ((dx * v.vector[0] + dy * v.vector[1]) - |
|
293 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
|
294 inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; |
|
295 } |
|
296 next_inc = 0; |
|
297 |
|
298 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 color = _pixman_gradient_walker_pixel (&walker, t); |
|
306 color16 = dither_8888_to_0565(color, toggle); |
|
307 color16b = dither_8888_to_0565(color, toggle^1); |
|
308 // compute the difference |
|
309 dither_diff = color16 ^ color16b; |
|
310 while (buffer < end) { |
|
311 *buffer++ = color16; |
|
312 // use dither_diff to toggle between color16 and color16b |
|
313 color16 ^= dither_diff; |
|
314 toggle ^= 1; |
|
315 } |
|
316 } |
|
317 else |
|
318 { |
|
319 int i; |
|
320 |
|
321 i = 0; |
|
322 while (buffer < end) |
|
323 { |
|
324 if (!mask || *mask++) |
|
325 { |
|
326 *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, |
|
327 t + next_inc), |
|
328 toggle); |
|
329 } |
|
330 toggle ^= 1; |
|
331 i++; |
|
332 next_inc = inc * i; |
|
333 buffer++; |
|
334 } |
|
335 } |
|
336 } |
|
337 else |
|
338 { |
|
339 /* projective transformation */ |
|
340 double t; |
|
341 |
|
342 t = 0; |
|
343 |
|
344 while (buffer < end) |
|
345 { |
|
346 if (!mask || *mask++) |
|
347 { |
|
348 if (v.vector[2] != 0) |
|
349 { |
|
350 double invden, v2; |
|
351 |
|
352 invden = pixman_fixed_1 * (double) pixman_fixed_1 / |
|
353 (l * (double) v.vector[2]); |
|
354 v2 = v.vector[2] * (1. / pixman_fixed_1); |
|
355 t = ((dx * v.vector[0] + dy * v.vector[1]) - |
|
356 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; |
|
357 } |
|
358 |
|
359 *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t), |
|
360 toggle); |
|
361 } |
|
362 toggle ^= 1; |
|
363 |
|
364 ++buffer; |
|
365 |
|
366 v.vector[0] += unit.vector[0]; |
|
367 v.vector[1] += unit.vector[1]; |
|
368 v.vector[2] += unit.vector[2]; |
|
369 } |
|
370 } |
|
371 |
|
372 iter->y++; |
|
373 |
|
374 return iter->buffer; |
|
375 } |
|
376 |
|
377 static uint32_t * |
|
378 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) |
|
379 { |
|
380 uint32_t *buffer = linear_get_scanline_narrow (iter, NULL); |
|
381 |
|
382 pixman_expand_to_float ( |
|
383 (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); |
|
384 |
|
385 return buffer; |
|
386 } |
|
387 |
|
388 void |
|
389 _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 if (iter->iter_flags & ITER_16) |
|
407 iter->get_scanline = linear_get_scanline_16; |
|
408 else if (iter->iter_flags & ITER_NARROW) |
|
409 iter->get_scanline = linear_get_scanline_narrow; |
|
410 else |
|
411 iter->get_scanline = linear_get_scanline_wide; |
|
412 } |
|
413 } |
|
414 |
|
415 PIXMAN_EXPORT pixman_image_t * |
|
416 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 image = _pixman_image_allocate (); |
|
425 |
|
426 if (!image) |
|
427 return NULL; |
|
428 |
|
429 linear = &image->linear; |
|
430 |
|
431 if (!_pixman_init_gradient (&linear->common, stops, n_stops)) |
|
432 { |
|
433 free (image); |
|
434 return NULL; |
|
435 } |
|
436 |
|
437 linear->p1 = *p1; |
|
438 linear->p2 = *p2; |
|
439 |
|
440 image->type = LINEAR; |
|
441 |
|
442 return image; |
|
443 } |
|
444 |