Lines
72.62 %
Functions
60.87 %
Branches
57.88 %
/* -*- 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 {
return image;
_pixman_black_image (void)
image = __pixman_black_image;
color.alpha = 0xffff;
if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
_pixman_white_image (void)
image = __pixman_white_image;
color.red = 0xffff;
color.green = 0xffff;
color.blue = 0xffff;
if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_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;
} cache[16];
static int n_cached;
#else /* !PIXMAN_HAS_ATOMIC_OPS */
return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT);
return _pixman_image_for_color (CAIRO_COLOR_BLACK);
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)
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;
color.red = cairo_color->red_short;
color.green = cairo_color->green_short;
color.blue = cairo_color->blue_short;
color.alpha = cairo_color->alpha_short;
if (image == NULL)
if (n_cached < ARRAY_LENGTH (cache)) {
i = n_cached++;
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);
void
_cairo_image_reset_static_data (void)
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;
_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;
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))
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);
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,
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
if (unlikely (pixman_image == 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);
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;
case CAIRO_EXTEND_REFLECT:
pixman_repeat = PIXMAN_REPEAT_REFLECT;
case CAIRO_EXTEND_PAD:
pixman_repeat = PIXMAN_REPEAT_PAD;
pixman_image_set_repeat (pixman_image, pixman_repeat);
return pixman_image;
_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern,
int *tx, int *ty)
int width, height;
*tx = -extents->x;
*ty = -extents->y;
width = extents->width;
height = extents->height;
image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0);
_cairo_mesh_pattern_rasterize (pattern,
pixman_image_get_data (image),
width, height,
pixman_image_get_stride (image),
*tx, *ty);
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);
_defer_free_cleanup (pixman_image_t *pixman_image,
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;
_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
uint32_t pixel;
float *rgba;
switch (image->format) {
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
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)
if (color.alpha == 0xffff)
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)
if (pixel == 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);
case CAIRO_FORMAT_RGB30:
pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
pixel &= 0x3fffffff; /* ignore alpha bits */
if (pixel == 0x3fffffff)
/* convert 10bpc to 16bpc */
color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
color.blue = expand_channel(pixel & 0x3fff, 10);
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
if (pixel == 0xffffffff)
if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
case CAIRO_FORMAT_RGB96F:
case CAIRO_FORMAT_RGBA128F:
if (image->format == CAIRO_FORMAT_RGBA128F)
rgba = (float *)&image->data[y * image->stride + 16 * x];
color.alpha = 65535.f * rgba[3];
else
rgba = (float *)&image->data[y * image->stride + 12 * x];
if (color.alpha == 0xffff && rgba[0] == 0.f && rgba[1] == 0.f && rgba[2] == 0.f)
if (color.alpha == 0xffff && rgba[0] == 1.f && rgba[1] == 1.f && rgba[2] == 1.f)
color.red = rgba[0] * 65535.f;
color.green = rgba[1] * 65535.f;
color.blue = rgba[2] * 65535.f;
/* ========================================================================== */
/* Index into filter table */
typedef enum
KERNEL_IMPULSE,
KERNEL_BOX,
KERNEL_LINEAR,
KERNEL_MITCHELL,
KERNEL_NOTCH,
KERNEL_CATMULL_ROM,
KERNEL_LANCZOS3,
KERNEL_LANCZOS3_STRETCHED,
KERNEL_TENT
} kernel_t;
/* Produce contribution of a filter of size r for pixel centered on x.
For a typical low-pass function this evaluates the function at x/r.
If the frequency is higher than 1/2, such as when r is less than 1,
this may need to integrate several samples, see cubic for examples.
typedef double (* kernel_func_t) (double x, double r);
/* Return maximum number of pixels that will be non-zero. Except for
impluse this is the maximum of 2 and the width of the non-zero part
of the filter rounded up to the next integer.
typedef int (* kernel_width_func_t) (double r);
/* Table of filters */
typedef struct
kernel_t kernel;
kernel_func_t func;
kernel_width_func_t width;
} filter_info_t;
/* PIXMAN_KERNEL_IMPULSE: Returns pixel nearest the center. This
matches PIXMAN_FILTER_NEAREST. This is useful if you wish to
combine the result of nearest in one direction with another filter
in the other.
static double
impulse_kernel (double x, double r)
return 1;
static int
impulse_width (double r)
/* PIXMAN_KERNEL_BOX: Intersection of a box of width r with square
pixels. This is the smallest possible filter such that the output
image contains an equal contribution from all the input
pixels. Lots of software uses this. The function is a trapazoid of
width r+1, not a box.
When r == 1.0, PIXMAN_KERNEL_BOX, PIXMAN_KERNEL_LINEAR, and
PIXMAN_KERNEL_TENT all produce the same filter, allowing
them to be exchanged at this point.
box_kernel (double x, double r)
return MAX (0.0, MIN (MIN (r, 1.0),
MIN ((r + 1) / 2 - x, (r + 1) / 2 + x)));
box_width (double r)
return r < 1.0 ? 2 : ceil(r + 1);
/* PIXMAN_KERNEL_LINEAR: Weighted sum of the two pixels nearest the
center, or a triangle of width 2. This matches
PIXMAN_FILTER_BILINEAR. This is useful if you wish to combine the
result of bilinear in one direction with another filter in the
other. This is not a good filter if r > 1. You may actually want
PIXMAN_FILTER_TENT.
linear_kernel (double x, double r)
return MAX (1.0 - fabs(x), 0.0);
linear_width (double r)
return 2;
/* Cubic functions described in the Mitchell-Netravali paper.
http://mentallandscape.com/Papers_siggraph88.pdf. This describes
all possible cubic functions that can be used for sampling.
general_cubic (double x, double r, double B, double C)
double ax;
if (r < 1.0)
return
general_cubic(x * 2 - .5, r * 2, B, C) +
general_cubic(x * 2 + .5, r * 2, B, C);
ax = fabs (x / r);
if (ax < 1)
return (((12 - 9 * B - 6 * C) * ax +
(-18 + 12 * B + 6 * C)) * ax * ax +
(6 - 2 * B)) / 6;
else if (ax < 2)
return ((((-B - 6 * C) * ax +
(6 * B + 30 * C)) * ax +
(-12 * B - 48 * C)) * ax +
(8 * B + 24 * C)) / 6;
return 0.0;
cubic_width (double r)
return MAX (2, ceil (r * 4));
/* PIXMAN_KERNEL_CATMULL_ROM: Catmull-Rom interpolation. Often called
"cubic interpolation", "b-spline", or just "cubic" by other
software. This filter has negative values so it can produce ringing
and output pixels outside the range of input pixels. This is very
close to lanczos2 so there is no reason to supply that as well.
cubic_kernel (double x, double r)
return general_cubic (x, r, 0.0, 0.5);
/* PIXMAN_KERNEL_MITCHELL: Cubic recommended by the Mitchell-Netravali
paper. This has negative values and because the values at +/-1 are
not zero it does not interpolate the pixels, meaning it will change
an image even if there is no translation.
mitchell_kernel (double x, double r)
return general_cubic (x, r, 1/3.0, 1/3.0);
/* PIXMAN_KERNEL_NOTCH: Cubic recommended by the Mitchell-Netravali
paper to remove postaliasing artifacts. This does not remove
aliasing already present in the source image, though it may appear
to due to it's excessive blurriness. In any case this is more
useful than gaussian for image reconstruction.
notch_kernel (double x, double r)
return general_cubic (x, r, 1.5, -0.25);
/* PIXMAN_KERNEL_LANCZOS3: lanczos windowed sinc function from -3 to
+3. Very popular with high-end software though I think any
advantage over cubics is hidden by quantization and programming
mistakes. You will see LANCZOS5 or even 7 sometimes.
sinc (double x)
return x ? sin (M_PI * x) / (M_PI * x) : 1.0;
lanczos (double x, double n)
return fabs (x) < n ? sinc (x) * sinc (x * (1.0 / n)) : 0.0;
lanczos3_kernel (double x, double r)
lanczos3_kernel (x * 2 - .5, r * 2) +
lanczos3_kernel (x * 2 + .5, r * 2);
return lanczos (x / r, 3.0);
lanczos3_width (double r)
return MAX (2, ceil (r * 6));
/* PIXMAN_KERNEL_LANCZOS3_STRETCHED - The LANCZOS3 kernel widened by
4/3. Recommended by Jim Blinn
http://graphics.cs.cmu.edu/nsp/course/15-462/Fall07/462/papers/jaggy.pdf
nice_kernel (double x, double r)
return lanczos3_kernel (x, r * (4.0/3));
nice_width (double r)
return MAX (2.0, ceil (r * 8));
/* PIXMAN_KERNEL_TENT: Triangle of width 2r. Lots of software uses
this as a "better" filter, twice the size of a box but smaller than
a cubic.
tent_kernel (double x, double r)
return box_kernel(x, r);
return MAX (1.0 - fabs(x / r), 0.0);
tent_width (double r)
return r < 1.0 ? 2 : ceil(2 * r);
static const filter_info_t filters[] =
{ KERNEL_IMPULSE, impulse_kernel, impulse_width },
{ KERNEL_BOX, box_kernel, box_width },
{ KERNEL_LINEAR, linear_kernel, linear_width },
{ KERNEL_MITCHELL, mitchell_kernel, cubic_width },
{ KERNEL_NOTCH, notch_kernel, cubic_width },
{ KERNEL_CATMULL_ROM, cubic_kernel, cubic_width },
{ KERNEL_LANCZOS3, lanczos3_kernel, lanczos3_width },
{ KERNEL_LANCZOS3_STRETCHED,nice_kernel, nice_width },
{ KERNEL_TENT, tent_kernel, tent_width }
/* Fills in one dimension of the filter array */
static void get_filter(kernel_t filter, double r,
int width, int subsample,
pixman_fixed_t* out)
pixman_fixed_t *p = out;
int n_phases = 1 << subsample;
double step = 1.0 / n_phases;
kernel_func_t func = filters[filter].func;
/* special-case the impulse filter: */
if (width <= 1)
for (i = 0; i < n_phases; ++i)
*p++ = pixman_fixed_1;
return;
double frac = (i + .5) * step;
/* Center of left-most pixel: */
double x1 = ceil (frac - width / 2.0 - 0.5) - frac + 0.5;
double total = 0;
pixman_fixed_t new_total = 0;
int j;
for (j = 0; j < width; ++j)
double v = func(x1 + j, r);
total += v;
p[j] = pixman_double_to_fixed (v);
/* Normalize */
total = 1 / total;
new_total += (p[j] *= total);
/* Put any error on center pixel */
p[width / 2] += (pixman_fixed_1 - new_total);
p += width;
/* Create the parameter list for a SEPARABLE_CONVOLUTION filter
* with the given kernels and scale parameters.
static pixman_fixed_t *
create_separable_convolution (int *n_values,
kernel_t xfilter,
double sx,
kernel_t yfilter,
double sy)
int xwidth, xsubsample, ywidth, ysubsample, size_x, size_y;
pixman_fixed_t *params;
xwidth = filters[xfilter].width(sx);
xsubsample = 0;
if (xwidth > 1)
while (sx * (1 << xsubsample) <= 128.0) xsubsample++;
size_x = (1 << xsubsample) * xwidth;
ywidth = filters[yfilter].width(sy);
ysubsample = 0;
if (ywidth > 1)
while (sy * (1 << ysubsample) <= 128.0) ysubsample++;
size_y = (1 << ysubsample) * ywidth;
*n_values = 4 + size_x + size_y;
params = _cairo_malloc (*n_values * sizeof (pixman_fixed_t));
if (!params) return 0;
params[0] = pixman_int_to_fixed (xwidth);
params[1] = pixman_int_to_fixed (ywidth);
params[2] = pixman_int_to_fixed (xsubsample);
params[3] = pixman_int_to_fixed (ysubsample);
get_filter(xfilter, sx, xwidth, xsubsample, params + 4);
get_filter(yfilter, sy, ywidth, ysubsample, params + 4 + size_x);
return params;
static cairo_bool_t
_pixman_image_set_properties (pixman_image_t *pixman_image,
const cairo_pattern_t *pattern,
int *ix,int *iy)
status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix,
pattern->filter,
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;
pixman_filter_t pixman_filter;
double dx, dy;
/* Compute scale factors from 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. The factors are the size of an axis-aligned
* rectangle with the same area as the parallelgram a 1x1
* square transforms to.
dx = hypot (pattern->matrix.xx, pattern->matrix.xy);
dy = hypot (pattern->matrix.yx, pattern->matrix.yy);
/* Clip at maximum pixman_fixed number. Besides making it
* passable to pixman, this avoids errors from inf and nan.
if (! (dx < 0x7FFF)) dx = 0x7FFF;
if (! (dy < 0x7FFF)) dy = 0x7FFF;
switch (pattern->filter) {
case CAIRO_FILTER_FAST:
pixman_filter = PIXMAN_FILTER_FAST;
case CAIRO_FILTER_GOOD:
pixman_filter = PIXMAN_FILTER_SEPARABLE_CONVOLUTION;
kernel = KERNEL_BOX;
/* Clip the filter size to prevent extreme slowness. This
value could be raised if 2-pass filtering is done */
if (dx > 16.0) dx = 16.0;
if (dy > 16.0) dy = 16.0;
/* Match the bilinear filter for scales > .75: */
if (dx < 1.0/0.75) dx = 1.0;
if (dy < 1.0/0.75) dy = 1.0;
case CAIRO_FILTER_BEST:
kernel = KERNEL_CATMULL_ROM; /* LANCZOS3 is better but not much */
if (dx > 16.0) { dx = 16.0; kernel = KERNEL_BOX; }
/* blur up to 2x scale, then blend to square pixels for larger: */
else if (dx < 1.0) {
if (dx < 1.0/128) dx = 1.0/127;
else if (dx < 0.5) dx = 1.0 / (1.0 / dx - 1.0);
else dx = 1.0;
if (dy > 16.0) { dy = 16.0; kernel = KERNEL_BOX; }
else if (dy < 1.0) {
if (dy < 1.0/128) dy = 1.0/127;
else if (dy < 0.5) dy = 1.0 / (1.0 / dy - 1.0);
else dy = 1.0;
case CAIRO_FILTER_NEAREST:
pixman_filter = PIXMAN_FILTER_NEAREST;
case CAIRO_FILTER_BILINEAR:
pixman_filter = PIXMAN_FILTER_BILINEAR;
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. */
pixman_filter = PIXMAN_FILTER_BEST;
if (pixman_filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) {
int n_params;
params = create_separable_convolution
(&n_params, kernel, dx, kernel, dy);
pixman_image_set_filter (pixman_image, pixman_filter,
params, n_params);
free (params);
pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
switch (pattern->extend) {
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);
proxy_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
_cairo_surface_release_source_image (proxy->image, image, image_extra);
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 = _cairo_calloc (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, FALSE);
proxy->image = image;
_cairo_surface_attach_snapshot (source, &proxy->base, NULL);
return &proxy->base;
detach_proxy (cairo_surface_t *source,
cairo_surface_t *proxy)
cairo_surface_finish (proxy);
cairo_surface_destroy (proxy);
get_proxy (cairo_surface_t *proxy)
return ((struct proxy *)proxy)->image;
_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 *sample,
cairo_surface_t *source, *clone, *proxy;
cairo_rectangle_int_t limit;
cairo_rectangle_int_t src_limit;
cairo_status_t status;
cairo_extend_t extend;
cairo_matrix_t *m, matrix;
double sx = 1.0, sy = 1.0;
int tx = 0, ty = 0;
source = _cairo_pattern_get_source (pattern, &limit);
src_limit = 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))
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;
sx = (double)src_limit.width / limit.width;
sy = (double)src_limit.height / limit.height;
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);
if (dst->base.content == source->content)
clone = cairo_image_surface_create (dst->format,
clone = _cairo_image_surface_create_with_content (source->content,
limit.width,
limit.height);
if (dst->base.foreground_source)
clone->foreground_source = cairo_pattern_reference (dst->base.foreground_source);
m = NULL;
if (tx | ty)
cairo_matrix_translate (&matrix, tx, ty);
m = &matrix;
cairo_matrix_init_scale (&matrix, sx, sy);
cairo_matrix_translate (&matrix, src_limit.x/sx, src_limit.y/sy);
/* 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);
if (clone->foreground_used)
dst->base.foreground_used = clone->foreground_used;
detach_proxy (source, proxy);
if (unlikely (status)) {
cairo_surface_destroy (clone);
done:
pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image);
*ix = -limit.x;
*iy = -limit.y;
cairo_pattern_union_t tmp_pattern;
_cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base);
status = cairo_matrix_invert(&matrix);
cairo_matrix_translate (&matrix, src_limit.x, src_limit.y);
cairo_matrix_scale (&matrix, sx, sy);
cairo_pattern_set_matrix (&tmp_pattern.base, &matrix);
if (! _pixman_image_set_properties (pixman_image,
&tmp_pattern.base, extents,
ix, iy)) {
pixman_image= NULL;
_pixman_image_for_surface (cairo_image_surface_t *dst,
cairo_extend_t extend = pattern->base.extend;
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)
if (sample->width == 1 && sample->height == 1) {
if (sample->x < 0 ||
sample->y < 0 ||
sample->x >= source->width ||
sample->y >= source->height)
cairo_surface_destroy (defer_free);
pixman_image = _pixel_to_solid (source,
sample->x, sample->y);
if (pixman_image) {
/* 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))
return pixman_image_ref (source->pixman_image);
pixman_image = pixman_image_create_bits (source->pixman_format,
source->width,
source->height,
(uint32_t *) source->data,
source->stride);
if (unlikely (pixman_image == 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->x + sample->width <= sub->extents.width &&
sample->y + sample->height <= sub->extents.height)
is_contained = TRUE;
if (is_contained) {
sub->extents.x + sample->x,
sub->extents.y + sample->y);
if (pixman_image)
if (extend == CAIRO_EXTEND_NONE)
*ix = sub->extents.x;
*iy = sub->extents.y;
if (is_contained &&
/* Avoid sub-byte offsets, force a copy in that case. */
if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
void *data = source->data
+ sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+ sub->extents.y * source->stride;
sub->extents.width,
sub->extents.height,
data,
/* 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;
void *extra;
status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
if (unlikely (status))
pixman_image = pixman_image_create_bits (image->pixman_format,
image->width,
image->height,
(uint32_t *) image->data,
image->stride);
_cairo_surface_release_source_image (pattern->surface, image, extra);
cleanup = _cairo_malloc (sizeof (*cleanup));
if (unlikely (cleanup == NULL)) {
cleanup->surface = pattern->surface;
cleanup->image = image;
cleanup->image_extra = extra;
_acquire_source_cleanup, cleanup);
&pattern->base, extents,
struct raster_source_cleanup {
const cairo_pattern_t *pattern;
_raster_source_cleanup (pixman_image_t *pixman_image,
struct raster_source_cleanup *data = closure;
_cairo_raster_source_pattern_release (data->pattern,
data->surface);
_pixman_image_for_raster (cairo_image_surface_t *dst,
const cairo_raster_source_pattern_t *pattern,
struct raster_source_cleanup *cleanup;
surface = _cairo_raster_source_pattern_acquire (&pattern->base,
&dst->base, NULL);
if (unlikely (surface == NULL || surface->status))
status = _cairo_surface_acquire_source_image (surface, &image, &extra);
_cairo_raster_source_pattern_release (&pattern->base, surface);
assert (image->width == pattern->extents.width);
assert (image->height == pattern->extents.height);
_cairo_surface_release_source_image (surface, image, extra);
cleanup = _cairo_calloc (sizeof (*cleanup));
cleanup->pattern = &pattern->base;
cleanup->surface = surface;
_raster_source_cleanup, cleanup);
_pixman_image_for_pattern (cairo_image_surface_t *dst,
*tx = *ty = 0;
if (pattern == NULL)
switch (pattern->type) {
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,
case CAIRO_PATTERN_TYPE_SURFACE:
return _pixman_image_for_surface (dst,
(const cairo_surface_pattern_t *) pattern,
tx, ty);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _pixman_image_for_raster (dst,
(const cairo_raster_source_pattern_t *) pattern,
_cairo_image_source_finish (void *abstract_surface)
cairo_image_source_t *source = abstract_surface;
pixman_image_unref (source->pixman_image);
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,
int *src_x, int *src_y)
cairo_image_source_t *source;
source = _cairo_calloc (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);
_cairo_surface_init (&source->base,
&_cairo_image_source_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA,
FALSE); /* is_vector */
source->is_opaque_solid =
pattern == NULL || _cairo_pattern_is_opaque_solid (pattern);
return &source->base;