kolibrios/contrib/sdk/sources/cairo/src/cairo-image-source.c
Sergey Semyonov (Serge) 754f9336f0 upload sdk
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
2013-12-15 08:09:20 +00:00

1188 lines
32 KiB
C

/* -*- 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"
#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;
}