forked from KolibriOS/kolibrios
754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
1188 lines
32 KiB
C
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;
|
|
}
|