/src/cairo/src/cairo-image-source.c
Line | Count | Source |
1 | | /* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ |
2 | | /* cairo - a vector graphics library with display and print output |
3 | | * |
4 | | * Copyright © 2003 University of Southern California |
5 | | * Copyright © 2009,2010,2011 Intel Corporation |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it either under the terms of the GNU Lesser General Public |
9 | | * License version 2.1 as published by the Free Software Foundation |
10 | | * (the "LGPL") or, at your option, under the terms of the Mozilla |
11 | | * Public License Version 1.1 (the "MPL"). If you do not alter this |
12 | | * notice, a recipient may use your version of this file under either |
13 | | * the MPL or the LGPL. |
14 | | * |
15 | | * You should have received a copy of the LGPL along with this library |
16 | | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
17 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
18 | | * You should have received a copy of the MPL along with this library |
19 | | * in the file COPYING-MPL-1.1 |
20 | | * |
21 | | * The contents of this file are subject to the Mozilla Public License |
22 | | * Version 1.1 (the "License"); you may not use this file except in |
23 | | * compliance with the License. You may obtain a copy of the License at |
24 | | * http://www.mozilla.org/MPL/ |
25 | | * |
26 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
27 | | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
28 | | * the specific language governing rights and limitations. |
29 | | * |
30 | | * The Original Code is the cairo graphics library. |
31 | | * |
32 | | * The Initial Developer of the Original Code is University of Southern |
33 | | * California. |
34 | | * |
35 | | * Contributor(s): |
36 | | * Carl D. Worth <cworth@cworth.org> |
37 | | * Chris Wilson <chris@chris-wilson.co.uk> |
38 | | */ |
39 | | |
40 | | /* The purpose of this file/surface is to simply translate a pattern |
41 | | * to a pixman_image_t and thence to feed it back to the general |
42 | | * compositor interface. |
43 | | */ |
44 | | |
45 | | #include "cairoint.h" |
46 | | |
47 | | #include "cairo-image-surface-private.h" |
48 | | |
49 | | #include "cairo-compositor-private.h" |
50 | | #include "cairo-error-private.h" |
51 | | #include "cairo-pattern-inline.h" |
52 | | #include "cairo-paginated-private.h" |
53 | | #include "cairo-recording-surface-private.h" |
54 | | #include "cairo-surface-observer-private.h" |
55 | | #include "cairo-surface-snapshot-inline.h" |
56 | | #include "cairo-surface-subsurface-private.h" |
57 | | |
58 | 12 | #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ |
59 | | |
60 | | #if CAIRO_NO_MUTEX |
61 | | #define PIXMAN_HAS_ATOMIC_OPS 1 |
62 | | #endif |
63 | | |
64 | | #if PIXMAN_HAS_ATOMIC_OPS |
65 | | static pixman_image_t *__pixman_transparent_image; |
66 | | static pixman_image_t *__pixman_black_image; |
67 | | static pixman_image_t *__pixman_white_image; |
68 | | |
69 | | static pixman_image_t * |
70 | | _pixman_transparent_image (void) |
71 | | { |
72 | | pixman_image_t *image; |
73 | | |
74 | | TRACE ((stderr, "%s\n", __FUNCTION__)); |
75 | | |
76 | | image = __pixman_transparent_image; |
77 | | if (unlikely (image == NULL)) { |
78 | | pixman_color_t color; |
79 | | |
80 | | color.red = 0x00; |
81 | | color.green = 0x00; |
82 | | color.blue = 0x00; |
83 | | color.alpha = 0x00; |
84 | | |
85 | | image = pixman_image_create_solid_fill (&color); |
86 | | if (unlikely (image == NULL)) |
87 | | return NULL; |
88 | | |
89 | | if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image, |
90 | | NULL, image)) |
91 | | { |
92 | | pixman_image_ref (image); |
93 | | } |
94 | | } else { |
95 | | pixman_image_ref (image); |
96 | | } |
97 | | |
98 | | return image; |
99 | | } |
100 | | |
101 | | static pixman_image_t * |
102 | | _pixman_black_image (void) |
103 | | { |
104 | | pixman_image_t *image; |
105 | | |
106 | | TRACE ((stderr, "%s\n", __FUNCTION__)); |
107 | | |
108 | | image = __pixman_black_image; |
109 | | if (unlikely (image == NULL)) { |
110 | | pixman_color_t color; |
111 | | |
112 | | color.red = 0x00; |
113 | | color.green = 0x00; |
114 | | color.blue = 0x00; |
115 | | color.alpha = 0xffff; |
116 | | |
117 | | image = pixman_image_create_solid_fill (&color); |
118 | | if (unlikely (image == NULL)) |
119 | | return NULL; |
120 | | |
121 | | if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, |
122 | | NULL, image)) |
123 | | { |
124 | | pixman_image_ref (image); |
125 | | } |
126 | | } else { |
127 | | pixman_image_ref (image); |
128 | | } |
129 | | |
130 | | return image; |
131 | | } |
132 | | |
133 | | static pixman_image_t * |
134 | | _pixman_white_image (void) |
135 | | { |
136 | | pixman_image_t *image; |
137 | | |
138 | | TRACE ((stderr, "%s\n", __FUNCTION__)); |
139 | | |
140 | | image = __pixman_white_image; |
141 | | if (unlikely (image == NULL)) { |
142 | | pixman_color_t color; |
143 | | |
144 | | color.red = 0xffff; |
145 | | color.green = 0xffff; |
146 | | color.blue = 0xffff; |
147 | | color.alpha = 0xffff; |
148 | | |
149 | | image = pixman_image_create_solid_fill (&color); |
150 | | if (unlikely (image == NULL)) |
151 | | return NULL; |
152 | | |
153 | | if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image, |
154 | | NULL, image)) |
155 | | { |
156 | | pixman_image_ref (image); |
157 | | } |
158 | | } else { |
159 | | pixman_image_ref (image); |
160 | | } |
161 | | |
162 | | return image; |
163 | | } |
164 | | |
165 | | static uint32_t |
166 | | hars_petruska_f54_1_random (void) |
167 | | { |
168 | | #define rol(x,k) ((x << k) | (x >> (32-k))) |
169 | | static uint32_t x; |
170 | | return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; |
171 | | #undef rol |
172 | | } |
173 | | |
174 | | static struct { |
175 | | cairo_color_t color; |
176 | | pixman_image_t *image; |
177 | | } cache[16]; |
178 | | static int n_cached; |
179 | | |
180 | | #else /* !PIXMAN_HAS_ATOMIC_OPS */ |
181 | | static pixman_image_t * |
182 | | _pixman_transparent_image (void) |
183 | 0 | { |
184 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
185 | 0 | return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT); |
186 | 0 | } |
187 | | |
188 | | static pixman_image_t * |
189 | | _pixman_black_image (void) |
190 | 0 | { |
191 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
192 | 0 | return _pixman_image_for_color (CAIRO_COLOR_BLACK); |
193 | 0 | } |
194 | | |
195 | | static pixman_image_t * |
196 | | _pixman_white_image (void) |
197 | 0 | { |
198 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
199 | 0 | return _pixman_image_for_color (CAIRO_COLOR_WHITE); |
200 | 0 | } |
201 | | #endif /* !PIXMAN_HAS_ATOMIC_OPS */ |
202 | | |
203 | | |
204 | | pixman_image_t * |
205 | | _pixman_image_for_color (const cairo_color_t *cairo_color) |
206 | 24 | { |
207 | 24 | pixman_color_t color; |
208 | 24 | pixman_image_t *image; |
209 | | |
210 | | #if PIXMAN_HAS_ATOMIC_OPS |
211 | | int i; |
212 | | |
213 | | if (CAIRO_COLOR_IS_CLEAR (cairo_color)) |
214 | | return _pixman_transparent_image (); |
215 | | |
216 | | if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) { |
217 | | if (cairo_color->red_short <= 0x00ff && |
218 | | cairo_color->green_short <= 0x00ff && |
219 | | cairo_color->blue_short <= 0x00ff) |
220 | | { |
221 | | return _pixman_black_image (); |
222 | | } |
223 | | |
224 | | if (cairo_color->red_short >= 0xff00 && |
225 | | cairo_color->green_short >= 0xff00 && |
226 | | cairo_color->blue_short >= 0xff00) |
227 | | { |
228 | | return _pixman_white_image (); |
229 | | } |
230 | | } |
231 | | |
232 | | CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex); |
233 | | for (i = 0; i < n_cached; i++) { |
234 | | if (_cairo_color_equal (&cache[i].color, cairo_color)) { |
235 | | image = pixman_image_ref (cache[i].image); |
236 | | goto UNLOCK; |
237 | | } |
238 | | } |
239 | | #endif |
240 | | |
241 | 24 | color.red = cairo_color->red_short; |
242 | 24 | color.green = cairo_color->green_short; |
243 | 24 | color.blue = cairo_color->blue_short; |
244 | 24 | color.alpha = cairo_color->alpha_short; |
245 | | |
246 | 24 | image = pixman_image_create_solid_fill (&color); |
247 | | #if PIXMAN_HAS_ATOMIC_OPS |
248 | | if (image == NULL) |
249 | | goto UNLOCK; |
250 | | |
251 | | if (n_cached < ARRAY_LENGTH (cache)) { |
252 | | i = n_cached++; |
253 | | } else { |
254 | | i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache); |
255 | | pixman_image_unref (cache[i].image); |
256 | | } |
257 | | cache[i].image = pixman_image_ref (image); |
258 | | cache[i].color = *cairo_color; |
259 | | |
260 | | UNLOCK: |
261 | | CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); |
262 | | #endif |
263 | 24 | return image; |
264 | 24 | } |
265 | | |
266 | | |
267 | | void |
268 | | _cairo_image_reset_static_data (void) |
269 | 0 | { |
270 | | #if PIXMAN_HAS_ATOMIC_OPS |
271 | | while (n_cached) |
272 | | pixman_image_unref (cache[--n_cached].image); |
273 | | |
274 | | if (__pixman_transparent_image) { |
275 | | pixman_image_unref (__pixman_transparent_image); |
276 | | __pixman_transparent_image = NULL; |
277 | | } |
278 | | |
279 | | if (__pixman_black_image) { |
280 | | pixman_image_unref (__pixman_black_image); |
281 | | __pixman_black_image = NULL; |
282 | | } |
283 | | |
284 | | if (__pixman_white_image) { |
285 | | pixman_image_unref (__pixman_white_image); |
286 | | __pixman_white_image = NULL; |
287 | | } |
288 | | #endif |
289 | 0 | } |
290 | | |
291 | | static pixman_image_t * |
292 | | _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, |
293 | | const cairo_rectangle_int_t *extents, |
294 | | int *ix, int *iy) |
295 | 12 | { |
296 | 12 | pixman_image_t *pixman_image; |
297 | 12 | pixman_gradient_stop_t pixman_stops_static[2]; |
298 | 12 | pixman_gradient_stop_t *pixman_stops = pixman_stops_static; |
299 | 12 | pixman_transform_t pixman_transform; |
300 | 12 | cairo_matrix_t matrix; |
301 | 12 | cairo_circle_double_t extremes[2]; |
302 | 12 | pixman_point_fixed_t p1, p2; |
303 | 12 | unsigned int i; |
304 | 12 | cairo_int_status_t status; |
305 | | |
306 | 12 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
307 | | |
308 | 12 | if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { |
309 | 12 | pixman_stops = _cairo_malloc_ab (pattern->n_stops, |
310 | 12 | sizeof(pixman_gradient_stop_t)); |
311 | 12 | if (unlikely (pixman_stops == NULL)) |
312 | 0 | return NULL; |
313 | 12 | } |
314 | | |
315 | 1.86k | for (i = 0; i < pattern->n_stops; i++) { |
316 | 1.85k | pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); |
317 | 1.85k | pixman_stops[i].color.red = pattern->stops[i].color.red_short; |
318 | 1.85k | pixman_stops[i].color.green = pattern->stops[i].color.green_short; |
319 | 1.85k | pixman_stops[i].color.blue = pattern->stops[i].color.blue_short; |
320 | 1.85k | pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short; |
321 | 1.85k | } |
322 | | |
323 | 12 | _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes); |
324 | | |
325 | 12 | p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); |
326 | 12 | p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); |
327 | 12 | p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); |
328 | 12 | p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); |
329 | | |
330 | 12 | if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
331 | 12 | pixman_image = pixman_image_create_linear_gradient (&p1, &p2, |
332 | 12 | pixman_stops, |
333 | 12 | pattern->n_stops); |
334 | 12 | } else { |
335 | 0 | pixman_fixed_t r1, r2; |
336 | |
|
337 | 0 | r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); |
338 | 0 | r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); |
339 | |
|
340 | 0 | pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2, |
341 | 0 | pixman_stops, |
342 | 0 | pattern->n_stops); |
343 | 0 | } |
344 | | |
345 | 12 | if (pixman_stops != pixman_stops_static) |
346 | 12 | free (pixman_stops); |
347 | | |
348 | 12 | if (unlikely (pixman_image == NULL)) |
349 | 0 | return NULL; |
350 | | |
351 | 12 | *ix = *iy = 0; |
352 | 12 | status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter, |
353 | 12 | extents->x + extents->width/2., |
354 | 12 | extents->y + extents->height/2., |
355 | 12 | &pixman_transform, ix, iy); |
356 | 12 | if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { |
357 | 12 | if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) || |
358 | 12 | ! pixman_image_set_transform (pixman_image, &pixman_transform)) |
359 | 0 | { |
360 | 0 | pixman_image_unref (pixman_image); |
361 | 0 | return NULL; |
362 | 0 | } |
363 | 12 | } |
364 | | |
365 | 12 | { |
366 | 12 | pixman_repeat_t pixman_repeat; |
367 | | |
368 | 12 | switch (pattern->base.extend) { |
369 | 0 | default: |
370 | 0 | case CAIRO_EXTEND_NONE: |
371 | 0 | pixman_repeat = PIXMAN_REPEAT_NONE; |
372 | 0 | break; |
373 | 0 | case CAIRO_EXTEND_REPEAT: |
374 | 0 | pixman_repeat = PIXMAN_REPEAT_NORMAL; |
375 | 0 | break; |
376 | 0 | case CAIRO_EXTEND_REFLECT: |
377 | 0 | pixman_repeat = PIXMAN_REPEAT_REFLECT; |
378 | 0 | break; |
379 | 12 | case CAIRO_EXTEND_PAD: |
380 | 12 | pixman_repeat = PIXMAN_REPEAT_PAD; |
381 | 12 | break; |
382 | 12 | } |
383 | | |
384 | 12 | pixman_image_set_repeat (pixman_image, pixman_repeat); |
385 | 12 | } |
386 | | |
387 | 0 | return pixman_image; |
388 | 12 | } |
389 | | |
390 | | static pixman_image_t * |
391 | | _pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern, |
392 | | const cairo_rectangle_int_t *extents, |
393 | | int *tx, int *ty) |
394 | 0 | { |
395 | 0 | pixman_image_t *image; |
396 | 0 | int width, height; |
397 | |
|
398 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
399 | |
|
400 | 0 | *tx = -extents->x; |
401 | 0 | *ty = -extents->y; |
402 | 0 | width = extents->width; |
403 | 0 | height = extents->height; |
404 | |
|
405 | 0 | image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0); |
406 | 0 | if (unlikely (image == NULL)) |
407 | 0 | return NULL; |
408 | | |
409 | 0 | _cairo_mesh_pattern_rasterize (pattern, |
410 | 0 | pixman_image_get_data (image), |
411 | 0 | width, height, |
412 | 0 | pixman_image_get_stride (image), |
413 | 0 | *tx, *ty); |
414 | 0 | return image; |
415 | 0 | } |
416 | | |
417 | | struct acquire_source_cleanup { |
418 | | cairo_surface_t *surface; |
419 | | cairo_image_surface_t *image; |
420 | | void *image_extra; |
421 | | }; |
422 | | |
423 | | static void |
424 | | _acquire_source_cleanup (pixman_image_t *pixman_image, |
425 | | void *closure) |
426 | 0 | { |
427 | 0 | struct acquire_source_cleanup *data = closure; |
428 | |
|
429 | 0 | _cairo_surface_release_source_image (data->surface, |
430 | 0 | data->image, |
431 | 0 | data->image_extra); |
432 | 0 | free (data); |
433 | 0 | } |
434 | | |
435 | | static void |
436 | | _defer_free_cleanup (pixman_image_t *pixman_image, |
437 | | void *closure) |
438 | 28 | { |
439 | 28 | cairo_surface_destroy (closure); |
440 | 28 | } |
441 | | |
442 | | static uint16_t |
443 | | expand_channel (uint16_t v, uint32_t bits) |
444 | 0 | { |
445 | 0 | int offset = 16 - bits; |
446 | 0 | while (offset > 0) { |
447 | 0 | v |= v >> bits; |
448 | 0 | offset -= bits; |
449 | 0 | bits += bits; |
450 | 0 | } |
451 | 0 | return v; |
452 | 0 | } |
453 | | |
454 | | static pixman_image_t * |
455 | | _pixel_to_solid (cairo_image_surface_t *image, int x, int y) |
456 | 0 | { |
457 | 0 | uint32_t pixel; |
458 | 0 | float *rgba; |
459 | 0 | pixman_color_t color; |
460 | |
|
461 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
462 | |
|
463 | 0 | switch (image->format) { |
464 | 0 | default: |
465 | 0 | case CAIRO_FORMAT_INVALID: |
466 | 0 | ASSERT_NOT_REACHED; |
467 | 0 | return NULL; |
468 | | |
469 | 0 | case CAIRO_FORMAT_A1: |
470 | 0 | pixel = *(uint8_t *) (image->data + y * image->stride + x/8); |
471 | 0 | return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image (); |
472 | | |
473 | 0 | case CAIRO_FORMAT_A8: |
474 | 0 | color.alpha = *(uint8_t *) (image->data + y * image->stride + x); |
475 | 0 | color.alpha |= color.alpha << 8; |
476 | 0 | if (color.alpha == 0) |
477 | 0 | return _pixman_transparent_image (); |
478 | 0 | if (color.alpha == 0xffff) |
479 | 0 | return _pixman_black_image (); |
480 | | |
481 | 0 | color.red = color.green = color.blue = 0; |
482 | 0 | return pixman_image_create_solid_fill (&color); |
483 | | |
484 | 0 | case CAIRO_FORMAT_RGB16_565: |
485 | 0 | pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x); |
486 | 0 | if (pixel == 0) |
487 | 0 | return _pixman_black_image (); |
488 | 0 | if (pixel == 0xffff) |
489 | 0 | return _pixman_white_image (); |
490 | | |
491 | 0 | color.alpha = 0xffff; |
492 | 0 | color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5); |
493 | 0 | color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6); |
494 | 0 | color.blue = expand_channel ((pixel & 0x1f) << 11, 5); |
495 | 0 | return pixman_image_create_solid_fill (&color); |
496 | | |
497 | 0 | case CAIRO_FORMAT_RGB30: |
498 | 0 | pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); |
499 | 0 | pixel &= 0x3fffffff; /* ignore alpha bits */ |
500 | 0 | if (pixel == 0) |
501 | 0 | return _pixman_black_image (); |
502 | 0 | if (pixel == 0x3fffffff) |
503 | 0 | return _pixman_white_image (); |
504 | | |
505 | | /* convert 10bpc to 16bpc */ |
506 | 0 | color.alpha = 0xffff; |
507 | 0 | color.red = expand_channel((pixel >> 20) & 0x3fff, 10); |
508 | 0 | color.green = expand_channel((pixel >> 10) & 0x3fff, 10); |
509 | 0 | color.blue = expand_channel(pixel & 0x3fff, 10); |
510 | 0 | return pixman_image_create_solid_fill (&color); |
511 | | |
512 | 0 | case CAIRO_FORMAT_ARGB32: |
513 | 0 | case CAIRO_FORMAT_RGB24: |
514 | 0 | pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x); |
515 | 0 | color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff; |
516 | 0 | if (color.alpha == 0) |
517 | 0 | return _pixman_transparent_image (); |
518 | 0 | if (pixel == 0xffffffff) |
519 | 0 | return _pixman_white_image (); |
520 | 0 | if (color.alpha == 0xffff && (pixel & 0xffffff) == 0) |
521 | 0 | return _pixman_black_image (); |
522 | | |
523 | 0 | color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00); |
524 | 0 | color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00); |
525 | 0 | color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00); |
526 | 0 | return pixman_image_create_solid_fill (&color); |
527 | | |
528 | 0 | case CAIRO_FORMAT_RGB96F: |
529 | 0 | case CAIRO_FORMAT_RGBA128F: |
530 | 0 | if (image->format == CAIRO_FORMAT_RGBA128F) |
531 | 0 | { |
532 | 0 | rgba = (float *)&image->data[y * image->stride + 16 * x]; |
533 | 0 | color.alpha = 65535.f * rgba[3]; |
534 | |
|
535 | 0 | if (color.alpha == 0) |
536 | 0 | return _pixman_transparent_image (); |
537 | 0 | } |
538 | 0 | else |
539 | 0 | { |
540 | 0 | rgba = (float *)&image->data[y * image->stride + 12 * x]; |
541 | 0 | color.alpha = 0xffff; |
542 | 0 | } |
543 | | |
544 | 0 | if (color.alpha == 0xffff && rgba[0] == 0.f && rgba[1] == 0.f && rgba[2] == 0.f) |
545 | 0 | return _pixman_black_image (); |
546 | 0 | if (color.alpha == 0xffff && rgba[0] == 1.f && rgba[1] == 1.f && rgba[2] == 1.f) |
547 | 0 | return _pixman_white_image (); |
548 | | |
549 | 0 | color.red = rgba[0] * 65535.f; |
550 | 0 | color.green = rgba[1] * 65535.f; |
551 | 0 | color.blue = rgba[2] * 65535.f; |
552 | 0 | return pixman_image_create_solid_fill (&color); |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | /* ========================================================================== */ |
557 | | |
558 | | /* Index into filter table */ |
559 | | typedef enum |
560 | | { |
561 | | KERNEL_IMPULSE, |
562 | | KERNEL_BOX, |
563 | | KERNEL_LINEAR, |
564 | | KERNEL_MITCHELL, |
565 | | KERNEL_NOTCH, |
566 | | KERNEL_CATMULL_ROM, |
567 | | KERNEL_LANCZOS3, |
568 | | KERNEL_LANCZOS3_STRETCHED, |
569 | | KERNEL_TENT |
570 | | } kernel_t; |
571 | | |
572 | | /* Produce contribution of a filter of size r for pixel centered on x. |
573 | | For a typical low-pass function this evaluates the function at x/r. |
574 | | If the frequency is higher than 1/2, such as when r is less than 1, |
575 | | this may need to integrate several samples, see cubic for examples. |
576 | | */ |
577 | | typedef double (* kernel_func_t) (double x, double r); |
578 | | |
579 | | /* Return maximum number of pixels that will be non-zero. Except for |
580 | | impluse this is the maximum of 2 and the width of the non-zero part |
581 | | of the filter rounded up to the next integer. |
582 | | */ |
583 | | typedef int (* kernel_width_func_t) (double r); |
584 | | |
585 | | /* Table of filters */ |
586 | | typedef struct |
587 | | { |
588 | | kernel_t kernel; |
589 | | kernel_func_t func; |
590 | | kernel_width_func_t width; |
591 | | } filter_info_t; |
592 | | |
593 | | /* PIXMAN_KERNEL_IMPULSE: Returns pixel nearest the center. This |
594 | | matches PIXMAN_FILTER_NEAREST. This is useful if you wish to |
595 | | combine the result of nearest in one direction with another filter |
596 | | in the other. |
597 | | */ |
598 | | |
599 | | static double |
600 | | impulse_kernel (double x, double r) |
601 | 0 | { |
602 | 0 | return 1; |
603 | 0 | } |
604 | | |
605 | | static int |
606 | | impulse_width (double r) |
607 | 0 | { |
608 | 0 | return 1; |
609 | 0 | } |
610 | | |
611 | | /* PIXMAN_KERNEL_BOX: Intersection of a box of width r with square |
612 | | pixels. This is the smallest possible filter such that the output |
613 | | image contains an equal contribution from all the input |
614 | | pixels. Lots of software uses this. The function is a trapazoid of |
615 | | width r+1, not a box. |
616 | | |
617 | | When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and |
618 | | PIXMAN_KERNEL_TENT all produce the same filter, allowing |
619 | | them to be exchanged at this point. |
620 | | */ |
621 | | |
622 | | static double |
623 | | box_kernel (double x, double r) |
624 | 0 | { |
625 | 0 | return MAX (0.0, MIN (MIN (r, 1.0), |
626 | 0 | MIN ((r + 1) / 2 - x, (r + 1) / 2 + x))); |
627 | 0 | } |
628 | | |
629 | | static int |
630 | | box_width (double r) |
631 | 0 | { |
632 | 0 | return r < 1.0 ? 2 : ceil(r + 1); |
633 | 0 | } |
634 | | |
635 | | /* PIXMAN_KERNEL_LINEAR: Weighted sum of the two pixels nearest the |
636 | | center, or a triangle of width 2. This matches |
637 | | PIXMAN_FILTER_BILINEAR. This is useful if you wish to combine the |
638 | | result of bilinear in one direction with another filter in the |
639 | | other. This is not a good filter if r > 1. You may actually want |
640 | | PIXMAN_FILTER_TENT. |
641 | | |
642 | | When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and |
643 | | PIXMAN_KERNEL_TENT all produce the same filter, allowing |
644 | | them to be exchanged at this point. |
645 | | */ |
646 | | |
647 | | static double |
648 | | linear_kernel (double x, double r) |
649 | 0 | { |
650 | 0 | return MAX (1.0 - fabs(x), 0.0); |
651 | 0 | } |
652 | | |
653 | | static int |
654 | | linear_width (double r) |
655 | 0 | { |
656 | 0 | return 2; |
657 | 0 | } |
658 | | |
659 | | /* Cubic functions described in the Mitchell-Netravali paper. |
660 | | http://mentallandscape.com/Papers_siggraph88.pdf. This describes |
661 | | all possible cubic functions that can be used for sampling. |
662 | | */ |
663 | | |
664 | | static double |
665 | | general_cubic (double x, double r, double B, double C) |
666 | 0 | { |
667 | 0 | double ax; |
668 | 0 | if (r < 1.0) |
669 | 0 | return |
670 | 0 | general_cubic(x * 2 - .5, r * 2, B, C) + |
671 | 0 | general_cubic(x * 2 + .5, r * 2, B, C); |
672 | | |
673 | 0 | ax = fabs (x / r); |
674 | |
|
675 | 0 | if (ax < 1) |
676 | 0 | { |
677 | 0 | return (((12 - 9 * B - 6 * C) * ax + |
678 | 0 | (-18 + 12 * B + 6 * C)) * ax * ax + |
679 | 0 | (6 - 2 * B)) / 6; |
680 | 0 | } |
681 | 0 | else if (ax < 2) |
682 | 0 | { |
683 | 0 | return ((((-B - 6 * C) * ax + |
684 | 0 | (6 * B + 30 * C)) * ax + |
685 | 0 | (-12 * B - 48 * C)) * ax + |
686 | 0 | (8 * B + 24 * C)) / 6; |
687 | 0 | } |
688 | 0 | else |
689 | 0 | { |
690 | 0 | return 0.0; |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | | static int |
695 | | cubic_width (double r) |
696 | 0 | { |
697 | 0 | return MAX (2, ceil (r * 4)); |
698 | 0 | } |
699 | | |
700 | | /* PIXMAN_KERNEL_CATMULL_ROM: Catmull-Rom interpolation. Often called |
701 | | "cubic interpolation", "b-spline", or just "cubic" by other |
702 | | software. This filter has negative values so it can produce ringing |
703 | | and output pixels outside the range of input pixels. This is very |
704 | | close to lanczos2 so there is no reason to supply that as well. |
705 | | */ |
706 | | |
707 | | static double |
708 | | cubic_kernel (double x, double r) |
709 | 0 | { |
710 | 0 | return general_cubic (x, r, 0.0, 0.5); |
711 | 0 | } |
712 | | |
713 | | /* PIXMAN_KERNEL_MITCHELL: Cubic recommended by the Mitchell-Netravali |
714 | | paper. This has negative values and because the values at +/-1 are |
715 | | not zero it does not interpolate the pixels, meaning it will change |
716 | | an image even if there is no translation. |
717 | | */ |
718 | | |
719 | | static double |
720 | | mitchell_kernel (double x, double r) |
721 | 0 | { |
722 | 0 | return general_cubic (x, r, 1/3.0, 1/3.0); |
723 | 0 | } |
724 | | |
725 | | /* PIXMAN_KERNEL_NOTCH: Cubic recommended by the Mitchell-Netravali |
726 | | paper to remove postaliasing artifacts. This does not remove |
727 | | aliasing already present in the source image, though it may appear |
728 | | to due to it's excessive blurriness. In any case this is more |
729 | | useful than gaussian for image reconstruction. |
730 | | */ |
731 | | |
732 | | static double |
733 | | notch_kernel (double x, double r) |
734 | 0 | { |
735 | 0 | return general_cubic (x, r, 1.5, -0.25); |
736 | 0 | } |
737 | | |
738 | | /* PIXMAN_KERNEL_LANCZOS3: lanczos windowed sinc function from -3 to |
739 | | +3. Very popular with high-end software though I think any |
740 | | advantage over cubics is hidden by quantization and programming |
741 | | mistakes. You will see LANCZOS5 or even 7 sometimes. |
742 | | */ |
743 | | |
744 | | static double |
745 | | sinc (double x) |
746 | 0 | { |
747 | 0 | return x ? sin (M_PI * x) / (M_PI * x) : 1.0; |
748 | 0 | } |
749 | | |
750 | | static double |
751 | | lanczos (double x, double n) |
752 | 0 | { |
753 | 0 | return fabs (x) < n ? sinc (x) * sinc (x * (1.0 / n)) : 0.0; |
754 | 0 | } |
755 | | |
756 | | static double |
757 | | lanczos3_kernel (double x, double r) |
758 | 0 | { |
759 | 0 | if (r < 1.0) |
760 | 0 | return |
761 | 0 | lanczos3_kernel (x * 2 - .5, r * 2) + |
762 | 0 | lanczos3_kernel (x * 2 + .5, r * 2); |
763 | 0 | else |
764 | 0 | return lanczos (x / r, 3.0); |
765 | 0 | } |
766 | | |
767 | | static int |
768 | | lanczos3_width (double r) |
769 | 0 | { |
770 | 0 | return MAX (2, ceil (r * 6)); |
771 | 0 | } |
772 | | |
773 | | /* PIXMAN_KERNEL_LANCZOS3_STRETCHED - The LANCZOS3 kernel widened by |
774 | | 4/3. Recommended by Jim Blinn |
775 | | http://graphics.cs.cmu.edu/nsp/course/15-462/Fall07/462/papers/jaggy.pdf |
776 | | */ |
777 | | |
778 | | static double |
779 | | nice_kernel (double x, double r) |
780 | 0 | { |
781 | 0 | return lanczos3_kernel (x, r * (4.0/3)); |
782 | 0 | } |
783 | | |
784 | | static int |
785 | | nice_width (double r) |
786 | 0 | { |
787 | 0 | return MAX (2.0, ceil (r * 8)); |
788 | 0 | } |
789 | | |
790 | | /* PIXMAN_KERNEL_TENT: Triangle of width 2r. Lots of software uses |
791 | | this as a "better" filter, twice the size of a box but smaller than |
792 | | a cubic. |
793 | | |
794 | | When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and |
795 | | PIXMAN_KERNEL_TENT all produce the same filter, allowing |
796 | | them to be exchanged at this point. |
797 | | */ |
798 | | |
799 | | static double |
800 | | tent_kernel (double x, double r) |
801 | 0 | { |
802 | 0 | if (r < 1.0) |
803 | 0 | return box_kernel(x, r); |
804 | 0 | else |
805 | 0 | return MAX (1.0 - fabs(x / r), 0.0); |
806 | 0 | } |
807 | | |
808 | | static int |
809 | | tent_width (double r) |
810 | 0 | { |
811 | 0 | return r < 1.0 ? 2 : ceil(2 * r); |
812 | 0 | } |
813 | | |
814 | | |
815 | | static const filter_info_t filters[] = |
816 | | { |
817 | | { KERNEL_IMPULSE, impulse_kernel, impulse_width }, |
818 | | { KERNEL_BOX, box_kernel, box_width }, |
819 | | { KERNEL_LINEAR, linear_kernel, linear_width }, |
820 | | { KERNEL_MITCHELL, mitchell_kernel, cubic_width }, |
821 | | { KERNEL_NOTCH, notch_kernel, cubic_width }, |
822 | | { KERNEL_CATMULL_ROM, cubic_kernel, cubic_width }, |
823 | | { KERNEL_LANCZOS3, lanczos3_kernel, lanczos3_width }, |
824 | | { KERNEL_LANCZOS3_STRETCHED,nice_kernel, nice_width }, |
825 | | { KERNEL_TENT, tent_kernel, tent_width } |
826 | | }; |
827 | | |
828 | | /* Fills in one dimension of the filter array */ |
829 | | static void get_filter(kernel_t filter, double r, |
830 | | int width, int subsample, |
831 | | pixman_fixed_t* out) |
832 | 0 | { |
833 | 0 | int i; |
834 | 0 | pixman_fixed_t *p = out; |
835 | 0 | int n_phases = 1 << subsample; |
836 | 0 | double step = 1.0 / n_phases; |
837 | 0 | kernel_func_t func = filters[filter].func; |
838 | | |
839 | | /* special-case the impulse filter: */ |
840 | 0 | if (width <= 1) |
841 | 0 | { |
842 | 0 | for (i = 0; i < n_phases; ++i) |
843 | 0 | *p++ = pixman_fixed_1; |
844 | 0 | return; |
845 | 0 | } |
846 | | |
847 | 0 | for (i = 0; i < n_phases; ++i) |
848 | 0 | { |
849 | 0 | double frac = (i + .5) * step; |
850 | | /* Center of left-most pixel: */ |
851 | 0 | double x1 = ceil (frac - width / 2.0 - 0.5) - frac + 0.5; |
852 | 0 | double total = 0; |
853 | 0 | pixman_fixed_t new_total = 0; |
854 | 0 | int j; |
855 | |
|
856 | 0 | for (j = 0; j < width; ++j) |
857 | 0 | { |
858 | 0 | double v = func(x1 + j, r); |
859 | 0 | total += v; |
860 | 0 | p[j] = pixman_double_to_fixed (v); |
861 | 0 | } |
862 | | |
863 | | /* Normalize */ |
864 | 0 | total = 1 / total; |
865 | 0 | for (j = 0; j < width; ++j) |
866 | 0 | new_total += (p[j] *= total); |
867 | | |
868 | | /* Put any error on center pixel */ |
869 | 0 | p[width / 2] += (pixman_fixed_1 - new_total); |
870 | |
|
871 | 0 | p += width; |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | |
876 | | /* Create the parameter list for a SEPARABLE_CONVOLUTION filter |
877 | | * with the given kernels and scale parameters. |
878 | | */ |
879 | | static pixman_fixed_t * |
880 | | create_separable_convolution (int *n_values, |
881 | | kernel_t xfilter, |
882 | | double sx, |
883 | | kernel_t yfilter, |
884 | | double sy) |
885 | 0 | { |
886 | 0 | int xwidth, xsubsample, ywidth, ysubsample, size_x, size_y; |
887 | 0 | pixman_fixed_t *params; |
888 | |
|
889 | 0 | xwidth = filters[xfilter].width(sx); |
890 | 0 | xsubsample = 0; |
891 | 0 | if (xwidth > 1) |
892 | 0 | while (sx * (1 << xsubsample) <= 128.0) xsubsample++; |
893 | 0 | size_x = (1 << xsubsample) * xwidth; |
894 | |
|
895 | 0 | ywidth = filters[yfilter].width(sy); |
896 | 0 | ysubsample = 0; |
897 | 0 | if (ywidth > 1) |
898 | 0 | while (sy * (1 << ysubsample) <= 128.0) ysubsample++; |
899 | 0 | size_y = (1 << ysubsample) * ywidth; |
900 | |
|
901 | 0 | *n_values = 4 + size_x + size_y; |
902 | 0 | params = _cairo_malloc (*n_values * sizeof (pixman_fixed_t)); |
903 | 0 | if (!params) return 0; |
904 | | |
905 | 0 | params[0] = pixman_int_to_fixed (xwidth); |
906 | 0 | params[1] = pixman_int_to_fixed (ywidth); |
907 | 0 | params[2] = pixman_int_to_fixed (xsubsample); |
908 | 0 | params[3] = pixman_int_to_fixed (ysubsample); |
909 | |
|
910 | 0 | get_filter(xfilter, sx, xwidth, xsubsample, params + 4); |
911 | 0 | get_filter(yfilter, sy, ywidth, ysubsample, params + 4 + size_x); |
912 | |
|
913 | 0 | return params; |
914 | 0 | } |
915 | | |
916 | | /* ========================================================================== */ |
917 | | |
918 | | static cairo_bool_t |
919 | | _pixman_image_set_properties (pixman_image_t *pixman_image, |
920 | | const cairo_pattern_t *pattern, |
921 | | const cairo_rectangle_int_t *extents, |
922 | | int *ix,int *iy) |
923 | 28 | { |
924 | 28 | pixman_transform_t pixman_transform; |
925 | 28 | cairo_int_status_t status; |
926 | | |
927 | 28 | status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix, |
928 | 28 | pattern->filter, |
929 | 28 | extents->x + extents->width/2., |
930 | 28 | extents->y + extents->height/2., |
931 | 28 | &pixman_transform, ix, iy); |
932 | 28 | if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) |
933 | 16 | { |
934 | | /* If the transform is an identity, we don't need to set it |
935 | | * and we can use any filtering, so choose the fastest one. */ |
936 | 16 | pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); |
937 | 16 | } |
938 | 12 | else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS || |
939 | 12 | ! pixman_image_set_transform (pixman_image, |
940 | 12 | &pixman_transform))) |
941 | 0 | { |
942 | 0 | return FALSE; |
943 | 0 | } |
944 | 12 | else |
945 | 12 | { |
946 | 12 | pixman_filter_t pixman_filter; |
947 | 12 | kernel_t kernel; |
948 | 12 | double dx, dy; |
949 | | |
950 | | /* Compute scale factors from the pattern matrix. These scale |
951 | | * factors are from user to pattern space, and as such they |
952 | | * are greater than 1.0 for downscaling and less than 1.0 for |
953 | | * upscaling. The factors are the size of an axis-aligned |
954 | | * rectangle with the same area as the parallelgram a 1x1 |
955 | | * square transforms to. |
956 | | */ |
957 | 12 | dx = hypot (pattern->matrix.xx, pattern->matrix.xy); |
958 | 12 | dy = hypot (pattern->matrix.yx, pattern->matrix.yy); |
959 | | |
960 | | /* Clip at maximum pixman_fixed number. Besides making it |
961 | | * passable to pixman, this avoids errors from inf and nan. |
962 | | */ |
963 | 12 | if (! (dx < 0x7FFF)) dx = 0x7FFF; |
964 | 12 | if (! (dy < 0x7FFF)) dy = 0x7FFF; |
965 | | |
966 | 12 | switch (pattern->filter) { |
967 | 0 | case CAIRO_FILTER_FAST: |
968 | 0 | pixman_filter = PIXMAN_FILTER_FAST; |
969 | 0 | break; |
970 | 0 | case CAIRO_FILTER_GOOD: |
971 | 0 | pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION; |
972 | 0 | kernel = KERNEL_BOX; |
973 | | /* Clip the filter size to prevent extreme slowness. This |
974 | | value could be raised if 2-pass filtering is done */ |
975 | 0 | if (dx > 16.0) dx = 16.0; |
976 | 0 | if (dy > 16.0) dy = 16.0; |
977 | | /* Match the bilinear filter for scales > .75: */ |
978 | 0 | if (dx < 1.0/0.75) dx = 1.0; |
979 | 0 | if (dy < 1.0/0.75) dy = 1.0; |
980 | 0 | break; |
981 | 0 | case CAIRO_FILTER_BEST: |
982 | 0 | pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION; |
983 | 0 | kernel = KERNEL_CATMULL_ROM; /* LANCZOS3 is better but not much */ |
984 | | /* Clip the filter size to prevent extreme slowness. This |
985 | | value could be raised if 2-pass filtering is done */ |
986 | 0 | if (dx > 16.0) { dx = 16.0; kernel = KERNEL_BOX; } |
987 | | /* blur up to 2x scale, then blend to square pixels for larger: */ |
988 | 0 | else if (dx < 1.0) { |
989 | 0 | if (dx < 1.0/128) dx = 1.0/127; |
990 | 0 | else if (dx < 0.5) dx = 1.0 / (1.0 / dx - 1.0); |
991 | 0 | else dx = 1.0; |
992 | 0 | } |
993 | 0 | if (dy > 16.0) { dy = 16.0; kernel = KERNEL_BOX; } |
994 | 0 | else if (dy < 1.0) { |
995 | 0 | if (dy < 1.0/128) dy = 1.0/127; |
996 | 0 | else if (dy < 0.5) dy = 1.0 / (1.0 / dy - 1.0); |
997 | 0 | else dy = 1.0; |
998 | 0 | } |
999 | 0 | break; |
1000 | 12 | case CAIRO_FILTER_NEAREST: |
1001 | 12 | pixman_filter = PIXMAN_FILTER_NEAREST; |
1002 | 12 | break; |
1003 | 0 | case CAIRO_FILTER_BILINEAR: |
1004 | 0 | pixman_filter = PIXMAN_FILTER_BILINEAR; |
1005 | 0 | break; |
1006 | 0 | case CAIRO_FILTER_GAUSSIAN: |
1007 | | /* XXX: The GAUSSIAN value has no implementation in cairo |
1008 | | * whatsoever, so it was really a mistake to have it in the |
1009 | | * API. We could fix this by officially deprecating it, or |
1010 | | * else inventing semantics and providing an actual |
1011 | | * implementation for it. */ |
1012 | 0 | default: |
1013 | 0 | pixman_filter = PIXMAN_FILTER_BEST; |
1014 | 12 | } |
1015 | | |
1016 | 12 | if (pixman_filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) { |
1017 | 0 | int n_params; |
1018 | 0 | pixman_fixed_t *params; |
1019 | 0 | params = create_separable_convolution |
1020 | 0 | (&n_params, kernel, dx, kernel, dy); |
1021 | 0 | pixman_image_set_filter (pixman_image, pixman_filter, |
1022 | 0 | params, n_params); |
1023 | 0 | free (params); |
1024 | 12 | } else { |
1025 | 12 | pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0); |
1026 | 12 | } |
1027 | 12 | } |
1028 | | |
1029 | 28 | { |
1030 | 28 | pixman_repeat_t pixman_repeat; |
1031 | | |
1032 | 28 | switch (pattern->extend) { |
1033 | 0 | default: |
1034 | 28 | case CAIRO_EXTEND_NONE: |
1035 | 28 | pixman_repeat = PIXMAN_REPEAT_NONE; |
1036 | 28 | break; |
1037 | 0 | case CAIRO_EXTEND_REPEAT: |
1038 | 0 | pixman_repeat = PIXMAN_REPEAT_NORMAL; |
1039 | 0 | break; |
1040 | 0 | case CAIRO_EXTEND_REFLECT: |
1041 | 0 | pixman_repeat = PIXMAN_REPEAT_REFLECT; |
1042 | 0 | break; |
1043 | 0 | case CAIRO_EXTEND_PAD: |
1044 | 0 | pixman_repeat = PIXMAN_REPEAT_PAD; |
1045 | 0 | break; |
1046 | 28 | } |
1047 | | |
1048 | 28 | pixman_image_set_repeat (pixman_image, pixman_repeat); |
1049 | 28 | } |
1050 | | |
1051 | 28 | if (pattern->has_component_alpha) |
1052 | 0 | pixman_image_set_component_alpha (pixman_image, TRUE); |
1053 | | |
1054 | 28 | return TRUE; |
1055 | 28 | } |
1056 | | |
1057 | | struct proxy { |
1058 | | cairo_surface_t base; |
1059 | | cairo_surface_t *image; |
1060 | | }; |
1061 | | |
1062 | | static cairo_status_t |
1063 | | proxy_acquire_source_image (void *abstract_surface, |
1064 | | cairo_image_surface_t **image_out, |
1065 | | void **image_extra) |
1066 | 0 | { |
1067 | 0 | struct proxy *proxy = abstract_surface; |
1068 | 0 | return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); |
1069 | 0 | } |
1070 | | |
1071 | | static void |
1072 | | proxy_release_source_image (void *abstract_surface, |
1073 | | cairo_image_surface_t *image, |
1074 | | void *image_extra) |
1075 | 0 | { |
1076 | 0 | struct proxy *proxy = abstract_surface; |
1077 | 0 | _cairo_surface_release_source_image (proxy->image, image, image_extra); |
1078 | 0 | } |
1079 | | |
1080 | | static cairo_status_t |
1081 | | proxy_finish (void *abstract_surface) |
1082 | 51 | { |
1083 | 51 | return CAIRO_STATUS_SUCCESS; |
1084 | 51 | } |
1085 | | |
1086 | | static const cairo_surface_backend_t proxy_backend = { |
1087 | | CAIRO_INTERNAL_SURFACE_TYPE_NULL, |
1088 | | proxy_finish, |
1089 | | NULL, |
1090 | | |
1091 | | NULL, /* create similar */ |
1092 | | NULL, /* create similar image */ |
1093 | | NULL, /* map to image */ |
1094 | | NULL, /* unmap image */ |
1095 | | |
1096 | | _cairo_surface_default_source, |
1097 | | proxy_acquire_source_image, |
1098 | | proxy_release_source_image, |
1099 | | }; |
1100 | | |
1101 | | static cairo_surface_t * |
1102 | | attach_proxy (cairo_surface_t *source, |
1103 | | cairo_surface_t *image) |
1104 | 51 | { |
1105 | 51 | struct proxy *proxy; |
1106 | | |
1107 | 51 | proxy = _cairo_calloc (sizeof (*proxy)); |
1108 | 51 | if (unlikely (proxy == NULL)) |
1109 | 0 | return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
1110 | | |
1111 | 51 | _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content, FALSE); |
1112 | | |
1113 | 51 | proxy->image = image; |
1114 | 51 | _cairo_surface_attach_snapshot (source, &proxy->base, NULL); |
1115 | | |
1116 | 51 | return &proxy->base; |
1117 | 51 | } |
1118 | | |
1119 | | static void |
1120 | | detach_proxy (cairo_surface_t *source, |
1121 | | cairo_surface_t *proxy) |
1122 | 51 | { |
1123 | 51 | cairo_surface_finish (proxy); |
1124 | 51 | cairo_surface_destroy (proxy); |
1125 | 51 | } |
1126 | | |
1127 | | static cairo_surface_t * |
1128 | | get_proxy (cairo_surface_t *proxy) |
1129 | 0 | { |
1130 | 0 | return ((struct proxy *)proxy)->image; |
1131 | 0 | } |
1132 | | |
1133 | | static pixman_image_t * |
1134 | | _pixman_image_for_recording (cairo_image_surface_t *dst, |
1135 | | const cairo_surface_pattern_t *pattern, |
1136 | | cairo_bool_t is_mask, |
1137 | | const cairo_rectangle_int_t *extents, |
1138 | | const cairo_rectangle_int_t *sample, |
1139 | | int *ix, int *iy) |
1140 | 51 | { |
1141 | 51 | cairo_surface_t *source, *clone, *proxy; |
1142 | 51 | cairo_rectangle_int_t limit; |
1143 | 51 | cairo_rectangle_int_t src_limit; |
1144 | 51 | pixman_image_t *pixman_image; |
1145 | 51 | cairo_status_t status; |
1146 | 51 | cairo_extend_t extend; |
1147 | 51 | cairo_matrix_t *m, matrix; |
1148 | 51 | double sx = 1.0, sy = 1.0; |
1149 | 51 | int tx = 0, ty = 0; |
1150 | | |
1151 | 51 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1152 | | |
1153 | 51 | *ix = *iy = 0; |
1154 | | |
1155 | 51 | source = _cairo_pattern_get_source (pattern, &limit); |
1156 | 51 | src_limit = limit; |
1157 | | |
1158 | 51 | extend = pattern->base.extend; |
1159 | 51 | if (_cairo_rectangle_contains_rectangle (&limit, sample)) |
1160 | 51 | extend = CAIRO_EXTEND_NONE; |
1161 | | |
1162 | 51 | if (extend == CAIRO_EXTEND_NONE) { |
1163 | 51 | if (! _cairo_rectangle_intersect (&limit, sample)) |
1164 | 0 | return _pixman_transparent_image (); |
1165 | 51 | } |
1166 | | |
1167 | 51 | if (! _cairo_matrix_is_identity (&pattern->base.matrix)) { |
1168 | 32 | double x1, y1, x2, y2; |
1169 | | |
1170 | 32 | matrix = pattern->base.matrix; |
1171 | 32 | status = cairo_matrix_invert (&matrix); |
1172 | 32 | assert (status == CAIRO_STATUS_SUCCESS); |
1173 | | |
1174 | 32 | x1 = limit.x; |
1175 | 32 | y1 = limit.y; |
1176 | 32 | x2 = limit.x + limit.width; |
1177 | 32 | y2 = limit.y + limit.height; |
1178 | | |
1179 | 32 | _cairo_matrix_transform_bounding_box (&matrix, |
1180 | 32 | &x1, &y1, &x2, &y2, NULL); |
1181 | | |
1182 | 32 | limit.x = floor (x1); |
1183 | 32 | limit.y = floor (y1); |
1184 | 32 | limit.width = ceil (x2) - limit.x; |
1185 | 32 | limit.height = ceil (y2) - limit.y; |
1186 | 32 | sx = (double)src_limit.width / limit.width; |
1187 | 32 | sy = (double)src_limit.height / limit.height; |
1188 | 32 | } |
1189 | 51 | tx = limit.x; |
1190 | 51 | ty = limit.y; |
1191 | | |
1192 | | /* XXX transformations! */ |
1193 | 51 | proxy = _cairo_surface_has_snapshot (source, &proxy_backend); |
1194 | 51 | if (proxy != NULL) { |
1195 | 0 | clone = cairo_surface_reference (get_proxy (proxy)); |
1196 | 0 | goto done; |
1197 | 0 | } |
1198 | | |
1199 | 51 | if (is_mask) { |
1200 | 0 | clone = cairo_image_surface_create (CAIRO_FORMAT_A8, |
1201 | 0 | limit.width, limit.height); |
1202 | 51 | } else { |
1203 | 51 | if (dst->base.content == source->content) |
1204 | 51 | clone = cairo_image_surface_create (dst->format, |
1205 | 51 | limit.width, limit.height); |
1206 | 0 | else |
1207 | 0 | clone = _cairo_image_surface_create_with_content (source->content, |
1208 | 0 | limit.width, |
1209 | 0 | limit.height); |
1210 | 51 | if (dst->base.foreground_source) |
1211 | 0 | clone->foreground_source = cairo_pattern_reference (dst->base.foreground_source); |
1212 | 51 | } |
1213 | | |
1214 | 51 | m = NULL; |
1215 | 51 | if (extend == CAIRO_EXTEND_NONE) { |
1216 | 51 | matrix = pattern->base.matrix; |
1217 | 51 | if (tx | ty) |
1218 | 32 | cairo_matrix_translate (&matrix, tx, ty); |
1219 | 51 | m = &matrix; |
1220 | 51 | } else { |
1221 | 0 | cairo_matrix_init_scale (&matrix, sx, sy); |
1222 | 0 | cairo_matrix_translate (&matrix, src_limit.x/sx, src_limit.y/sy); |
1223 | 0 | m = &matrix; |
1224 | 0 | } |
1225 | | |
1226 | | /* Handle recursion by returning future reads from the current image */ |
1227 | 51 | proxy = attach_proxy (source, clone); |
1228 | 51 | status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL); |
1229 | 51 | if (clone->foreground_used) |
1230 | 0 | dst->base.foreground_used = clone->foreground_used; |
1231 | 51 | detach_proxy (source, proxy); |
1232 | 51 | if (unlikely (status)) { |
1233 | 0 | cairo_surface_destroy (clone); |
1234 | 0 | return NULL; |
1235 | 0 | } |
1236 | | |
1237 | 51 | done: |
1238 | 51 | pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); |
1239 | 51 | cairo_surface_destroy (clone); |
1240 | | |
1241 | 51 | if (extend == CAIRO_EXTEND_NONE) { |
1242 | 51 | *ix = -limit.x; |
1243 | 51 | *iy = -limit.y; |
1244 | 51 | } else { |
1245 | 0 | cairo_pattern_union_t tmp_pattern; |
1246 | 0 | _cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base); |
1247 | 0 | matrix = pattern->base.matrix; |
1248 | 0 | status = cairo_matrix_invert(&matrix); |
1249 | 0 | assert (status == CAIRO_STATUS_SUCCESS); |
1250 | 0 | cairo_matrix_translate (&matrix, src_limit.x, src_limit.y); |
1251 | 0 | cairo_matrix_scale (&matrix, sx, sy); |
1252 | 0 | status = cairo_matrix_invert(&matrix); |
1253 | 0 | assert (status == CAIRO_STATUS_SUCCESS); |
1254 | 0 | cairo_pattern_set_matrix (&tmp_pattern.base, &matrix); |
1255 | 0 | if (! _pixman_image_set_properties (pixman_image, |
1256 | 0 | &tmp_pattern.base, extents, |
1257 | 0 | ix, iy)) { |
1258 | 0 | pixman_image_unref (pixman_image); |
1259 | 0 | pixman_image= NULL; |
1260 | 0 | } |
1261 | 0 | } |
1262 | | |
1263 | 51 | return pixman_image; |
1264 | 51 | } |
1265 | | |
1266 | | static pixman_image_t * |
1267 | | _pixman_image_for_surface (cairo_image_surface_t *dst, |
1268 | | const cairo_surface_pattern_t *pattern, |
1269 | | cairo_bool_t is_mask, |
1270 | | const cairo_rectangle_int_t *extents, |
1271 | | const cairo_rectangle_int_t *sample, |
1272 | | int *ix, int *iy) |
1273 | 79 | { |
1274 | 79 | cairo_extend_t extend = pattern->base.extend; |
1275 | 79 | pixman_image_t *pixman_image; |
1276 | | |
1277 | 79 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1278 | | |
1279 | 79 | *ix = *iy = 0; |
1280 | 79 | pixman_image = NULL; |
1281 | 79 | if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) |
1282 | 51 | return _pixman_image_for_recording(dst, pattern, |
1283 | 51 | is_mask, extents, sample, |
1284 | 51 | ix, iy); |
1285 | | |
1286 | 28 | if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && |
1287 | 28 | (! is_mask || ! pattern->base.has_component_alpha || |
1288 | 0 | (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0)) |
1289 | 28 | { |
1290 | 28 | cairo_surface_t *defer_free = NULL; |
1291 | 28 | cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; |
1292 | 28 | cairo_surface_type_t type; |
1293 | | |
1294 | 28 | if (_cairo_surface_is_snapshot (&source->base)) { |
1295 | 28 | defer_free = _cairo_surface_snapshot_get_target (&source->base); |
1296 | 28 | source = (cairo_image_surface_t *) defer_free; |
1297 | 28 | } |
1298 | | |
1299 | 28 | type = source->base.backend->type; |
1300 | 28 | if (type == CAIRO_SURFACE_TYPE_IMAGE) { |
1301 | 28 | if (extend != CAIRO_EXTEND_NONE && |
1302 | 0 | sample->x >= 0 && |
1303 | 0 | sample->y >= 0 && |
1304 | 0 | sample->x + sample->width <= source->width && |
1305 | 0 | sample->y + sample->height <= source->height) |
1306 | 0 | { |
1307 | 0 | extend = CAIRO_EXTEND_NONE; |
1308 | 0 | } |
1309 | | |
1310 | 28 | if (sample->width == 1 && sample->height == 1) { |
1311 | 0 | if (sample->x < 0 || |
1312 | 0 | sample->y < 0 || |
1313 | 0 | sample->x >= source->width || |
1314 | 0 | sample->y >= source->height) |
1315 | 0 | { |
1316 | 0 | if (extend == CAIRO_EXTEND_NONE) { |
1317 | 0 | cairo_surface_destroy (defer_free); |
1318 | 0 | return _pixman_transparent_image (); |
1319 | 0 | } |
1320 | 0 | } |
1321 | 0 | else |
1322 | 0 | { |
1323 | 0 | pixman_image = _pixel_to_solid (source, |
1324 | 0 | sample->x, sample->y); |
1325 | 0 | if (pixman_image) { |
1326 | 0 | cairo_surface_destroy (defer_free); |
1327 | 0 | return pixman_image; |
1328 | 0 | } |
1329 | 0 | } |
1330 | 0 | } |
1331 | | |
1332 | | #if PIXMAN_HAS_ATOMIC_OPS |
1333 | | /* avoid allocating a 'pattern' image if we can reuse the original */ |
1334 | | if (extend == CAIRO_EXTEND_NONE && |
1335 | | _cairo_matrix_is_pixman_translation (&pattern->base.matrix, |
1336 | | pattern->base.filter, |
1337 | | ix, iy)) |
1338 | | { |
1339 | | cairo_surface_destroy (defer_free); |
1340 | | return pixman_image_ref (source->pixman_image); |
1341 | | } |
1342 | | #endif |
1343 | | |
1344 | 28 | pixman_image = pixman_image_create_bits (source->pixman_format, |
1345 | 28 | source->width, |
1346 | 28 | source->height, |
1347 | 28 | (uint32_t *) source->data, |
1348 | 28 | source->stride); |
1349 | 28 | if (unlikely (pixman_image == NULL)) { |
1350 | 0 | cairo_surface_destroy (defer_free); |
1351 | 0 | return NULL; |
1352 | 0 | } |
1353 | | |
1354 | 28 | if (defer_free) { |
1355 | 28 | pixman_image_set_destroy_function (pixman_image, |
1356 | 28 | _defer_free_cleanup, |
1357 | 28 | defer_free); |
1358 | 28 | } |
1359 | 28 | } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { |
1360 | 0 | cairo_surface_subsurface_t *sub; |
1361 | 0 | cairo_bool_t is_contained = FALSE; |
1362 | |
|
1363 | 0 | sub = (cairo_surface_subsurface_t *) source; |
1364 | 0 | source = (cairo_image_surface_t *) sub->target; |
1365 | |
|
1366 | 0 | if (sample->x >= 0 && |
1367 | 0 | sample->y >= 0 && |
1368 | 0 | sample->x + sample->width <= sub->extents.width && |
1369 | 0 | sample->y + sample->height <= sub->extents.height) |
1370 | 0 | { |
1371 | 0 | is_contained = TRUE; |
1372 | 0 | } |
1373 | |
|
1374 | 0 | if (sample->width == 1 && sample->height == 1) { |
1375 | 0 | if (is_contained) { |
1376 | 0 | pixman_image = _pixel_to_solid (source, |
1377 | 0 | sub->extents.x + sample->x, |
1378 | 0 | sub->extents.y + sample->y); |
1379 | 0 | if (pixman_image) |
1380 | 0 | return pixman_image; |
1381 | 0 | } else { |
1382 | 0 | if (extend == CAIRO_EXTEND_NONE) |
1383 | 0 | return _pixman_transparent_image (); |
1384 | 0 | } |
1385 | 0 | } |
1386 | | |
1387 | | #if PIXMAN_HAS_ATOMIC_OPS |
1388 | | *ix = sub->extents.x; |
1389 | | *iy = sub->extents.y; |
1390 | | if (is_contained && |
1391 | | _cairo_matrix_is_pixman_translation (&pattern->base.matrix, |
1392 | | pattern->base.filter, |
1393 | | ix, iy)) |
1394 | | { |
1395 | | return pixman_image_ref (source->pixman_image); |
1396 | | } |
1397 | | #endif |
1398 | | |
1399 | | /* Avoid sub-byte offsets, force a copy in that case. */ |
1400 | 0 | if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { |
1401 | 0 | if (is_contained) { |
1402 | 0 | void *data = source->data |
1403 | 0 | + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 |
1404 | 0 | + sub->extents.y * source->stride; |
1405 | 0 | pixman_image = pixman_image_create_bits (source->pixman_format, |
1406 | 0 | sub->extents.width, |
1407 | 0 | sub->extents.height, |
1408 | 0 | data, |
1409 | 0 | source->stride); |
1410 | 0 | if (unlikely (pixman_image == NULL)) |
1411 | 0 | return NULL; |
1412 | 0 | } else { |
1413 | | /* XXX for a simple translation and EXTEND_NONE we can |
1414 | | * fix up the pattern matrix instead. |
1415 | | */ |
1416 | 0 | } |
1417 | 0 | } |
1418 | 0 | } |
1419 | 28 | } |
1420 | | |
1421 | 28 | if (pixman_image == NULL) { |
1422 | 0 | struct acquire_source_cleanup *cleanup; |
1423 | 0 | cairo_image_surface_t *image; |
1424 | 0 | void *extra; |
1425 | 0 | cairo_status_t status; |
1426 | |
|
1427 | 0 | status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra); |
1428 | 0 | if (unlikely (status)) |
1429 | 0 | return NULL; |
1430 | | |
1431 | 0 | pixman_image = pixman_image_create_bits (image->pixman_format, |
1432 | 0 | image->width, |
1433 | 0 | image->height, |
1434 | 0 | (uint32_t *) image->data, |
1435 | 0 | image->stride); |
1436 | 0 | if (unlikely (pixman_image == NULL)) { |
1437 | 0 | _cairo_surface_release_source_image (pattern->surface, image, extra); |
1438 | 0 | return NULL; |
1439 | 0 | } |
1440 | | |
1441 | 0 | cleanup = _cairo_malloc (sizeof (*cleanup)); |
1442 | 0 | if (unlikely (cleanup == NULL)) { |
1443 | 0 | _cairo_surface_release_source_image (pattern->surface, image, extra); |
1444 | 0 | pixman_image_unref (pixman_image); |
1445 | 0 | return NULL; |
1446 | 0 | } |
1447 | | |
1448 | 0 | cleanup->surface = pattern->surface; |
1449 | 0 | cleanup->image = image; |
1450 | 0 | cleanup->image_extra = extra; |
1451 | 0 | pixman_image_set_destroy_function (pixman_image, |
1452 | 0 | _acquire_source_cleanup, cleanup); |
1453 | 0 | } |
1454 | | |
1455 | 28 | if (! _pixman_image_set_properties (pixman_image, |
1456 | 28 | &pattern->base, extents, |
1457 | 28 | ix, iy)) { |
1458 | 0 | pixman_image_unref (pixman_image); |
1459 | 0 | pixman_image= NULL; |
1460 | 0 | } |
1461 | | |
1462 | 28 | return pixman_image; |
1463 | 28 | } |
1464 | | |
1465 | | struct raster_source_cleanup { |
1466 | | const cairo_pattern_t *pattern; |
1467 | | cairo_surface_t *surface; |
1468 | | cairo_image_surface_t *image; |
1469 | | void *image_extra; |
1470 | | }; |
1471 | | |
1472 | | static void |
1473 | | _raster_source_cleanup (pixman_image_t *pixman_image, |
1474 | | void *closure) |
1475 | 0 | { |
1476 | 0 | struct raster_source_cleanup *data = closure; |
1477 | |
|
1478 | 0 | _cairo_surface_release_source_image (data->surface, |
1479 | 0 | data->image, |
1480 | 0 | data->image_extra); |
1481 | |
|
1482 | 0 | _cairo_raster_source_pattern_release (data->pattern, |
1483 | 0 | data->surface); |
1484 | |
|
1485 | 0 | free (data); |
1486 | 0 | } |
1487 | | |
1488 | | static pixman_image_t * |
1489 | | _pixman_image_for_raster (cairo_image_surface_t *dst, |
1490 | | const cairo_raster_source_pattern_t *pattern, |
1491 | | cairo_bool_t is_mask, |
1492 | | const cairo_rectangle_int_t *extents, |
1493 | | const cairo_rectangle_int_t *sample, |
1494 | | int *ix, int *iy) |
1495 | 0 | { |
1496 | 0 | pixman_image_t *pixman_image; |
1497 | 0 | struct raster_source_cleanup *cleanup; |
1498 | 0 | cairo_image_surface_t *image; |
1499 | 0 | void *extra; |
1500 | 0 | cairo_status_t status; |
1501 | 0 | cairo_surface_t *surface; |
1502 | |
|
1503 | 0 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1504 | |
|
1505 | 0 | *ix = *iy = 0; |
1506 | |
|
1507 | 0 | surface = _cairo_raster_source_pattern_acquire (&pattern->base, |
1508 | 0 | &dst->base, NULL); |
1509 | 0 | if (unlikely (surface == NULL || surface->status)) |
1510 | 0 | return NULL; |
1511 | | |
1512 | 0 | status = _cairo_surface_acquire_source_image (surface, &image, &extra); |
1513 | 0 | if (unlikely (status)) { |
1514 | 0 | _cairo_raster_source_pattern_release (&pattern->base, surface); |
1515 | 0 | return NULL; |
1516 | 0 | } |
1517 | | |
1518 | 0 | assert (image->width == pattern->extents.width); |
1519 | 0 | assert (image->height == pattern->extents.height); |
1520 | |
|
1521 | 0 | pixman_image = pixman_image_create_bits (image->pixman_format, |
1522 | 0 | image->width, |
1523 | 0 | image->height, |
1524 | 0 | (uint32_t *) image->data, |
1525 | 0 | image->stride); |
1526 | 0 | if (unlikely (pixman_image == NULL)) { |
1527 | 0 | _cairo_surface_release_source_image (surface, image, extra); |
1528 | 0 | _cairo_raster_source_pattern_release (&pattern->base, surface); |
1529 | 0 | return NULL; |
1530 | 0 | } |
1531 | | |
1532 | 0 | cleanup = _cairo_calloc (sizeof (*cleanup)); |
1533 | 0 | if (unlikely (cleanup == NULL)) { |
1534 | 0 | pixman_image_unref (pixman_image); |
1535 | 0 | _cairo_surface_release_source_image (surface, image, extra); |
1536 | 0 | _cairo_raster_source_pattern_release (&pattern->base, surface); |
1537 | 0 | return NULL; |
1538 | 0 | } |
1539 | | |
1540 | 0 | cleanup->pattern = &pattern->base; |
1541 | 0 | cleanup->surface = surface; |
1542 | 0 | cleanup->image = image; |
1543 | 0 | cleanup->image_extra = extra; |
1544 | 0 | pixman_image_set_destroy_function (pixman_image, |
1545 | 0 | _raster_source_cleanup, cleanup); |
1546 | |
|
1547 | 0 | if (! _pixman_image_set_properties (pixman_image, |
1548 | 0 | &pattern->base, extents, |
1549 | 0 | ix, iy)) { |
1550 | 0 | pixman_image_unref (pixman_image); |
1551 | 0 | pixman_image= NULL; |
1552 | 0 | } |
1553 | |
|
1554 | 0 | return pixman_image; |
1555 | 0 | } |
1556 | | |
1557 | | pixman_image_t * |
1558 | | _pixman_image_for_pattern (cairo_image_surface_t *dst, |
1559 | | const cairo_pattern_t *pattern, |
1560 | | cairo_bool_t is_mask, |
1561 | | const cairo_rectangle_int_t *extents, |
1562 | | const cairo_rectangle_int_t *sample, |
1563 | | int *tx, int *ty) |
1564 | 115 | { |
1565 | 115 | *tx = *ty = 0; |
1566 | | |
1567 | 115 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1568 | | |
1569 | 115 | if (pattern == NULL) |
1570 | 0 | return _pixman_white_image (); |
1571 | | |
1572 | 115 | switch (pattern->type) { |
1573 | 0 | default: |
1574 | 0 | ASSERT_NOT_REACHED; |
1575 | 24 | case CAIRO_PATTERN_TYPE_SOLID: |
1576 | 24 | return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color); |
1577 | | |
1578 | 0 | case CAIRO_PATTERN_TYPE_RADIAL: |
1579 | 12 | case CAIRO_PATTERN_TYPE_LINEAR: |
1580 | 12 | return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern, |
1581 | 12 | extents, tx, ty); |
1582 | | |
1583 | 0 | case CAIRO_PATTERN_TYPE_MESH: |
1584 | 0 | return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern, |
1585 | 0 | extents, tx, ty); |
1586 | | |
1587 | 79 | case CAIRO_PATTERN_TYPE_SURFACE: |
1588 | 79 | return _pixman_image_for_surface (dst, |
1589 | 79 | (const cairo_surface_pattern_t *) pattern, |
1590 | 79 | is_mask, extents, sample, |
1591 | 79 | tx, ty); |
1592 | | |
1593 | 0 | case CAIRO_PATTERN_TYPE_RASTER_SOURCE: |
1594 | 0 | return _pixman_image_for_raster (dst, |
1595 | 0 | (const cairo_raster_source_pattern_t *) pattern, |
1596 | 0 | is_mask, extents, sample, |
1597 | 0 | tx, ty); |
1598 | 115 | } |
1599 | 115 | } |
1600 | | |
1601 | | static cairo_status_t |
1602 | | _cairo_image_source_finish (void *abstract_surface) |
1603 | 64 | { |
1604 | 64 | cairo_image_source_t *source = abstract_surface; |
1605 | | |
1606 | 64 | pixman_image_unref (source->pixman_image); |
1607 | 64 | return CAIRO_STATUS_SUCCESS; |
1608 | 64 | } |
1609 | | |
1610 | | const cairo_surface_backend_t _cairo_image_source_backend = { |
1611 | | CAIRO_SURFACE_TYPE_IMAGE, |
1612 | | _cairo_image_source_finish, |
1613 | | NULL, /* read-only wrapper */ |
1614 | | }; |
1615 | | |
1616 | | cairo_surface_t * |
1617 | | _cairo_image_source_create_for_pattern (cairo_surface_t *dst, |
1618 | | const cairo_pattern_t *pattern, |
1619 | | cairo_bool_t is_mask, |
1620 | | const cairo_rectangle_int_t *extents, |
1621 | | const cairo_rectangle_int_t *sample, |
1622 | | int *src_x, int *src_y) |
1623 | 64 | { |
1624 | 64 | cairo_image_source_t *source; |
1625 | | |
1626 | 64 | TRACE ((stderr, "%s\n", __FUNCTION__)); |
1627 | | |
1628 | 64 | source = _cairo_calloc (sizeof (cairo_image_source_t)); |
1629 | 64 | if (unlikely (source == NULL)) |
1630 | 0 | return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
1631 | | |
1632 | 64 | source->pixman_image = |
1633 | 64 | _pixman_image_for_pattern ((cairo_image_surface_t *)dst, |
1634 | 64 | pattern, is_mask, |
1635 | 64 | extents, sample, |
1636 | 64 | src_x, src_y); |
1637 | 64 | if (unlikely (source->pixman_image == NULL)) { |
1638 | 0 | free (source); |
1639 | 0 | return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); |
1640 | 0 | } |
1641 | | |
1642 | 64 | _cairo_surface_init (&source->base, |
1643 | 64 | &_cairo_image_source_backend, |
1644 | 64 | NULL, /* device */ |
1645 | 64 | CAIRO_CONTENT_COLOR_ALPHA, |
1646 | 64 | FALSE); /* is_vector */ |
1647 | | |
1648 | 64 | source->is_opaque_solid = |
1649 | 64 | pattern == NULL || _cairo_pattern_is_opaque_solid (pattern); |
1650 | | |
1651 | 64 | return &source->base; |
1652 | 64 | } |