mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-18 14:08:09 +02:00
I had a look at how complex would it be to add correct downscaling to Cairo now that Pixman supports convolution filters. It turns out it this is rather easy. Here is an initial, minimal attempt. It uses convolution filters only if the image is being downscaled by more than half a pixel in at least one dimension. Some discussion: 1. The sampling and reconstruction kernels are picked in a way that gives comparable quality when upscaling and downscaling. I paired box sampling with bilinear reconstruction and impulse (point) sampling with box reconstruction. This gives the expected result for NEAREST filter. BEST filter uses Lanczos3 for both kernels. > Do we need to use a reconstruction filter for NEAREST at all? Or maybe > differentiate between NEAREST and FAST in that case? If impulse (point) sampling is used, there must be some reconstruction filter, otherwise no image is produced. That's because the sampling grid does not match the data grid, and since there is no reconstruction filter, values between data points are undefined. The alternative is to use box sampling + no reconstruction. 2. Subsampling bits are always set to 1, since this doesn't seem to affect quality at all. 3. I am not sure whether this code works correctly for matrices with a skew component. It should be OK for any combination of scale, rotation and translation. 4. This patch causes new failures in the test suite: - recording-surface*: possibly an effect of improved quality. - surface-pattern-scale-down*, surface-pattern-big-scale-down: the reference images should be updated. - pthread-same-source: I have no idea why this is failing, since this test shouldn't even trigger the new code. - large-source-roi: this test attempts to downscale an image which is 30000 pixels wide down to 7 pixels. The filter parameters seem to be created correctly, but they might trigger an overflow somewhere in the convolution code; the output rectangle is white instead of red, as if nothing was drawn. - device-offset-scale: there are subtle differences which look like convolution-related smoothing; I'm not sure whether this is OK or not.
1232 lines
34 KiB
C
1232 lines
34 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
|
|
{
|
|
double scale_x, scale_y;
|
|
int shrink_x, shrink_y;
|
|
pixman_filter_t pixman_filter;
|
|
pixman_kernel_t pixman_kernel_sample, pixman_kernel_reconstruct;
|
|
|
|
/* Compute scale factors as the length of basis vectors transformed by
|
|
* the pattern matrix. These scale factors are from user to pattern space,
|
|
* and as such they are greater than 1.0 for downscaling and less than 1.0
|
|
* for upscaling.
|
|
* TODO: this approach may not be completely correct if the matrix
|
|
* contains a skew component. */
|
|
scale_x = hypot (pattern->matrix.xx, pattern->matrix.yx);
|
|
scale_y = hypot (pattern->matrix.yx, pattern->matrix.yy);
|
|
|
|
/* Use convolution filtering if the transformation shrinks the image
|
|
* by more than half a pixel */
|
|
shrink_x = (extents->width / scale_x - extents->width) < -0.5;
|
|
shrink_y = (extents->height / scale_y - extents->height) < -0.5;
|
|
|
|
switch (pattern->filter) {
|
|
case CAIRO_FILTER_FAST:
|
|
case CAIRO_FILTER_NEAREST:
|
|
pixman_filter = PIXMAN_FILTER_NEAREST;
|
|
pixman_kernel_sample = PIXMAN_KERNEL_IMPULSE;
|
|
pixman_kernel_reconstruct = PIXMAN_KERNEL_BOX;
|
|
break;
|
|
case CAIRO_FILTER_GOOD:
|
|
case CAIRO_FILTER_BILINEAR:
|
|
pixman_filter = PIXMAN_FILTER_BILINEAR;
|
|
pixman_kernel_sample = PIXMAN_KERNEL_BOX;
|
|
pixman_kernel_reconstruct = PIXMAN_KERNEL_LINEAR;
|
|
break;
|
|
case CAIRO_FILTER_BEST:
|
|
pixman_filter = PIXMAN_FILTER_BEST;
|
|
pixman_kernel_sample = PIXMAN_KERNEL_LANCZOS3;
|
|
pixman_kernel_reconstruct = PIXMAN_KERNEL_LANCZOS3;
|
|
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_BILINEAR;
|
|
pixman_kernel_sample = PIXMAN_KERNEL_BOX;
|
|
pixman_kernel_reconstruct = PIXMAN_KERNEL_LINEAR;
|
|
}
|
|
|
|
if (pixman_filter != PIXMAN_FILTER_NEAREST && (shrink_x || shrink_y)) {
|
|
pixman_kernel_t sampling_kernel_x, sampling_kernel_y;
|
|
int n_params;
|
|
pixman_fixed_t *params;
|
|
|
|
sampling_kernel_x = shrink_x ? pixman_kernel_sample : PIXMAN_KERNEL_IMPULSE;
|
|
sampling_kernel_y = shrink_y ? pixman_kernel_sample : PIXMAN_KERNEL_IMPULSE;
|
|
|
|
n_params = 0;
|
|
params = pixman_filter_create_separable_convolution (&n_params,
|
|
scale_x * 65536.0 + 0.5,
|
|
scale_y * 65536.0 + 0.5,
|
|
pixman_kernel_reconstruct,
|
|
pixman_kernel_reconstruct,
|
|
sampling_kernel_x,
|
|
sampling_kernel_y,
|
|
1, 1);
|
|
|
|
pixman_image_set_filter (pixman_image,
|
|
PIXMAN_FILTER_SEPARABLE_CONVOLUTION,
|
|
params, n_params);
|
|
|
|
free (params);
|
|
} else {
|
|
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;
|
|
}
|