summaryrefslogtreecommitdiff
path: root/src/cairo-image-source.c
diff options
context:
space:
mode:
authorhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
committerhk57.kim <hk57.kim@samsung.com>2015-06-03 15:16:56 +0900
commit4078c98a5d481778482f52d3aaf7a1777ffe6088 (patch)
treec18ae9d21e2b29c349231c5b61d742e9fca9c1d9 /src/cairo-image-source.c
parentcce6a0e298fac08c588204b085e7a807fa75813d (diff)
downloadcairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.gz
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.bz2
cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.zip
Cairo 1.12.14
Change-Id: Ibc39e63896ec42cab29fbbbf615a46f2d58319a8 Signed-off-by: hk57.kim <hk57.kim@samsung.com>
Diffstat (limited to 'src/cairo-image-source.c')
-rwxr-xr-xsrc/cairo-image-source.c1331
1 files changed, 1331 insertions, 0 deletions
diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
new file mode 100755
index 000000000..e02ebe5e0
--- /dev/null
+++ b/src/cairo-image-source.c
@@ -0,0 +1,1331 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010,2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* The purpose of this file/surface is to simply translate a pattern
+ * to a pixman_image_t and thence to feed it back to the general
+ * compositor interface.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-image-surface-private.h"
+
+#include "cairo-compositor-private.h"
+#include "cairo-error-private.h"
+#include "cairo-pattern-inline.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-observer-private.h"
+#include "cairo-surface-snapshot-inline.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-image-filters-private.h"
+
+#if CAIRO_HAS_TG_SURFACE
+#include "cairo-tg-private.h"
+#endif
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+#if CAIRO_NO_MUTEX
+#define PIXMAN_HAS_ATOMIC_OPS 1
+#endif
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_transparent_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0x00;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_black_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ pixman_image_t *image;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ image = __pixman_white_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0xffff;
+ color.green = 0xffff;
+ color.blue = 0xffff;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_BLACK);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+ return _pixman_image_for_color (CAIRO_COLOR_WHITE);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+
+pixman_image_t *
+_pixman_image_for_color (const cairo_color_t *cairo_color)
+{
+ pixman_color_t color;
+ pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ int i;
+
+ if (CAIRO_COLOR_IS_CLEAR (cairo_color))
+ return _pixman_transparent_image ();
+
+ if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) {
+ if (cairo_color->red_short <= 0x00ff &&
+ cairo_color->green_short <= 0x00ff &&
+ cairo_color->blue_short <= 0x00ff)
+ {
+ return _pixman_black_image ();
+ }
+
+ if (cairo_color->red_short >= 0xff00 &&
+ cairo_color->green_short >= 0xff00 &&
+ cairo_color->blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, cairo_color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
+#endif
+
+ color.red = cairo_color->red_short;
+ color.green = cairo_color->green_short;
+ color.blue = cairo_color->blue_short;
+ color.alpha = cairo_color->alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+ if (image == NULL)
+ goto UNLOCK;
+
+ if (n_cached < ARRAY_LENGTH (cache)) {
+ i = n_cached++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+ pixman_image_unref (cache[i].image);
+ }
+ cache[i].image = pixman_image_ref (image);
+ cache[i].color = *cairo_color;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+ return image;
+}
+
+
+void
+_cairo_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+ while (n_cached)
+ pixman_image_unref (cache[--n_cached].image);
+
+ if (__pixman_transparent_image) {
+ pixman_image_unref (__pixman_transparent_image);
+ __pixman_transparent_image = NULL;
+ }
+
+ if (__pixman_black_image) {
+ pixman_image_unref (__pixman_black_image);
+ __pixman_black_image = NULL;
+ }
+
+ if (__pixman_white_image) {
+ pixman_image_unref (__pixman_white_image);
+ __pixman_white_image = NULL;
+ }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ pixman_gradient_stop_t pixman_stops_static[2];
+ pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+ pixman_transform_t pixman_transform;
+ cairo_matrix_t matrix;
+ cairo_circle_double_t extremes[2];
+ pixman_point_fixed_t p1, p2;
+ unsigned int i;
+ cairo_int_status_t status;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof(pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return NULL;
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ }
+
+ _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
+
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ } else {
+ pixman_fixed_t r1, r2;
+
+ r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
+
+ pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+
+ *ix = *iy = 0;
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
+ ! pixman_image_set_transform (pixman_image, &pixman_transform))
+ {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (pattern->base.extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *tx, int *ty)
+{
+ pixman_image_t *image;
+ int width, height;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *tx = -extents->x;
+ *ty = -extents->y;
+ width = extents->width;
+ height = extents->height;
+
+ image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ _cairo_mesh_pattern_rasterize (pattern,
+ pixman_image_get_data (image),
+ width, height,
+ pixman_image_get_stride (image),
+ *tx, *ty);
+ return image;
+}
+
+struct acquire_source_cleanup {
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct acquire_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+ free (data);
+}
+
+static void
+_defer_free_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ cairo_surface_destroy (closure);
+}
+
+typedef struct _cairo_image_buffer
+{
+ cairo_format_t format;
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+} cairo_image_buffer_t;
+
+static inline void
+_get_image_buffer (cairo_surface_t *surface, cairo_image_buffer_t *image_buffer)
+{
+ if (surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE)
+ {
+ cairo_image_surface_t *image = (cairo_image_surface_t *)surface;
+
+ image_buffer->format = image->format;
+ image_buffer->data = image->data;
+ image_buffer->width = image->width;
+ image_buffer->height = image->height;
+ image_buffer->stride = image->stride;
+ image_buffer->pixman_image = image->pixman_image;
+ image_buffer->pixman_format = image->pixman_format;
+ }
+#if CAIRO_HAS_TG_SURFACE
+ else if (surface->backend->type == CAIRO_SURFACE_TYPE_TG)
+ {
+ cairo_tg_surface_t *tg = (cairo_tg_surface_t *)surface;
+
+ image_buffer->format = tg->format;
+ image_buffer->data = tg->data;
+ image_buffer->width = tg->width;
+ image_buffer->height = tg->height;
+ image_buffer->stride = tg->stride;
+ image_buffer->pixman_image = ((cairo_image_surface_t *)(tg->image_surface))->pixman_image;
+ image_buffer->pixman_format = ((cairo_image_surface_t *)(tg->image_surface))->pixman_format;
+
+ /* flush the journal to make the memory image_buffer up-to-date. */
+ cairo_surface_flush (surface);
+ }
+#endif
+}
+
+static uint16_t
+expand_channel (uint16_t v, uint32_t bits)
+{
+ int offset = 16 - bits;
+ while (offset > 0) {
+ v |= v >> bits;
+ offset -= bits;
+ bits += bits;
+ }
+ return v;
+}
+
+static pixman_image_t *
+_pixel_to_solid (const cairo_image_buffer_t *image_buffer, int x, int y)
+{
+ uint32_t pixel;
+ pixman_color_t color;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ switch (image_buffer->format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return NULL;
+
+ case CAIRO_FORMAT_A1:
+ pixel = *(uint8_t *) (image_buffer->data + y * image_buffer->stride + x/8);
+ return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
+
+ case CAIRO_FORMAT_A8:
+ color.alpha = *(uint8_t *) (image_buffer->data + y * image_buffer->stride + x);
+ color.alpha |= color.alpha << 8;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (color.alpha == 0xffff)
+ return _pixman_black_image ();
+
+ color.red = color.green = color.blue = 0;
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB16_565:
+ pixel = *(uint16_t *) (image_buffer->data + y * image_buffer->stride + 2 * x);
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0xffff)
+ return _pixman_white_image ();
+
+ color.alpha = 0xffff;
+ color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+ color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+ color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB30:
+ pixel = *(uint32_t *) (image_buffer->data + y * image_buffer->stride + 4 * x);
+ pixel &= 0x3fffffff; /* ignore alpha bits */
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0x3fffffff)
+ return _pixman_white_image ();
+
+ /* convert 10bpc to 16bpc */
+ color.alpha = 0xffff;
+ color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
+ color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
+ color.blue = expand_channel(pixel & 0x3fff, 10);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ pixel = *(uint32_t *) (image_buffer->data + y * image_buffer->stride + 4 * x);
+ color.alpha = image_buffer->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (pixel == 0xffffffff)
+ return _pixman_white_image ();
+ if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
+ return _pixman_black_image ();
+
+ color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
+ color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
+ color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
+ return pixman_image_create_solid_fill (&color);
+ }
+}
+
+static cairo_bool_t
+_pixman_image_set_properties (pixman_image_t *pixman_image,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix,int *iy)
+{
+ pixman_transform_t pixman_transform;
+ cairo_int_status_t status;
+
+ status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix,
+ pattern->filter,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.,
+ &pixman_transform, ix, iy);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ {
+ /* If the transform is an identity, we don't need to set it
+ * and we can use any filtering, so choose the fastest one. */
+ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ }
+ else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
+ ! pixman_image_set_transform (pixman_image,
+ &pixman_transform)))
+ {
+ return FALSE;
+ }
+ else
+ {
+ pixman_filter_t pixman_filter;
+
+ switch (pattern->filter) {
+ case CAIRO_FILTER_FAST:
+ pixman_filter = PIXMAN_FILTER_FAST;
+ break;
+ case CAIRO_FILTER_GOOD:
+ pixman_filter = PIXMAN_FILTER_GOOD;
+ break;
+ case CAIRO_FILTER_BEST:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ break;
+ case CAIRO_FILTER_NEAREST:
+ pixman_filter = PIXMAN_FILTER_NEAREST;
+ break;
+ case CAIRO_FILTER_BILINEAR:
+ pixman_filter = PIXMAN_FILTER_BILINEAR;
+ break;
+ case CAIRO_FILTER_GAUSSIAN:
+ /* XXX: The GAUSSIAN value has no implementation in cairo
+ * whatsoever, so it was really a mistake to have it in the
+ * API. We could fix this by officially deprecating it, or
+ * else inventing semantics and providing an actual
+ * implementation for it. */
+ default:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ }
+
+ pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (pattern->extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ if (pattern->has_component_alpha)
+ pixman_image_set_component_alpha (pixman_image, TRUE);
+
+ return TRUE;
+}
+
+struct proxy {
+ cairo_surface_t base;
+ cairo_surface_t *image;
+};
+
+static cairo_status_t
+proxy_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
+}
+
+static void
+proxy_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ struct proxy *proxy = abstract_surface;
+ _cairo_surface_release_source_image (proxy->image, image, image_extra);
+}
+
+static cairo_status_t
+proxy_finish (void *abstract_surface)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t proxy_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+ proxy_finish,
+ NULL,
+
+ NULL, /* create similar */
+ NULL, /* create similar image */
+ NULL, /* map to image */
+ NULL, /* unmap image */
+
+ _cairo_surface_default_source,
+ proxy_acquire_source_image,
+ proxy_release_source_image,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+ cairo_surface_t *image)
+{
+ struct proxy *proxy;
+
+ proxy = malloc (sizeof (*proxy));
+ if (unlikely (proxy == NULL))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
+
+ proxy->image = image;
+ _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+ return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *source,
+ cairo_surface_t *proxy)
+{
+ cairo_surface_finish (proxy);
+ cairo_surface_destroy (proxy);
+}
+
+static cairo_surface_t *
+get_proxy (cairo_surface_t *proxy)
+{
+ return ((struct proxy *)proxy)->image;
+}
+
+static pixman_image_t *
+_pixman_image_for_recording (cairo_image_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ cairo_surface_t *source, *clone, *proxy;
+ cairo_rectangle_int_t limit;
+ pixman_image_t *pixman_image;
+ cairo_status_t status;
+ cairo_extend_t extend;
+ cairo_matrix_t *m, matrix;
+ int tx = 0, ty = 0;
+ cairo_surface_t *blurred_surface;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+
+ source = _cairo_pattern_get_source (pattern, &limit);
+
+ extend = pattern->base.extend;
+ if (_cairo_rectangle_contains_rectangle (&limit, sample))
+ extend = CAIRO_EXTEND_NONE;
+ if (extend == CAIRO_EXTEND_NONE) {
+ if (! _cairo_rectangle_intersect (&limit, sample))
+ return _pixman_transparent_image ();
+
+ if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
+ double x1, y1, x2, y2;
+
+ matrix = pattern->base.matrix;
+ status = cairo_matrix_invert (&matrix);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x1 = limit.x;
+ y1 = limit.y;
+ x2 = limit.x + limit.width;
+ y2 = limit.y + limit.height;
+
+ _cairo_matrix_transform_bounding_box (&matrix,
+ &x1, &y1, &x2, &y2, NULL);
+
+ limit.x = floor (x1);
+ limit.y = floor (y1);
+ limit.width = ceil (x2) - limit.x;
+ limit.height = ceil (y2) - limit.y;
+ }
+ }
+ tx = limit.x;
+ ty = limit.y;
+
+ /* XXX transformations! */
+ proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
+ if (proxy != NULL) {
+ clone = cairo_surface_reference (get_proxy (proxy));
+ goto done;
+ }
+
+ if (is_mask) {
+ clone = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ limit.width, limit.height);
+ } else {
+ if (dst->base.content == source->content)
+ clone = cairo_image_surface_create (dst->format,
+ limit.width, limit.height);
+ else
+ clone = _cairo_image_surface_create_with_content (source->content,
+ limit.width,
+ limit.height);
+ }
+
+ m = NULL;
+ if (extend == CAIRO_EXTEND_NONE) {
+ matrix = pattern->base.matrix;
+ if (tx | ty)
+ cairo_matrix_translate (&matrix, tx, ty);
+ m = &matrix;
+ } else {
+ /* XXX extract scale factor for repeating patterns */
+ }
+
+ /* Handle recursion by returning future reads from the current image */
+ proxy = attach_proxy (source, clone);
+ status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL);
+ detach_proxy (source, proxy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (clone);
+ return NULL;
+ }
+
+done:
+ /* filter with gaussian */
+ blurred_surface = _cairo_image_gaussian_filter (clone, &pattern->base);
+
+ pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ cairo_surface_destroy (clone);
+
+ *ix = -limit.x;
+ *iy = -limit.y;
+ if (extend != CAIRO_EXTEND_NONE) {
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+ }
+
+ return pixman_image;
+}
+
+static inline cairo_bool_t
+_surface_type_is_image_buffer (cairo_surface_type_t type)
+{
+#if CAIRO_HAS_TG_SURFACE
+ return type == CAIRO_SURFACE_TYPE_IMAGE || type == CAIRO_SURFACE_TYPE_TG;
+#else
+ return type == CAIRO_SURFACE_TYPE_IMAGE;
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (cairo_image_surface_t *dst,
+ const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ cairo_extend_t extend = pattern->base.extend;
+ pixman_image_t *pixman_image = NULL;
+ pixman_image_t *blurred_pixman_image = NULL;
+ cairo_surface_t *blurred_surface = NULL;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+ pixman_image = NULL;
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
+ return _pixman_image_for_recording(dst, pattern,
+ is_mask, extents, sample,
+ ix, iy);
+
+ if (_surface_type_is_image_buffer (pattern->surface->type) &&
+ (! is_mask || ! pattern->base.has_component_alpha ||
+ (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+ {
+ cairo_surface_t *defer_free = NULL;
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_image_buffer_t image_buffer;
+
+ if (_cairo_surface_is_snapshot (&source->base)) {
+ defer_free = _cairo_surface_snapshot_get_target (&source->base);
+ source = (cairo_image_surface_t *) defer_free;
+ }
+
+ if (_surface_type_is_image_buffer (source->base.backend->type)) {
+ _get_image_buffer (source, &image_buffer);
+
+ if (extend != CAIRO_EXTEND_NONE &&
+ sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= image_buffer.width &&
+ sample->y + sample->height <= image_buffer.height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (sample->x < 0 ||
+ sample->y < 0 ||
+ sample->x >= image_buffer.width ||
+ sample->y >= image_buffer.height)
+ {
+ if (extend == CAIRO_EXTEND_NONE) {
+ cairo_surface_destroy (defer_free);
+ return _pixman_transparent_image ();
+ }
+ }
+ else
+ {
+ pixman_image = _pixel_to_solid (&image_buffer, sample->x, sample->y);
+ if (pixman_image) {
+ cairo_surface_destroy (defer_free);
+ return pixman_image;
+ }
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ pattern->base.filter,
+ ix, iy))
+ {
+ cairo_surface_destroy (defer_free);
+ /* filter with gaussian */
+ if (pattern->filter == CAIRO_FILTER_GAUSSIAN) {
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ return blurred_pixman_image;
+ }
+ else
+ return pixman_image_ref (image_buffer.pixman_image);
+ }
+#endif
+
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ pixman_image = pixman_image_create_bits (image_buffer.pixman_format,
+ image_buffer.width,
+ image_buffer.height,
+ (uint32_t *) image_buffer.data,
+ image_buffer.stride);
+ if (unlikely (pixman_image == NULL)) {
+ cairo_surface_destroy (defer_free);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (defer_free) {
+ pixman_image_set_destroy_function (pixman_image,
+ _defer_free_cleanup,
+ defer_free);
+ }
+ }
+ else
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ } else if (source->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub;
+ cairo_bool_t is_contained = FALSE;
+
+ sub = (cairo_surface_subsurface_t *) source;
+ source = sub->target;
+
+ _get_image_buffer (source, &image_buffer);
+
+ if (sample->x >= 0 &&
+ sample->y >= 0 &&
+ sample->x + sample->width <= sub->extents.width &&
+ sample->y + sample->height <= sub->extents.height)
+ {
+ is_contained = TRUE;
+ }
+
+ if (sample->width == 1 && sample->height == 1) {
+ if (is_contained) {
+ pixman_image = _pixel_to_solid (&image_buffer,
+ sub->extents.x + sample->x,
+ sub->extents.y + sample->y);
+ if (pixman_image)
+ return pixman_image;
+ } else {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ *ix = sub->extents.x;
+ *iy = sub->extents.y;
+ if (is_contained &&
+ _cairo_matrix_is_pixman_translation (&pattern->base.matrix,
+ pattern->base.filter,
+ ix, iy))
+ {
+ /* filter with gaussian */
+ if (pattern->filter == CAIRO_FILTER_GAUSSIAN) {
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ return blurred_pixman_image;
+ }
+ }
+#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ if (PIXMAN_FORMAT_BPP (image_buffer.pixman_format) >= 8) {
+ if (is_contained) {
+ void *data = image_buffer.data
+ + sub->extents.x * PIXMAN_FORMAT_BPP(image_buffer.pixman_format)/8
+ + sub->extents.y * image_buffer.stride;
+ pixman_image = pixman_image_create_bits (image_buffer.pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ data,
+ image_buffer.stride);
+ if (unlikely (pixman_image == NULL)) {
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+ } else {
+ /* XXX for a simple translation and EXTEND_NONE we can
+ * fix up the pattern matrix instead.
+ */
+ }
+ }
+ }
+ else
+ /* filter */
+ blurred_surface = _cairo_image_gaussian_filter (&source->base, &pattern->base);
+ }
+ }
+
+ if (pixman_image == NULL && blurred_surface == NULL) {
+ struct acquire_source_cleanup *cleanup;
+ cairo_image_surface_t *image;
+ void *extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
+ if (unlikely (status)) {
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN) {
+ pixman_image = pixman_image_create_bits (image->pixman_format,
+ image->width,
+ image->height,
+ (uint32_t *) image->data,
+ image->stride);
+ if (unlikely (pixman_image == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+ }
+ else
+ /* filter with gaussian */
+ blurred_surface = _cairo_image_gaussian_filter (&image->base, &pattern->base);
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+ if (blurred_surface)
+ cairo_surface_destroy (blurred_surface);
+ if (blurred_pixman_image)
+ pixman_image_unref (blurred_pixman_image);
+ return NULL;
+ }
+
+ if (pixman_image) {
+ cleanup->surface = pattern->surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _acquire_source_cleanup, cleanup);
+ }
+ }
+
+ if (blurred_surface) {
+ blurred_pixman_image = pixman_image_ref (((cairo_image_surface_t *)blurred_surface)->pixman_image);
+ cairo_surface_destroy (blurred_surface);
+ }
+
+ if (blurred_pixman_image) {
+ if (! _pixman_image_set_properties (blurred_pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (blurred_pixman_image);
+ blurred_pixman_image= NULL;
+ }
+ }
+ if (pixman_image) {
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+ }
+
+ if (blurred_pixman_image) {
+ if (pixman_image)
+ pixman_image_unref (pixman_image);
+ }
+ else
+ blurred_pixman_image = pixman_image;
+
+ return blurred_pixman_image;
+}
+
+struct raster_source_cleanup {
+ const cairo_pattern_t *pattern;
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_raster_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct raster_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+
+ _cairo_raster_source_pattern_release (data->pattern,
+ data->surface);
+
+ free (data);
+}
+
+static pixman_image_t *
+_pixman_image_for_raster (cairo_image_surface_t *dst,
+ const cairo_raster_source_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ struct raster_source_cleanup *cleanup;
+ cairo_image_surface_t *image;
+ void *extra;
+ cairo_status_t status;
+ cairo_surface_t *surface;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ *ix = *iy = 0;
+
+ surface = _cairo_raster_source_pattern_acquire (&pattern->base,
+ &dst->base, NULL);
+ if (unlikely (surface == NULL || surface->status))
+ return NULL;
+
+ status = _cairo_surface_acquire_source_image (surface, &image, &extra);
+ if (unlikely (status)) {
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ assert (image->width == pattern->extents.width);
+ assert (image->height == pattern->extents.height);
+
+ pixman_image = pixman_image_create_bits (image->pixman_format,
+ image->width,
+ image->height,
+ (uint32_t *) image->data,
+ image->stride);
+ if (unlikely (pixman_image == NULL)) {
+ _cairo_surface_release_source_image (surface, image, extra);
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ pixman_image_unref (pixman_image);
+ _cairo_surface_release_source_image (surface, image, extra);
+ _cairo_raster_source_pattern_release (&pattern->base, surface);
+ return NULL;
+ }
+
+ cleanup->pattern = &pattern->base;
+ cleanup->surface = surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _raster_source_cleanup, cleanup);
+
+ if (! _pixman_image_set_properties (pixman_image,
+ &pattern->base, extents,
+ ix, iy)) {
+ pixman_image_unref (pixman_image);
+ pixman_image= NULL;
+ }
+
+ return pixman_image;
+}
+
+pixman_image_t *
+_pixman_image_for_pattern (cairo_image_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *tx, int *ty)
+{
+ *tx = *ty = 0;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ if (pattern == NULL)
+ return _pixman_white_image ();
+
+ switch (pattern->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _pixman_image_for_surface (dst,
+ (const cairo_surface_pattern_t *) pattern,
+ is_mask, extents, sample,
+ tx, ty);
+
+ case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
+ return _pixman_image_for_raster (dst,
+ (const cairo_raster_source_pattern_t *) pattern,
+ is_mask, extents, sample,
+ tx, ty);
+ }
+}
+
+static cairo_status_t
+_cairo_image_source_finish (void *abstract_surface)
+{
+ cairo_image_source_t *source = abstract_surface;
+
+ pixman_image_unref (source->pixman_image);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+const cairo_surface_backend_t _cairo_image_source_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_source_finish,
+ NULL, /* read-only wrapper */
+};
+
+cairo_surface_t *
+_cairo_image_source_create_for_pattern (cairo_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ const cairo_rectangle_int_t *sample,
+ int *src_x, int *src_y)
+{
+ cairo_image_source_t *source;
+
+ TRACE ((stderr, "%s\n", __FUNCTION__));
+
+ source = malloc (sizeof (cairo_image_source_t));
+ if (unlikely (source == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ source->pixman_image =
+ _pixman_image_for_pattern ((cairo_image_surface_t *)dst,
+ pattern, is_mask,
+ extents, sample,
+ src_x, src_y);
+ if (unlikely (source->pixman_image == NULL)) {
+ free (source);
+ return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_surface_init (&source->base,
+ &_cairo_image_source_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ source->is_opaque_solid =
+ pattern == NULL || _cairo_pattern_is_opaque_solid (pattern);
+
+ return &source->base;
+}