/* -*- 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 * Chris Wilson */ /* 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" #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); } 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 (cairo_image_surface_t *image, int x, int y) { uint32_t pixel; pixman_color_t color; TRACE ((stderr, "%s\n", __FUNCTION__)); switch (image->format) { default: case CAIRO_FORMAT_INVALID: ASSERT_NOT_REACHED; return NULL; case CAIRO_FORMAT_A1: pixel = *(uint8_t *) (image->data + y * image->stride + x/8); return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image (); case CAIRO_FORMAT_A8: color.alpha = *(uint8_t *) (image->data + y * image->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->data + y * image->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->data + y * image->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->data + y * image->stride + 4 * x); color.alpha = image->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; 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: pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); 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 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; 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 (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE && (! 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_surface_type_t type; if (_cairo_surface_is_snapshot (&source->base)) { defer_free = _cairo_surface_snapshot_get_target (&source->base); source = (cairo_image_surface_t *) defer_free; } type = source->base.backend->type; if (type == CAIRO_SURFACE_TYPE_IMAGE) { if (extend != CAIRO_EXTEND_NONE && sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= source->width && sample->y + sample->height <= source->height) { extend = CAIRO_EXTEND_NONE; } if (sample->width == 1 && sample->height == 1) { if (sample->x < 0 || sample->y < 0 || sample->x >= source->width || sample->y >= source->height) { if (extend == CAIRO_EXTEND_NONE) { cairo_surface_destroy (defer_free); return _pixman_transparent_image (); } } else { pixman_image = _pixel_to_solid (source, 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); return pixman_image_ref (source->pixman_image); } #endif pixman_image = pixman_image_create_bits (source->pixman_format, source->width, source->height, (uint32_t *) source->data, source->stride); if (unlikely (pixman_image == NULL)) { cairo_surface_destroy (defer_free); return NULL; } if (defer_free) { pixman_image_set_destroy_function (pixman_image, _defer_free_cleanup, defer_free); } } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub; cairo_bool_t is_contained = FALSE; sub = (cairo_surface_subsurface_t *) source; source = (cairo_image_surface_t *) sub->target; 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 (source, 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)) { return pixman_image_ref (source->pixman_image); } #endif /* Avoid sub-byte offsets, force a copy in that case. */ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) { if (is_contained) { void *data = source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride; pixman_image = pixman_image_create_bits (source->pixman_format, sub->extents.width, sub->extents.height, data, source->stride); if (unlikely (pixman_image == NULL)) return NULL; } else { /* XXX for a simple translation and EXTEND_NONE we can * fix up the pattern matrix instead. */ } } } } if (pixman_image == 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)) return NULL; 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); return NULL; } cleanup = malloc (sizeof (*cleanup)); if (unlikely (cleanup == NULL)) { _cairo_surface_release_source_image (pattern->surface, image, extra); pixman_image_unref (pixman_image); return NULL; } cleanup->surface = pattern->surface; cleanup->image = image; cleanup->image_extra = extra; pixman_image_set_destroy_function (pixman_image, _acquire_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; } 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; }