summaryrefslogtreecommitdiff
path: root/src/cairo-image-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-image-surface.c')
-rw-r--r--src/cairo-image-surface.c3893
1 files changed, 3285 insertions, 608 deletions
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index b50a9941c..f3886c068 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -2,6 +2,7 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
+ * Copyright © 2009,2010 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
@@ -33,19 +34,35 @@
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairoint.h"
+#include "cairo-boxes-private.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
/* Limit on the width / height of an image surface in pixels. This is
* mainly determined by coordinates of things sent to pixman at the
* moment being in 16.16 format. */
#define MAX_IMAGE_SIZE 32767
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+static cairo_int_status_t
+_cairo_image_surface_fill (void *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
static cairo_bool_t
_cairo_image_surface_is_size_valid (int width, int height)
@@ -54,7 +71,7 @@ _cairo_image_surface_is_size_valid (int width, int height)
0 <= height && height <= MAX_IMAGE_SIZE;
}
-static cairo_format_t
+cairo_format_t
_cairo_format_from_pixman_format (pixman_format_code_t pixman_format)
{
switch (pixman_format) {
@@ -96,69 +113,18 @@ _cairo_format_from_pixman_format (pixman_format_code_t pixman_format)
return CAIRO_FORMAT_INVALID;
}
-static cairo_content_t
+cairo_content_t
_cairo_content_from_pixman_format (pixman_format_code_t pixman_format)
{
- switch (pixman_format) {
- case PIXMAN_a8r8g8b8:
- case PIXMAN_a8b8g8r8:
- case PIXMAN_a1r5g5b5:
- case PIXMAN_a1b5g5r5:
- case PIXMAN_a4r4g4b4:
- case PIXMAN_a4b4g4r4:
- case PIXMAN_a2r2g2b2:
- case PIXMAN_a2b2g2r2:
- case PIXMAN_a1r1g1b1:
- case PIXMAN_a1b1g1r1:
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9)
- case PIXMAN_a2b10g10r10:
-#endif
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1)
- case PIXMAN_b8g8r8a8:
-#endif
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16)
- case PIXMAN_a2r10g10b10:
-#endif
- return CAIRO_CONTENT_COLOR_ALPHA;
- case PIXMAN_x8r8g8b8:
- case PIXMAN_x8b8g8r8:
- case PIXMAN_r8g8b8:
- case PIXMAN_b8g8r8:
- case PIXMAN_r5g6b5:
- case PIXMAN_b5g6r5:
- case PIXMAN_x1r5g5b5:
- case PIXMAN_x1b5g5r5:
- case PIXMAN_x4r4g4b4:
- case PIXMAN_x4b4g4r4:
- case PIXMAN_r3g3b2:
- case PIXMAN_b2g3r3:
- case PIXMAN_c8:
- case PIXMAN_g8:
- case PIXMAN_r1g2b1:
- case PIXMAN_b1g2r1:
- case PIXMAN_c4:
- case PIXMAN_g4:
- case PIXMAN_g1:
- case PIXMAN_yuy2:
- case PIXMAN_yv12:
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,11,9)
- case PIXMAN_x2b10g10r10:
-#endif
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,14,1)
- case PIXMAN_b8g8r8x8:
-#endif
-#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,15,16)
- case PIXMAN_x2r10g10b10:
-#endif
- return CAIRO_CONTENT_COLOR;
- case PIXMAN_a8:
- case PIXMAN_a1:
- case PIXMAN_x4a4:
- case PIXMAN_a4:
- return CAIRO_CONTENT_ALPHA;
- }
+ cairo_content_t content;
- return CAIRO_CONTENT_COLOR_ALPHA;
+ content = 0;
+ if (PIXMAN_FORMAT_RGB (pixman_format))
+ content |= CAIRO_CONTENT_COLOR;
+ if (PIXMAN_FORMAT_A (pixman_format))
+ content |= CAIRO_CONTENT_ALPHA;
+
+ return content;
}
cairo_surface_t *
@@ -169,10 +135,6 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
int width = pixman_image_get_width (pixman_image);
int height = pixman_image_get_height (pixman_image);
- if (! _cairo_image_surface_is_size_valid (width, height)) {
- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
- }
-
surface = malloc (sizeof (cairo_image_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
@@ -186,7 +148,7 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
surface->pixman_format = pixman_format;
surface->format = _cairo_format_from_pixman_format (pixman_format);
- surface->data = (unsigned char *) pixman_image_get_data (pixman_image);
+ surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
surface->owns_data = FALSE;
surface->transparency = CAIRO_IMAGE_UNKNOWN;
@@ -195,8 +157,6 @@ _cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
surface->stride = pixman_image_get_stride (pixman_image);
surface->depth = pixman_image_get_depth (pixman_image);
- surface->clip_region = NULL;
-
return &surface->base;
}
@@ -305,46 +265,6 @@ _pixman_format_to_masks (pixman_format_code_t format,
}
}
-/* XXX: This function really should be eliminated. We don't really
- * want to advertise a cairo image surface that supports any possible
- * format. A minimal step would be to replace this function with one
- * that accepts a #cairo_internal_format_t rather than mask values. */
-cairo_surface_t *
-_cairo_image_surface_create_with_masks (unsigned char *data,
- cairo_format_masks_t *masks,
- int width,
- int height,
- int stride)
-{
- pixman_format_code_t pixman_format;
-
- if (! _pixman_format_from_masks (masks, &pixman_format)) {
- fprintf (stderr,
- "Error: Cairo %s does not yet support the requested image format:\n"
- "\tDepth: %d\n"
- "\tAlpha mask: 0x%08lx\n"
- "\tRed mask: 0x%08lx\n"
- "\tGreen mask: 0x%08lx\n"
- "\tBlue mask: 0x%08lx\n"
-#ifdef PACKAGE_BUGGREPORT
- "Please file an enhancement request (quoting the above) at:\n"
- PACKAGE_BUGREPORT"\n"
-#endif
- ,
- cairo_version_string (),
- masks->bpp, masks->alpha_mask,
- masks->red_mask, masks->green_mask, masks->blue_mask);
-
- ASSERT_NOT_REACHED;
- }
-
- return _cairo_image_surface_create_with_pixman_format (data,
- pixman_format,
- width,
- height,
- stride);
-}
-
static pixman_format_code_t
_cairo_format_to_pixman_format_code (cairo_format_t format)
{
@@ -442,9 +362,6 @@ _cairo_image_surface_create_with_content (cairo_content_t content,
int width,
int height)
{
- if (! CAIRO_CONTENT_VALID (content))
- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
-
return cairo_image_surface_create (_cairo_format_from_content (content),
width, height);
}
@@ -554,6 +471,9 @@ cairo_image_surface_create_for_data (unsigned char *data,
if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
+ if (! _cairo_image_surface_is_size_valid (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
minstride = cairo_format_stride_for_width (format, width);
if (stride < 0) {
if (stride > -minstride) {
@@ -573,21 +493,6 @@ cairo_image_surface_create_for_data (unsigned char *data,
}
slim_hidden_def (cairo_image_surface_create_for_data);
-cairo_surface_t *
-_cairo_image_surface_create_for_data_with_content (unsigned char *data,
- cairo_content_t content,
- int width,
- int height,
- int stride)
-{
- if (! CAIRO_CONTENT_VALID (content))
- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
-
- return cairo_image_surface_create_for_data (data,
- _cairo_format_from_content (content),
- width, height, stride);
-}
-
/**
* cairo_image_surface_get_data:
* @surface: a #cairo_image_surface_t
@@ -770,6 +675,9 @@ _cairo_image_surface_create_similar (void *abstract_other,
{
cairo_image_surface_t *other = abstract_other;
+ if (! _cairo_image_surface_is_size_valid (width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
if (content == other->base.content) {
return _cairo_image_surface_create_with_pixman_format (NULL,
other->pixman_format,
@@ -796,8 +704,6 @@ _cairo_image_surface_finish (void *abstract_surface)
surface->data = NULL;
}
- cairo_region_destroy (surface->clip_region);
-
return CAIRO_STATUS_SUCCESS;
}
@@ -825,176 +731,6 @@ _cairo_image_surface_release_source_image (void *abstract_surf
{
}
-static cairo_status_t
-_cairo_image_surface_acquire_dest_image (void *abstract_surface,
- cairo_rectangle_int_t *interest_rect,
- cairo_image_surface_t **image_out,
- cairo_rectangle_int_t *image_rect_out,
- void **image_extra)
-{
- cairo_image_surface_t *surface = abstract_surface;
-
- image_rect_out->x = 0;
- image_rect_out->y = 0;
- image_rect_out->width = surface->width;
- image_rect_out->height = surface->height;
-
- *image_out = surface;
- *image_extra = NULL;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static void
-_cairo_image_surface_release_dest_image (void *abstract_surface,
- cairo_rectangle_int_t *interest_rect,
- cairo_image_surface_t *image,
- cairo_rectangle_int_t *image_rect,
- void *image_extra)
-{
-}
-
-static cairo_status_t
-_cairo_image_surface_clone_similar (void *abstract_surface,
- cairo_surface_t *src,
- int src_x,
- int src_y,
- int width,
- int height,
- int *clone_offset_x,
- int *clone_offset_y,
- cairo_surface_t **clone_out)
-{
- cairo_image_surface_t *surface = abstract_surface;
-
- if (src->backend == surface->base.backend) {
- *clone_offset_x = *clone_offset_y = 0;
- *clone_out = cairo_surface_reference (src);
-
- return CAIRO_STATUS_SUCCESS;
- }
-
- return CAIRO_INT_STATUS_UNSUPPORTED;
-}
-
-static cairo_status_t
-_cairo_image_surface_set_matrix (cairo_image_surface_t *surface,
- const cairo_matrix_t *matrix,
- double xc, double yc)
-{
- pixman_transform_t pixman_transform;
-
- _cairo_matrix_to_pixman_matrix (matrix, &pixman_transform, xc, yc);
-
- if (! pixman_image_set_transform (surface->pixman_image, &pixman_transform))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_image_surface_set_filter (cairo_image_surface_t *surface,
- cairo_filter_t filter)
-{
- pixman_filter_t pixman_filter;
-
- switch (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;
- }
-
- if (! pixman_image_set_filter (surface->pixman_image,
- pixman_filter,
- NULL, 0))
- {
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_image_surface_set_extend (cairo_image_surface_t *surface,
- cairo_extend_t extend)
-{
- pixman_repeat_t pixman_repeat;
-
- switch (extend) {
- 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 (surface->pixman_image, pixman_repeat);
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_image_surface_set_component_alpha (cairo_image_surface_t *surface,
- cairo_bool_t ca)
-{
- pixman_image_set_component_alpha (surface->pixman_image, ca);
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_image_surface_set_attributes (cairo_image_surface_t *surface,
- cairo_surface_attributes_t *attributes,
- double xc, double yc)
-{
- cairo_int_status_t status;
-
- status = _cairo_image_surface_set_matrix (surface, &attributes->matrix,
- xc, yc);
- if (unlikely (status))
- return status;
-
- status = _cairo_image_surface_set_filter (surface, attributes->filter);
- if (unlikely (status))
- return status;
-
- status = _cairo_image_surface_set_extend (surface, attributes->extend);
- if (unlikely (status))
- return status;
-
- status = _cairo_image_surface_set_component_alpha (surface,
- attributes->has_component_alpha);
- if (unlikely (status))
- return status;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
/* XXX: I think we should fix pixman to match the names/order of the
* cairo operators, but that will likely be better done at the same
* time the X server is ported to pixman, (which will change a lot of
@@ -1077,25 +813,3100 @@ static cairo_status_t
_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface,
cairo_region_t *region)
{
- if (region == surface->clip_region)
+ if (! pixman_image_set_clip_region32 (surface->pixman_image, &region->rgn))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface)
+{
+ pixman_image_set_clip_region32 (surface->pixman_image, NULL);
+}
+
+static double
+_pixman_nearest_sample (double d)
+{
+ return ceil (d - .5);
+}
+
+static cairo_bool_t
+_nearest_sample (cairo_filter_t filter, double *tx, double *ty)
+{
+ if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
+ *tx = _pixman_nearest_sample (*tx);
+ *ty = _pixman_nearest_sample (*ty);
+ } else {
+ if (*tx != floor (*tx) || *ty != floor (*ty))
+ return FALSE;
+ }
+ return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT;
+}
+
+#if HAS_ATOMIC_OPS
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ static pixman_image_t *__pixman_transparent_image;
+ pixman_image_t *image;
+
+ 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 (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+ NULL, image) == NULL)
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ static pixman_image_t *__pixman_black_image;
+ pixman_image_t *image;
+
+ 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 (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+ NULL, image) == NULL)
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ static pixman_image_t *__pixman_white_image;
+ pixman_image_t *image;
+
+ 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 (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+ NULL, image) == NULL)
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+#else
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_white);
+}
+#endif
+
+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 pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
+{
+ static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+ } cache[16];
+ static int n_cached;
+ pixman_color_t color;
+ pixman_image_t *image;
+ int i;
+
+#if HAS_ATOMIC_OPS
+ if (pattern->color.alpha_short <= 0x00ff)
+ return _pixman_transparent_image ();
+
+ if (pattern->color.alpha_short >= 0xff00) {
+ if (pattern->color.red_short <= 0x00ff &&
+ pattern->color.green_short <= 0x00ff &&
+ pattern->color.blue_short <= 0x00ff)
+ {
+ return _pixman_black_image ();
+ }
+
+ if (pattern->color.red_short >= 0xff00 &&
+ pattern->color.green_short >= 0xff00 &&
+ pattern->color.blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+#endif
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
+
+ color.red = pattern->color.red_short;
+ color.green = pattern->color.green_short;
+ color.blue = pattern->color.blue_short;
+ color.alpha = pattern->color.alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
+ 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 = pattern->color;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+ return image;
+}
+
+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;
+ cairo_matrix_t matrix = pattern->base.matrix;
+ double tx, ty;
+ unsigned int i;
+
+ 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;
+ }
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
+ pixman_point_fixed_t p1, p2;
+ cairo_fixed_t xdim, ydim;
+
+ xdim = fabs (linear->p2.x - linear->p1.x);
+ ydim = fabs (linear->p2.y - linear->p1.y);
+
+ /*
+ * Transform the matrix to avoid overflow when converting between
+ * cairo_fixed_t and pixman_fixed_t (without incurring performance
+ * loss when the transformation is unnecessary).
+ *
+ * XXX: Consider converting out-of-range co-ordinates and transforms.
+ * Having a function to compute the required transformation to
+ * "normalize" a given bounding box would be generally useful -
+ * cf linear patterns, gradient patterns, surface patterns...
+ */
+ if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
+ _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
+ {
+ double sf;
+
+ if (xdim > ydim)
+ sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
+ else
+ sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
+
+ p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
+ p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
+ p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
+ p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
+
+ cairo_matrix_scale (&matrix, sf, sf);
+ }
+ else
+ {
+ p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
+ p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
+ p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
+ p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
+ }
+
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+ pixman_point_fixed_t c1, c2;
+ pixman_fixed_t r1, r2;
+
+ c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
+ c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
+ r1 = _cairo_fixed_to_16_16 (radial->r1);
+
+ c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
+ c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
+ r2 = _cairo_fixed_to_16_16 (radial->r2);
+
+ pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ tx = pattern->base.matrix.x0;
+ ty = pattern->base.matrix.y0;
+ if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
+ ! _nearest_sample (pattern->base.filter, &tx, &ty))
+ {
+ pixman_transform_t pixman_transform;
+
+ if (tx != 0. || ty != 0.) {
+ cairo_matrix_t m, inv;
+ cairo_status_t status;
+ double x, y;
+
+ /* pixman also limits the [xy]_offset to 16 bits so evenly
+ * spread the bits between the two.
+ */
+ inv = pattern->base.matrix;
+ status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x = floor (inv.x0 / 2);
+ y = floor (inv.y0 / 2);
+ tx = -x;
+ ty = -y;
+ cairo_matrix_init_translate (&inv, x, y);
+ cairo_matrix_multiply (&m, &inv, &pattern->base.matrix);
+ _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ } else {
+ tx = ty = 0;
+ _cairo_matrix_to_pixman_matrix (&pattern->base.matrix,
+ &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ }
+
+ if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+ *ix = tx;
+ *iy = ty;
+
+ {
+ 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;
+}
+
+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 pixman_image_t *
+_pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ cairo_extend_t extend;
+ double tx, ty;
+
+ tx = pattern->base.matrix.x0;
+ ty = pattern->base.matrix.y0;
+
+ extend = pattern->base.extend;
+
+ pixman_image = NULL;
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_surface_type_t type;
+
+ if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+ type = source->base.backend->type;
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+ if (extend != CAIRO_EXTEND_NONE &&
+ extents->x >= 0 && extents->y >= 0 &&
+ extents->x + extents->width <= source->width &&
+ extents->y + extents->height <= source->height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (pattern->base.filter, &tx, &ty))
+ {
+ *ix = tx;
+ *iy = ty;
+ 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))
+ return NULL;
+ } else if (type == CAIRO_INTERNAL_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 (extend != CAIRO_EXTEND_NONE &&
+ extents->x >= 0 && extents->y >= 0 &&
+ extents->x + extents->width <= sub->extents.width &&
+ extents->y + extents->height <= sub->extents.height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ is_contained = TRUE;
+ }
+
+ if (is_contained &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (pattern->base.filter, &tx, &ty))
+ {
+ *ix = tx + sub->extents.x;
+ *iy = ty + sub->extents.y;
+ return pixman_image_ref (source->pixman_image);
+ }
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ (uint32_t *) (source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride),
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ }
+ }
+ }
+
+ if (pixman_image == NULL) {
+ struct acquire_source_cleanup *cleanup;
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_status_t status;
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL))
+ return NULL;
+
+ cleanup->surface = pattern->surface;
+ status = _cairo_surface_acquire_source_image (pattern->surface,
+ &cleanup->image,
+ &cleanup->image_extra);
+ if (unlikely (status)) {
+ free (cleanup);
+ return NULL;
+ }
+
+ source = cleanup->image;
+ if (extend != CAIRO_EXTEND_NONE &&
+ extents->x >= 0 && extents->y >= 0 &&
+ extents->x + extents->width <= source->width &&
+ extents->y + extents->height <= source->height)
+ {
+ extend = CAIRO_EXTEND_NONE;
+ }
+
+ 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_release_source_image (pattern->surface,
+ cleanup->image,
+ cleanup->image_extra);
+ free (cleanup);
+ return NULL;
+ }
+
+ pixman_image_set_destroy_function (pixman_image,
+ _acquire_source_cleanup, cleanup);
+ }
+
+ if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
+ ! _nearest_sample (pattern->base.filter, &tx, &ty))
+ {
+ pixman_transform_t pixman_transform;
+ cairo_matrix_t m;
+
+ m = pattern->base.matrix;
+ if (m.x0 != 0. || m.y0 != 0.) {
+ cairo_matrix_t inv;
+ cairo_status_t status;
+ double x, y;
+
+ /* pixman also limits the [xy]_offset to 16 bits so evenly
+ * spread the bits between the two.
+ */
+ inv = m;
+ status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x = floor (inv.x0 / 2);
+ y = floor (inv.y0 / 2);
+ tx = -x;
+ ty = -y;
+ cairo_matrix_init_translate (&inv, x, y);
+ cairo_matrix_multiply (&m, &inv, &m);
+ } else {
+ tx = ty = 0;
+ }
+
+ _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+ *ix = tx;
+ *iy = ty;
+
+ if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
+ tx == pattern->base.matrix.x0 &&
+ ty == pattern->base.matrix.y0)
+ {
+ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ }
+ else
+ {
+ pixman_filter_t pixman_filter;
+
+ switch (pattern->base.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 (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_pattern (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *tx, int *ty)
+{
+ *tx = *ty = 0;
+
+ if (pattern == NULL)
+ return _pixman_white_image ();
+
+ switch (pattern->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+
+ 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_SURFACE:
+ return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
+ extents, tx, ty);
+ }
+}
+
+static void
+_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst,
+ const cairo_composite_rectangles_t *rects,
+ cairo_clip_t *clip)
+{
+ pixman_image_t *mask = NULL;
+ int mask_x = 0, mask_y = 0;
+
+ if (clip != NULL) {
+ cairo_surface_t *clip_surface;
+
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base);
+ assert (clip_surface->status == CAIRO_STATUS_SUCCESS);
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ mask_x = -clip->path->extents.x;
+ mask_y = -clip->path->extents.y;
+ } else {
+ if (rects->bounded.width == rects->unbounded.width &&
+ rects->bounded.height == rects->unbounded.height)
+ {
+ return;
+ }
+ }
+
+ /* top */
+ if (rects->bounded.y != rects->unbounded.y) {
+ int x = rects->unbounded.x;
+ int y = rects->unbounded.y;
+ int width = rects->unbounded.width;
+ int height = rects->bounded.y - y;
+
+ if (mask != NULL) {
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x, y, width, height,
+ 0);
+ }
+ }
+
+ /* left */
+ if (rects->bounded.x != rects->unbounded.x) {
+ int x = rects->unbounded.x;
+ int y = rects->bounded.y;
+ int width = rects->bounded.x - rects->unbounded.x;
+ int height = rects->bounded.height;
+
+ if (mask != NULL) {
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x, y, width, height,
+ 0);
+ }
+ }
+
+ /* right */
+ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+ int x = rects->bounded.x + rects->bounded.width;
+ int y = rects->bounded.y;
+ int width = rects->unbounded.x + rects->unbounded.width - x;
+ int height = rects->bounded.height;
+
+ if (mask != NULL) {
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x, y, width, height,
+ 0);
+ }
+ }
+
+ /* bottom */
+ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+ int x = rects->unbounded.x;
+ int y = rects->bounded.y + rects->bounded.height;
+ int width = rects->unbounded.width;
+ int height = rects->unbounded.y + rects->unbounded.height - y;
+
+ if (mask != NULL) {
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x, y, width, height,
+ 0);
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_region_t *clip_region,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ if (boxes->num_boxes <= 1 && clip_region == NULL) {
+ _cairo_image_surface_fixup_unbounded (dst, extents, NULL);
return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear, &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ 0);
+ }
+ }
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return dst->base.is_clear &&
+ dst->base.content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+/* low level compositor */
+typedef cairo_status_t
+(*image_draw_func_t) (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region);
+
+static pixman_image_t *
+_create_composite_mask_pattern (cairo_clip_t *clip,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_region_t *clip_region = NULL;
+ pixman_image_t *mask;
+ cairo_status_t status;
+ cairo_bool_t need_clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (! _cairo_status_is_error (status));
+
+ /* The all-clipped state should never propagate this far. */
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
- if (cairo_region_equal (surface->clip_region, region))
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+
+ mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL))
+ return NULL;
+
+ /* Is it worth setting the clip region here? */
+ if (clip_region != NULL) {
+ pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y);
+ pixman_image_set_clip_region32 (mask, &clip_region->rgn);
+ pixman_region32_translate (&clip_region->rgn, extents->x, extents->y);
+ }
+
+ status = draw_func (draw_closure,
+ mask, PIXMAN_a8,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ if (unlikely (status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+
+ if (need_clip_surface) {
+ cairo_surface_t *tmp;
+
+ tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8);
+ if (unlikely (tmp->status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+
+ pixman_image_ref (mask);
+
+ status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y);
+ cairo_surface_destroy (tmp);
+ if (unlikely (status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+ }
+
+ if (clip_region != NULL)
+ pixman_image_set_clip_region (mask, NULL);
+
+ return mask;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *mask;
+
+ mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (pattern == NULL) {
+ if (dst->pixman_format == PIXMAN_a8) {
+ pixman_image_composite (_pixman_operator (op),
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else {
+ pixman_image_t *src;
+
+ src = _pixman_white_image ();
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+ } else {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *tmp;
+ cairo_surface_t *clip_surface;
+ cairo_status_t status;
+
+ tmp = pixman_image_create_bits (dst->pixman_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (tmp == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (src == NULL) {
+ status = (*draw_func) (draw_closure,
+ tmp, dst->pixman_format,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ } else {
+ /* Initialize the temporary surface from the destination surface */
+ if (! dst->base.is_clear) {
+ pixman_image_composite (PIXMAN_OP_SRC,
+ dst->pixman_image, NULL, tmp,
+ extents->x, extents->y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height);
+ }
+
+ status = (*draw_func) (draw_closure,
+ tmp, dst->pixman_format,
+ op, src,
+ extents->x, extents->y,
+ extents, NULL);
+ }
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ assert (clip->path != NULL);
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base);
+ if (unlikely (clip_surface->status))
+ goto CLEANUP_SURFACE;
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite (PIXMAN_OP_LERP,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip->path->extents.x,
+ extents->y - clip->path->extents.y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#else
+ /* Punch the clip out of the destination */
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ NULL, dst->pixman_image,
+ extents->x - clip->path->extents.x,
+ extents->y - clip->path->extents.y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now add the two results together */
+ pixman_image_composite (PIXMAN_OP_ADD,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip->path->extents.x,
+ extents->y - clip->path->extents.y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#endif
+ } else {
+ pixman_image_composite (PIXMAN_OP_SRC,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip->path->extents.x,
+ extents->y - clip->path->extents.y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+
+ CLEANUP_SURFACE:
+ pixman_image_unref (tmp);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t *clip,
+ const cairo_pattern_t *pattern,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *mask, *src;
+ int src_x, src_y;
+
+ if (pattern == NULL) {
+ cairo_region_t *clip_region;
+ cairo_status_t status;
+
+ status = draw_func (draw_closure,
+ dst->pixman_image, dst->pixman_format,
+ CAIRO_OPERATOR_SOURCE, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0);
+
+ return status;
+ }
+
+ /* Create a surface that is mask IN clip */
+ mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite (PIXMAN_OP_LERP,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#else
+ /* Compute dest' = dest OUT (mask IN clip) */
+ pixman_image_composite (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ pixman_image_composite (PIXMAN_OP_ADD,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#endif
+ } else {
+ pixman_image_composite (PIXMAN_OP_SRC,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+
+ pixman_image_unref (src);
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_clip_and_composite (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ const cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (! _cairo_status_is_error (status));
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (reduce_alpha_op (dst, op, src)) {
+ op = CAIRO_OPERATOR_ADD;
+ src = NULL;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (clip, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ } else {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (need_clip_surface) {
+ if (extents->is_bounded) {
+ status = _clip_and_composite_with_mask (clip, op, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ } else {
+ status = _clip_and_composite_combine (clip, op, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ }
+ } else {
+ status = draw_func (draw_closure,
+ dst->pixman_image, dst->pixman_format,
+ op, src,
+ 0, 0,
+ &extents->bounded,
+ clip_region);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ _cairo_image_surface_fixup_unbounded (dst, extents,
+ need_clip_surface ? clip : NULL);
+ }
+
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
+
+ return status;
+}
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+_line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+_project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ pixman_line_fixed_t *out)
+{
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+
+typedef struct {
+ cairo_trapezoid_t *traps;
+ int num_traps;
+ cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static void
+_pixman_image_add_traps (pixman_image_t *image,
+ int dst_x, int dst_y,
+ composite_traps_info_t *info)
+{
+ cairo_trapezoid_t *t = info->traps;
+ int num_traps = info->num_traps;
+ while (num_traps--) {
+ pixman_trapezoid_t trap;
+
+ /* top/bottom will be clamped to surface bounds */
+ trap.top = _cairo_fixed_to_16_16 (t->top);
+ trap.bottom = _cairo_fixed_to_16_16 (t->bottom);
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (_line_exceeds_16_16 (&t->left))) {
+ _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
+ trap.left.p1.y = trap.top;
+ trap.left.p2.y = trap.bottom;
+ } else {
+ trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
+ }
+
+ if (unlikely (_line_exceeds_16_16 (&t->right))) {
+ _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
+ trap.right.p1.y = trap.top;
+ trap.right.p2.y = trap.bottom;
+ } else {
+ trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
+ }
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+
+ t++;
+ }
+}
+
+static cairo_status_t
+_composite_traps (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_traps_info_t *info = closure;
+ pixman_image_t *src, *mask;
+ pixman_format_code_t format;
+ int src_x = 0, src_y = 0;
+ cairo_status_t status;
+
+ /* Special case adding trapezoids onto a mask surface; we want to avoid
+ * creating an intermediate temporary mask unnecessarily.
+ *
+ * We make the assumption here that the portion of the trapezoids
+ * contained within the surface is bounded by [dst_x,dst_y,width,height];
+ * the Cairo core code passes bounds based on the trapezoid extents.
+ */
+ format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+ if (dst_format == format &&
+ (pattern == NULL ||
+ (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern))))
+ {
+ _pixman_image_add_traps (dst, dst_x, dst_y, info);
return CAIRO_STATUS_SUCCESS;
+ }
+
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = pixman_image_create_bits (format, extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_SOURCE;
+ }
+
+ _pixman_image_add_traps (mask, extents->x, extents->y, info);
+ pixman_image_composite (_pixman_operator (op),
+ src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+
+ status = CAIRO_STATUS_SUCCESS;
+ CLEANUP_SOURCE:
+ pixman_image_unref (src);
- cairo_region_destroy (surface->clip_region);
- surface->clip_region = cairo_region_reference (region);
+ return status;
+}
- if (! pixman_image_set_clip_region32 (surface->pixman_image,
- region ? &region->rgn : NULL))
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+ return
+ (color->alpha_short >> 8 << 24) |
+ (color->red_short >> 8 << 16) |
+ (color->green_short & 0xff00) |
+ (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t *color,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ uint32_t c;
+
+ if (!(format == PIXMAN_a8r8g8b8 ||
+ format == PIXMAN_x8r8g8b8 ||
+ format == PIXMAN_a8b8g8r8 ||
+ format == PIXMAN_x8b8g8r8 ||
+ format == PIXMAN_b8g8r8a8 ||
+ format == PIXMAN_b8g8r8x8 ||
+ format == PIXMAN_r5g6b5 ||
+ format == PIXMAN_b5g6r5 ||
+ format == PIXMAN_a8))
{
+ return FALSE;
+ }
+
+ c = color_to_uint32 (color);
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+ c = ((c & 0xff000000) >> 0) |
+ ((c & 0x00ff0000) >> 16) |
+ ((c & 0x0000ff00) >> 0) |
+ ((c & 0x000000ff) << 16);
+ }
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+ c = ((c & 0xff000000) >> 24) |
+ ((c & 0x00ff0000) >> 8) |
+ ((c & 0x0000ff00) << 8) |
+ ((c & 0x000000ff) << 24);
+ }
+
+ if (format == PIXMAN_a8) {
+ c = c >> 24;
+ } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+ c = ((((c) >> 3) & 0x001f) |
+ (((c) >> 5) & 0x07e0) |
+ (((c) >> 8) & 0xf800));
+ }
+
+ *pixel = c;
+ return TRUE;
+}
+
+static inline cairo_bool_t
+pattern_to_pixel (const cairo_solid_pattern_t *solid,
+ cairo_operator_t op,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ *pixel = 0;
+ return TRUE;
+ }
+
+ if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ if (op == CAIRO_OPERATOR_OVER) {
+ if (solid->color.alpha_short >= 0xff00)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (op != CAIRO_OPERATOR_SOURCE)
+ return FALSE;
+
+ return color_to_pixel (&solid->color, format, pixel);
+}
+
+typedef struct _fill_span {
+ cairo_span_renderer_t base;
+
+ uint8_t *mask_data;
+ pixman_image_t *src, *dst, *mask;
+} fill_span_renderer_t;
+
+static cairo_status_t
+_fill_span (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fill_span_renderer_t *renderer = abstract_renderer;
+ uint8_t *row;
+ unsigned i;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ row = renderer->mask_data - spans[0].x;
+ for (i = 0; i < num_spans - 1; i++) {
+ /* We implement setting the most common single pixel wide
+ * span case to avoid the overhead of a memset call.
+ * Open coding setting longer spans didn't show a
+ * noticeable improvement over memset.
+ */
+ if (spans[i+1].x == spans[i].x + 1) {
+ row[spans[i].x] = spans[i].coverage;
+ } else {
+ memset (row + spans[i].x,
+ spans[i].coverage,
+ spans[i+1].x - spans[i].x);
+ }
+ }
+
+ do {
+ pixman_image_composite (PIXMAN_OP_OVER,
+ renderer->src, renderer->mask, renderer->dst,
+ 0, 0, 0, 0,
+ spans[0].x, y++,
+ spans[i].x - spans[0].x, 1);
+ } while (--height);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* avoid using region code to re-validate boxes */
+static cairo_status_t
+_fill_unaligned_boxes (cairo_image_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ uint32_t pixel,
+ const cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ fill_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i;
+
+ /* XXX
+ * using composite for fill:
+ * spiral-box-nonalign-evenodd-fill.512 2201957 2.202
+ * spiral-box-nonalign-nonzero-fill.512 336726 0.337
+ * spiral-box-pixalign-evenodd-fill.512 352256 0.352
+ * spiral-box-pixalign-nonzero-fill.512 147056 0.147
+ * using fill:
+ * spiral-box-nonalign-evenodd-fill.512 3174565 3.175
+ * spiral-box-nonalign-nonzero-fill.512 182710 0.183
+ * spiral-box-pixalign-evenodd-fill.512 353863 0.354
+ * spiral-box-pixalign-nonzero-fill.512 147402 0.147
+ *
+ * cairo-perf-trace seems to favour using fill.
+ */
+
+ renderer.base.render_rows = _fill_span;
+ renderer.dst = dst->pixman_image;
+
+ if ((unsigned) extents->bounded.width <= sizeof (buf)) {
+ renderer.mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width, 1,
+ (uint32_t *) buf,
+ sizeof (buf));
+ } else {
+ renderer.mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width, 1,
+ NULL, 0);
+ }
+ if (unlikely (renderer.mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask);
+
+ renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+ if (unlikely (renderer.src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_MASK;
}
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+ /* first blit any aligned part of the boxes */
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
+
+ if (x2 > x1 && y2 > y1) {
+ cairo_box_t b;
+
+ pixman_fill ((uint32_t *) dst->data,
+ dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+
+ /* top */
+ if (! _cairo_fixed_is_integer (box[i].p1.y)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = box[i].p1.y;
+ b.p2.x = box[i].p2.x;
+ b.p2.y = _cairo_fixed_from_int (y1);
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* left */
+ if (! _cairo_fixed_is_integer (box[i].p1.x)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = box[i].p1.y;
+ b.p2.x = _cairo_fixed_from_int (x1);
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* right */
+ if (! _cairo_fixed_is_integer (box[i].p2.x)) {
+ b.p1.x = _cairo_fixed_from_int (x2);
+ b.p1.y = box[i].p1.y;
+ b.p2.x = box[i].p2.x;
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* bottom */
+ if (! _cairo_fixed_is_integer (box[i].p2.y)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = _cairo_fixed_from_int (y2);
+ b.p2.x = box[i].p2.x;
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+ } else {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+
+ CLEANUP_CONVERTER:
+ converter.base.destroy (&converter.base);
+ pixman_image_unref (renderer.src);
+ CLEANUP_MASK:
+ pixman_image_unref (renderer.mask);
+
+ return status;
+}
+
+typedef struct _cairo_image_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ uint8_t *mask_data;
+ uint32_t mask_stride;
+} cairo_image_surface_span_renderer_t;
+
+static cairo_status_t
+_cairo_image_surface_span (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
+ uint8_t *row;
+ unsigned i;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX will it be quicker to repeat the sparse memset,
+ * or perform a simpler memcpy?
+ * The fairly dense spiral benchmarks suggests that the sparse
+ * memset is a win there as well.
+ */
+ row = renderer->mask_data + y * renderer->mask_stride;
+ do {
+ for (i = 0; i < num_spans - 1; i++) {
+ if (! spans[i].coverage)
+ continue;
+
+ /* We implement setting rendering the most common single
+ * pixel wide span case to avoid the overhead of a memset
+ * call. Open coding setting longer spans didn't show a
+ * noticeable improvement over memset. */
+ if (spans[i+1].x == spans[i].x + 1) {
+ row[spans[i].x] = spans[i].coverage;
+ } else {
+ memset (row + spans[i].x,
+ spans[i].coverage,
+ spans[i+1].x - spans[i].x);
+ }
+ }
+ row += renderer->mask_stride;
+ } while (--height);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_composite_unaligned_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ cairo_image_surface_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ pixman_image_t *mask, *src;
+ cairo_status_t status;
+ const struct _cairo_boxes_chunk *chunk;
+ int i, src_x, src_y;
+
+ i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height;
+ if ((unsigned) i <= sizeof (buf)) {
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height,
+ (uint32_t *) buf,
+ CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8));
+ memset (buf, 0, i);
+ } else {
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height,
+ NULL, 0);
+ }
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer.base.render_rows = _cairo_image_surface_span;
+ renderer.mask_stride = pixman_image_get_stride (mask);
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
+ renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x;
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ src = _pixman_image_for_pattern (pattern, &extents->bounded, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ pixman_image_composite (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_unref (src);
+
+ CLEANUP:
+ converter.base.destroy (&converter.base);
+ pixman_image_unref (mask);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_mask = FALSE;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_mask &&
+ (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ if (! boxes->is_pixel_aligned) {
+ if (need_clip_mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op,
+ dst->pixman_format, &pixel))
+ {
+ return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents);
+ }
+ else
+ {
+ return _composite_unaligned_boxes (dst, op, pattern, boxes, extents);
+ }
+ }
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (! need_clip_mask &&
+ pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format,
+ &pixel))
+ {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+ }
+ }
+ }
+ else
+ {
+ pixman_image_t *src = NULL, *mask = NULL;
+ int src_x, src_y, mask_x = 0, mask_y = 0;
+ pixman_op_t pixman_op = _pixman_operator (op);
+
+ if (need_clip_mask) {
+ cairo_surface_t *clip_surface;
+
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ mask_x = -clip->path->extents.x;
+ mask_y = -clip->path->extents.y;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = NULL;
+ pixman_op = PIXMAN_OP_OUT_REVERSE;
+ }
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ }
+
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, &extents->bounded, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ src = mask;
+ src_x = mask_x;
+ src_y = mask_y;
+ mask = NULL;
+ }
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_image_composite (pixman_op,
+ src, mask, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1, y1,
+ x2 - x1, y2 - y1);
+ }
+ }
+
+ if (pattern != NULL)
+ pixman_image_unref (src);
+
+ if (! extents->is_bounded) {
+ status =
+ _cairo_image_surface_fixup_unbounded_boxes (dst, extents,
+ clip_region, boxes);
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_traps_t traps;
+ cairo_status_t status;
+ composite_traps_info_t info;
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Otherwise render via a mask and composite in the usual fashion. */
+ status = _cairo_traps_init_boxes (&traps, boxes);
+ if (unlikely (status))
+ return status;
+
+ info.num_traps = traps.num_traps;
+ info.traps = traps.traps;
+ info.antialias = antialias;
+ return _clip_and_composite (dst, op, src,
+ _composite_traps, &info,
+ extents, clip);
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+ return _cairo_fixed_integer_round (line->p1.x) == _cairo_fixed_integer_round (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
+ ! _mono_edge_is_vertical (&traps->traps[i].right))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ } else {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+ ! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_boxes_for_traps (cairo_boxes_t *boxes,
+ cairo_traps_t *traps)
+{
+ int i;
+
+ _cairo_boxes_init (boxes);
+
+ boxes->num_boxes = traps->num_traps;
+ boxes->chunks.base = (cairo_box_t *) traps->traps;
+ boxes->chunks.count = traps->num_traps;
+ boxes->chunks.size = traps->num_traps;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ boxes->chunks.base[i].p1.x = x1;
+ boxes->chunks.base[i].p1.y = y1;
+ boxes->chunks.base[i].p2.x = x2;
+ boxes->chunks.base[i].p2.y = y2;
+
+ if (boxes->is_pixel_aligned) {
+ boxes->is_pixel_aligned =
+ _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+ _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+ }
+ }
+}
+
+static cairo_status_t
+_clip_and_composite_trapezoids (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_traps_info_t info;
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_status_t status;
+
+ if (traps->num_traps == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL) {
+ cairo_region_t *clip_region;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (traps->has_intersections) {
+ if (traps->is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else if (traps->is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Use a fast path if the trapezoids consist of a simple region,
+ * but we can only do this if we do not have a clip surface, or can
+ * substitute the mask with the clip.
+ */
+ if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) &&
+ (! need_clip_surface ||
+ (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
+ {
+ cairo_boxes_t boxes;
+
+ _boxes_for_traps (&boxes, traps);
+ return _clip_and_composite_boxes (dst, op, src,
+ &boxes, antialias,
+ extents, clip);
+ }
+
+ /* No fast path, exclude self-intersections and clip trapezoids. */
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ info.traps = traps->traps;;
+ info.num_traps = traps->num_traps;
+ info.antialias = antialias;
+ return _clip_and_composite (dst, op, src,
+ _composite_traps, &info,
+ extents, clip);
+}
+
+static cairo_bool_t
+box_is_aligned (const cairo_box_t *box)
+{
+ return
+ _cairo_fixed_is_integer (box->p1.x) &&
+ _cairo_fixed_is_integer (box->p1.y) &&
+ _cairo_fixed_is_integer (box->p2.x) &&
+ _cairo_fixed_is_integer (box->p2.y);
+}
+
+static inline cairo_status_t
+_clip_to_boxes (cairo_clip_t **clip,
+ const cairo_composite_rectangles_t *extents,
+ cairo_box_t **boxes,
+ int *num_boxes)
+{
+ cairo_status_t status;
+ const cairo_rectangle_int_t *rect;
+
+ rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+
+ if (*clip == NULL)
+ goto EXTENTS;
+
+ status = _cairo_clip_rectangle (*clip, rect);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
+ switch ((int) status) {
+ case CAIRO_STATUS_SUCCESS:
+ if (extents->is_bounded || (*num_boxes == 1 && box_is_aligned (*boxes)))
+ *clip = NULL;
+ goto DONE;
+
+ case CAIRO_INT_STATUS_UNSUPPORTED:
+ goto EXTENTS;
+
+ default:
+ return status;
+ }
+
+ EXTENTS:
+ status = CAIRO_STATUS_SUCCESS;
+ _cairo_box_from_rectangle (&(*boxes)[0], rect);
+ *num_boxes = 1;
+ DONE:
+ return status;
+}
+
+static cairo_clip_path_t *
+_clip_get_single_path (cairo_clip_t *clip)
+{
+ cairo_clip_path_t *iter = clip->path;
+ cairo_clip_path_t *path = NULL;
+
+ do {
+ if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) {
+ if (path != NULL)
+ return FALSE;
+
+ path = iter;
+ }
+ iter = iter->prev;
+ } while (iter != NULL);
+
+ return path;
+}
+
+/* high level image interface */
+
+static cairo_int_status_t
+_cairo_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_path_t *clip_path;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ surface->width,
+ surface->height,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ /* If the clip cannot be reduced to a set of boxes, we will need to
+ * use a clipmask. Paint is special as it is the only operation that
+ * does not implicitly use a mask, so we may be able to reduce this
+ * operation to a fill...
+ */
+ if (clip != NULL &&
+ extents.is_bounded &&
+ (clip_path = _clip_get_single_path (clip)) != NULL)
+ {
+ status = _cairo_image_surface_fill (surface, op, source,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ }
+ else
+ {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, CAIRO_ANTIALIAS_DEFAULT,
+ &extents, clip);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_mask (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ const cairo_pattern_t *mask_pattern = closure;
+ pixman_image_t *src, *mask = NULL;
+ int src_x = 0, src_y = 0;
+ int mask_x = 0, mask_y = 0;
+
+ if (src_pattern != NULL) {
+ src = _pixman_image_for_pattern (src_pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = _pixman_image_for_pattern (mask_pattern, extents, &mask_x, &mask_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (mask_pattern->has_component_alpha)
+ pixman_image_set_component_alpha (mask, TRUE);
+ } else {
+ src = _pixman_image_for_pattern (mask_pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite (_pixman_operator (op), src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + mask_x, extents->y + mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ if (mask != NULL)
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
+_cairo_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ surface->width, surface->height,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status)) {
+ _cairo_clip_fini (&local_clip);
+ return status;
+ }
+
+ have_clip = TRUE;
+ }
+
+ status = _clip_and_composite (surface, op, source,
+ _composite_mask, (void *) mask,
+ &extents, clip);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+typedef struct {
+ cairo_polygon_t *polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_spans_info_t;
+
+//#define USE_BOTOR_SCAN_CONVERTER
+static cairo_status_t
+_composite_spans (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE];
+ composite_spans_info_t *info = closure;
+ cairo_image_surface_span_renderer_t renderer;
+#if USE_BOTOR_SCAN_CONVERTER
+ cairo_box_t box;
+ cairo_botor_scan_converter_t converter;
+#else
+ cairo_scan_converter_t *converter;
+#endif
+ pixman_image_t *mask;
+ cairo_status_t status;
+
+#if USE_BOTOR_SCAN_CONVERTER
+ box.p1.x = _cairo_fixed_from_int (extents->x);
+ box.p1.y = _cairo_fixed_from_int (extents->y);
+ box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+ box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+ _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+ status = converter.base.add_polygon (&converter.base, info->polygon);
+#else
+ converter = _cairo_tor_scan_converter_create (extents->x, extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ info->fill_rule);
+ status = converter->add_polygon (converter, info->polygon);
+#endif
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+
+ /* TODO: support rendering to A1 surfaces (or: go add span
+ * compositing to pixman.) */
+
+ if (pattern == NULL && dst_format == PIXMAN_a8) {
+ mask = dst;
+ dst = NULL;
+ } else {
+ int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8);
+ uint8_t *data = mask_buf;
+
+ if (extents->height * stride <= (int) sizeof (mask_buf))
+ memset (data, 0, extents->height * stride);
+ else
+ data = NULL, stride = 0;
+
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->width,
+ extents->height,
+ (uint32_t *) data,
+ stride);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_CONVERTER;
+ }
+ }
+
+ renderer.base.render_rows = _cairo_image_surface_span;
+ renderer.mask_stride = pixman_image_get_stride (mask);
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
+ if (dst != NULL)
+ renderer.mask_data -= extents->y * renderer.mask_stride + extents->x;
+ else
+ renderer.mask_data -= dst_y * renderer.mask_stride + dst_x;
+
+#if USE_BOTOR_SCAN_CONVERTER
+ status = converter.base.generate (&converter.base, &renderer.base);
+#else
+ status = converter->generate (converter, &renderer.base);
+#endif
+ if (unlikely (status))
+ goto CLEANUP_RENDERER;
+
+ if (dst != NULL) {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_RENDERER;
+ }
+
+ pixman_image_composite (_pixman_operator (op), src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0, /* mask.x, mask.y */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+
+ CLEANUP_RENDERER:
+ if (dst != NULL)
+ pixman_image_unref (mask);
+ CLEANUP_CONVERTER:
+#if USE_BOTOR_SCAN_CONVERTER
+ converter.base.destroy (&converter.base);
+#else
+ converter->destroy (converter);
+#endif
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (polygon->num_edges == 0) {
+ cairo_traps_t traps;
+
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_traps_init (&traps);
+ status = _clip_and_composite_trapezoids (dst, op, src,
+ &traps, antialias,
+ extents, clip);
+ _cairo_traps_fini (&traps);
+
+ return status;
+ }
+
+ _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ composite_spans_info_t info;
+
+ info.polygon = polygon;
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+
+ status = _clip_and_composite (dst, op, src,
+ _composite_spans, &info,
+ extents, clip);
+ } else {
+ cairo_traps_t traps;
+
+ _cairo_traps_init (&traps);
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_trapezoids (dst, op, src,
+ &traps, antialias,
+ extents, clip);
+ }
+
+ _cairo_traps_fini (&traps);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ surface->width,
+ surface->height,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (path->is_rectilinear) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ CAIRO_FILL_RULE_WINDING, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ surface->width,
+ surface->height,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents))
+ clip = NULL;
+
+ if (extents.is_bounded && clip != NULL) {
+ cairo_clip_path_t *clip_path;
+
+ if (((clip_path = _clip_get_single_path (clip)) != NULL) &&
+ _cairo_path_fixed_equal (&clip_path->path, path))
+ {
+ clip = NULL;
+ }
+ }
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ if (_cairo_path_fixed_is_rectilinear_fill (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+ } else {
+ cairo_polygon_t polygon;
+
+ assert (! path->is_empty_fill);
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ fill_rule, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_composite_glyphs_via_mask (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_font_t *font = info->font;
+ cairo_glyph_t *glyphs = info->glyphs;
+ int num_glyphs = info->num_glyphs;
+ pixman_image_t *mask = NULL;
+ pixman_image_t *src;
+ pixman_image_t *white;
+ pixman_format_code_t mask_format = 0; /* silence gcc */
+ cairo_status_t status;
+ int src_x, src_y;
+ int i;
+
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ white = _pixman_white_image ();
+ if (unlikely (white == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_scaled_font_freeze_cache (font);
+
+ for (i = 0; i < num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (font, glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ goto CLEANUP;
+
+ glyph_surface = scaled_glyph->surface;
+
+ if (glyph_surface->width == 0 || glyph_surface->height == 0)
+ continue;
+
+ /* To start, create the mask using the format from the first
+ * glyph. Later we'll deal with different formats. */
+ if (mask == NULL) {
+ mask_format = glyph_surface->pixman_format;
+ mask = pixman_image_create_bits (mask_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* If we have glyphs of different formats, we "upgrade" the mask
+ * to the wider of the formats. */
+ if (glyph_surface->pixman_format != mask_format &&
+ PIXMAN_FORMAT_BPP (mask_format) <
+ PIXMAN_FORMAT_BPP (glyph_surface->pixman_format))
+ {
+ pixman_image_t *new_mask;
+
+ mask_format = glyph_surface->pixman_format;
+ new_mask = pixman_image_create_bits (mask_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (new_mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ pixman_image_composite (PIXMAN_OP_SRC,
+ white, mask, new_mask,
+ 0, 0, 0, 0, 0, 0,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+ mask = new_mask;
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+ if (glyph_surface->pixman_format == mask_format) {
+ pixman_image_composite (PIXMAN_OP_ADD,
+ glyph_surface->pixman_image, NULL, mask,
+ 0, 0, 0, 0,
+ x - extents->x, y - extents->y,
+ glyph_surface->width,
+ glyph_surface->height);
+ } else {
+ pixman_image_composite (PIXMAN_OP_ADD,
+ white, glyph_surface->pixman_image, mask,
+ 0, 0, 0, 0,
+ x - extents->x, y - extents->y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+
+ pixman_image_composite (_pixman_operator (op),
+ src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+CLEANUP:
+ _cairo_scaled_font_thaw_cache (font);
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+ pixman_image_unref (white);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_glyphs (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ pixman_op_t pixman_op = _pixman_operator (op);
+ pixman_image_t *src = NULL;
+ int src_x, src_y;
+ cairo_status_t status;
+ int i;
+
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ src_x -= dst_x;
+ src_y -= dst_y;
+ } else {
+ src = _pixman_white_image ();
+ src_x = src_y = 0;
+ }
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ status = CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (info->font);
+ for (i = 0; i < info->num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ break;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->width && glyph_surface->height) {
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ pixman_image_composite (pixman_op,
+ src, glyph_surface->pixman_image, dst,
+ x + src_x, y + src_y,
+ 0, 0,
+ x - dst_x, y - dst_y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+ _cairo_scaled_font_thaw_cache (info->font);
+
+ pixman_image_unref (src);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ composite_glyphs_info_t glyph_info;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_bool_t overlap;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ surface->width,
+ surface->height,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ have_clip = TRUE;
+ }
+
+ glyph_info.font = scaled_font;
+ glyph_info.glyphs = glyphs;
+ glyph_info.num_glyphs = num_glyphs;
+
+ status = _clip_and_composite (surface, op, source,
+ overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs,
+ &glyph_info,
+ &extents, clip);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ *num_remaining = 0;
+ return status;
+}
+
+static cairo_bool_t
+_cairo_image_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_image_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+}
+
+/* legacy interface kept for compatibility until surface-fallback is removed */
+static cairo_status_t
+_cairo_image_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ image_rect_out->x = 0;
+ image_rect_out->y = 0;
+ image_rect_out->width = surface->width;
+ image_rect_out->height = surface->height;
+
+ *image_out = surface;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+}
+
+static cairo_status_t
+_cairo_image_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (src->backend == surface->base.backend) {
+ *clone_offset_x = *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
_cairo_image_surface_composite (cairo_operator_t op,
const cairo_pattern_t *src_pattern,
const cairo_pattern_t *mask_pattern,
@@ -1110,82 +3921,85 @@ _cairo_image_surface_composite (cairo_operator_t op,
unsigned int height,
cairo_region_t *clip_region)
{
- cairo_surface_attributes_t src_attr, mask_attr;
- cairo_image_surface_t *dst = abstract_dst;
- cairo_image_surface_t *src;
- cairo_image_surface_t *mask;
- cairo_int_status_t status;
+ cairo_image_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ pixman_image_t *src;
+ int src_offset_x, src_offset_y;
+ cairo_status_t status;
- status = _cairo_image_surface_set_clip_region (dst, clip_region);
- if (unlikely (status))
- return status;
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
- status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern,
- &dst->base,
- src_x, src_y,
- mask_x, mask_y,
- width, height,
- CAIRO_PATTERN_ACQUIRE_NONE,
- (cairo_surface_t **) &src,
- (cairo_surface_t **) &mask,
- &src_attr, &mask_attr);
- if (unlikely (status))
- return status;
+ extents.source.x = src_x;
+ extents.source.y = src_y;
+ extents.source.width = width;
+ extents.source.height = height;
- status = _cairo_image_surface_set_attributes (src, &src_attr,
- dst_x + width / 2.,
- dst_y + height / 2.);
- if (unlikely (status))
- goto CLEANUP_SURFACES;
+ extents.mask.x = dst_x;
+ extents.mask.y = dst_y;
+ extents.mask.width = width;
+ extents.mask.height = height;
- if (mask) {
- status = _cairo_image_surface_set_attributes (mask, &mask_attr,
- dst_x + width / 2.,
- dst_y + height / 2.);
- if (unlikely (status))
- goto CLEANUP_SURFACES;
+ extents.bounded.x = dst_x;
+ extents.bounded.y = dst_y;
+ extents.bounded.width = width;
+ extents.bounded.height = height;
+
+ if (clip_region != NULL) {
+ cairo_region_get_extents (clip_region, &extents.unbounded);
+ } else {
+ extents.unbounded.x = 0;
+ extents.unbounded.y = 0;
+ extents.unbounded.width = dst->width;
+ extents.unbounded.height = dst->height;
+ }
+
+ extents.is_bounded = _cairo_operator_bounded_by_either (op);
+
+ src = _pixman_image_for_pattern (src_pattern, &extents.bounded, &src_offset_x, &src_offset_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (mask_pattern != NULL) {
+ pixman_image_t *mask;
+ int mask_offset_x, mask_offset_y;
+
+ mask = _pixman_image_for_pattern (mask_pattern, &extents.bounded, &mask_offset_x, &mask_offset_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
pixman_image_composite (_pixman_operator (op),
- src->pixman_image,
- mask->pixman_image,
- dst->pixman_image,
- src_x + src_attr.x_offset,
- src_y + src_attr.y_offset,
- mask_x + mask_attr.x_offset,
- mask_y + mask_attr.y_offset,
- dst_x, dst_y,
- width, height);
+ src, mask, dst->pixman_image,
+ src_x + src_offset_x,
+ src_y + src_offset_y,
+ mask_x + mask_offset_x,
+ mask_y + mask_offset_y,
+ dst_x, dst_y, width, height);
+
+ pixman_image_unref (mask);
} else {
pixman_image_composite (_pixman_operator (op),
- src->pixman_image,
- NULL,
- dst->pixman_image,
- src_x + src_attr.x_offset,
- src_y + src_attr.y_offset,
+ src, NULL, dst->pixman_image,
+ src_x + src_offset_x,
+ src_y + src_offset_y,
0, 0,
- dst_x, dst_y,
- width, height);
+ dst_x, dst_y, width, height);
}
- if (! _cairo_operator_bounded_by_source (op)) {
- status = _cairo_surface_composite_fixup_unbounded (&dst->base,
- &src_attr, src->width, src->height,
- mask ? &mask_attr : NULL,
- mask ? mask->width : 0,
- mask ? mask->height : 0,
- src_x, src_y,
- mask_x, mask_y,
- dst_x, dst_y, width, height,
- clip_region);
- }
+ pixman_image_unref (src);
- CLEANUP_SURFACES:
- if (mask)
- _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr);
+ if (! extents.is_bounded)
+ _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
- _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
- return status;
+ return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
@@ -1212,9 +4026,6 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface,
pixman_color.blue = color->blue_short;
pixman_color.alpha = color->alpha_short;
- status = _cairo_image_surface_set_clip_region (surface, NULL);
- assert (status == CAIRO_STATUS_SUCCESS);
-
if (num_rects > ARRAY_LENGTH (stack_rects)) {
pixman_rects = _cairo_malloc_ab (num_rects, sizeof (pixman_rectangle16_t));
if (unlikely (pixman_rects == NULL))
@@ -1228,7 +4039,6 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface,
pixman_rects[i].height = rects[i].height;
}
- /* XXX: pixman_fill_region() should be implemented */
status = CAIRO_STATUS_SUCCESS;
if (! pixman_image_fill_rectangles (_pixman_operator (op),
surface->pixman_image,
@@ -1245,42 +4055,6 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface,
return status;
}
-static pixman_format_code_t
-_pixman_mask_format_from_antialias (cairo_antialias_t antialias)
-{
- if (antialias == CAIRO_ANTIALIAS_NONE)
- return PIXMAN_a1;
- return PIXMAN_a8;
-}
-
-static void
-_pixman_add_trapezoids (pixman_image_t *image,
- int dst_x, int dst_y,
- const cairo_trapezoid_t *traps,
- int num_traps)
-{
- while (num_traps--) {
- pixman_trapezoid_t trap;
-
- trap.top = _cairo_fixed_to_16_16 (traps->top);
- trap.bottom = _cairo_fixed_to_16_16 (traps->bottom);
-
- trap.left.p1.x = _cairo_fixed_to_16_16 (traps->left.p1.x);
- trap.left.p1.y = _cairo_fixed_to_16_16 (traps->left.p1.y);
- trap.left.p2.x = _cairo_fixed_to_16_16 (traps->left.p2.x);
- trap.left.p2.y = _cairo_fixed_to_16_16 (traps->left.p2.y);
-
- trap.right.p1.x = _cairo_fixed_to_16_16 (traps->right.p1.x);
- trap.right.p1.y = _cairo_fixed_to_16_16 (traps->right.p1.y);
- trap.right.p2.x = _cairo_fixed_to_16_16 (traps->right.p2.x);
- trap.right.p2.y = _cairo_fixed_to_16_16 (traps->right.p2.y);
-
- pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
-
- traps++;
- }
-}
-
static cairo_int_status_t
_cairo_image_surface_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
@@ -1296,11 +4070,10 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
int num_traps,
cairo_region_t *clip_region)
{
- cairo_surface_attributes_t attributes;
cairo_image_surface_t *dst = abstract_dst;
- cairo_image_surface_t *src;
- cairo_int_status_t status;
- pixman_image_t *mask;
+ cairo_composite_rectangles_t extents;
+ composite_traps_info_t info;
+ cairo_status_t status;
if (height == 0 || width == 0)
return CAIRO_STATUS_SUCCESS;
@@ -1308,101 +4081,66 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- /* Special case adding trapezoids onto a mask surface; we want to avoid
- * creating an intermediate temporary mask unnecessarily.
- *
- * We make the assumption here that the portion of the trapezoids
- * contained within the surface is bounded by [dst_x,dst_y,width,height];
- * the Cairo core code passes bounds based on the trapezoid extents.
- *
- * Currently the check clip_region == NULL is needed for correct
- * functioning, since pixman_add_trapezoids() doesn't obey the
- * surface clip, which is a libpixman bug , but there's no harm in
- * falling through to the general case when the surface is clipped
- * since libpixman would have to generate an intermediate mask anyways.
- */
- if (op == CAIRO_OPERATOR_ADD &&
- clip_region == NULL &&
- _cairo_pattern_is_opaque_solid (pattern) &&
- dst->base.content == CAIRO_CONTENT_ALPHA &&
- antialias != CAIRO_ANTIALIAS_NONE)
- {
- _pixman_add_trapezoids (dst->pixman_image, 0, 0, traps, num_traps);
- return CAIRO_STATUS_SUCCESS;
- }
+ extents.source.x = src_x;
+ extents.source.y = src_y;
+ extents.source.width = width;
+ extents.source.height = height;
- status = _cairo_image_surface_set_clip_region (dst, clip_region);
- if (unlikely (status))
- return status;
+ extents.mask.x = dst_x;
+ extents.mask.y = dst_y;
+ extents.mask.width = width;
+ extents.mask.height = height;
- status = _cairo_pattern_acquire_surface (pattern, &dst->base,
- src_x, src_y, width, height,
- CAIRO_PATTERN_ACQUIRE_NONE,
- (cairo_surface_t **) &src,
- &attributes);
- if (unlikely (status))
- return status;
+ extents.bounded.x = dst_x;
+ extents.bounded.y = dst_y;
+ extents.bounded.width = width;
+ extents.bounded.height = height;
- status = _cairo_image_surface_set_attributes (src, &attributes,
- dst_x + width / 2.,
- dst_y + height / 2.);
- if (unlikely (status))
- goto CLEANUP_SOURCE;
+ cairo_region_get_extents (clip_region, &extents.unbounded);
+ extents.is_bounded = _cairo_operator_bounded_by_either (op);
- mask = pixman_image_create_bits (_pixman_mask_format_from_antialias (antialias),
- width, height, NULL, 0);
- if (unlikely (mask == NULL)) {
- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto CLEANUP_SOURCE;
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
}
- _pixman_add_trapezoids (mask, dst_x, dst_y, traps, num_traps);
-
- pixman_image_composite (_pixman_operator (op),
- src->pixman_image,
- mask,
- dst->pixman_image,
- src_x + attributes.x_offset,
- src_y + attributes.y_offset,
- 0, 0,
- dst_x, dst_y,
- width, height);
-
- pixman_image_unref (mask);
+ info.traps = traps;
+ info.num_traps = num_traps;
+ info.antialias = antialias;
+ status = _composite_traps (&info,
+ dst->pixman_image,
+ dst->pixman_format,
+ op,
+ pattern,
+ 0, 0,
+ &extents.bounded,
+ clip_region);
- if (! _cairo_operator_bounded_by_mask (op)) {
- status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
- &attributes,
- src->width, src->height,
- width, height,
- src_x, src_y,
- 0, 0,
- dst_x, dst_y, width, height,
- clip_region);
- }
+ if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded)
+ _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
- CLEANUP_SOURCE:
- _cairo_pattern_release_surface (pattern, &src->base, &attributes);
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
return status;
}
-typedef struct _cairo_image_surface_span_renderer {
+typedef struct _legacy_image_surface_span_renderer {
cairo_span_renderer_t base;
cairo_operator_t op;
const cairo_pattern_t *pattern;
cairo_antialias_t antialias;
+ cairo_region_t *clip_region;
+ pixman_image_t *mask;
uint8_t *mask_data;
uint32_t mask_stride;
- cairo_image_surface_t *src;
- cairo_surface_attributes_t src_attributes;
- cairo_image_surface_t *mask;
cairo_image_surface_t *dst;
cairo_composite_rectangles_t composite_rectangles;
-} cairo_image_surface_span_renderer_t;
+} legacy_image_surface_span_renderer_t;
void
_cairo_image_surface_span_render_row (
@@ -1446,7 +4184,7 @@ _cairo_image_surface_span_renderer_render_rows (
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
- cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
while (height--)
_cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
return CAIRO_STATUS_SUCCESS;
@@ -1455,17 +4193,11 @@ _cairo_image_surface_span_renderer_render_rows (
static void
_cairo_image_surface_span_renderer_destroy (void *abstract_renderer)
{
- cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
- if (!renderer) return;
-
- if (renderer->src != NULL) {
- _cairo_pattern_release_surface (renderer->pattern,
- &renderer->src->base,
- &renderer->src_attributes);
- }
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
+ if (renderer == NULL)
+ return;
- if (renderer->mask != NULL)
- cairo_surface_destroy (&renderer->mask->base);
+ pixman_image_unref (renderer->mask);
free (renderer);
}
@@ -1473,47 +4205,41 @@ _cairo_image_surface_span_renderer_destroy (void *abstract_renderer)
static cairo_status_t
_cairo_image_surface_span_renderer_finish (void *abstract_renderer)
{
- cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
- cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
+ cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
+ cairo_image_surface_t *dst = renderer->dst;
+ pixman_image_t *src;
+ int src_x, src_y;
- if (renderer->src == NULL || renderer->mask == NULL)
- return CAIRO_STATUS_SUCCESS;
+ if (renderer->clip_region != NULL) {
+ cairo_status_t status;
- status = cairo_surface_status (&renderer->mask->base);
- if (status == CAIRO_STATUS_SUCCESS) {
- cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
- cairo_image_surface_t *src = renderer->src;
- cairo_image_surface_t *dst = renderer->dst;
- cairo_surface_attributes_t *src_attributes = &renderer->src_attributes;
- int width = rects->bounded.width;
- int height = rects->bounded.height;
+ status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ src = _pixman_image_for_pattern (renderer->pattern, &rects->bounded, &src_x, &src_y);
+ if (src == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+
+ pixman_image_composite (_pixman_operator (renderer->op),
+ src,
+ renderer->mask,
+ dst->pixman_image,
+ rects->bounded.x + src_x,
+ rects->bounded.y + src_y,
+ 0, 0,
+ rects->bounded.x, rects->bounded.y,
+ rects->bounded.width, rects->bounded.height);
+
+ if (! rects->is_bounded)
+ _cairo_image_surface_fixup_unbounded (dst, rects, NULL);
+
+ if (renderer->clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
- pixman_image_composite (_pixman_operator (renderer->op),
- src->pixman_image,
- renderer->mask->pixman_image,
- dst->pixman_image,
- rects->bounded.x + src_attributes->x_offset,
- rects->bounded.y + src_attributes->y_offset,
- 0, 0, /* mask.x, mask.y */
- rects->bounded.x, rects->bounded.y,
- width, height);
-
- if (! rects->is_bounded) {
- status = _cairo_surface_composite_shape_fixup_unbounded (
- &dst->base,
- src_attributes,
- src->width, src->height,
- width, height,
- rects->bounded.x, rects->bounded.y,
- 0, 0, /* mask.x, mask.y */
- rects->bounded.x, rects->bounded.y,
- width, height,
- dst->clip_region);
- }
- }
- if (status != CAIRO_STATUS_SUCCESS)
- return _cairo_span_renderer_set_error (abstract_renderer,
- status);
return CAIRO_STATUS_SUCCESS;
}
@@ -1539,14 +4265,10 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op,
cairo_region_t *clip_region)
{
cairo_image_surface_t *dst = abstract_dst;
- cairo_image_surface_span_renderer_t *renderer = calloc(1, sizeof(*renderer));
- cairo_status_t status;
+ legacy_image_surface_span_renderer_t *renderer;
- status = _cairo_image_surface_set_clip_region (dst, clip_region);
- if (unlikely (status))
- return _cairo_span_renderer_create_in_error (status);
-
- if (renderer == NULL)
+ renderer = calloc(1, sizeof(*renderer));
+ if (unlikely (renderer == NULL))
return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
@@ -1556,67 +4278,25 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op,
renderer->pattern = pattern;
renderer->antialias = antialias;
renderer->dst = dst;
+ renderer->clip_region = clip_region;
renderer->composite_rectangles = *rects;
- status = _cairo_pattern_acquire_surface (
- renderer->pattern, &renderer->dst->base,
- rects->bounded.x, rects->bounded.y,
- rects->bounded.width, rects->bounded.height,
- CAIRO_PATTERN_ACQUIRE_NONE,
- (cairo_surface_t **) &renderer->src,
- &renderer->src_attributes);
- if (status)
- goto unwind;
-
- status = _cairo_image_surface_set_attributes (
- renderer->src, &renderer->src_attributes,
- rects->bounded.x + rects->bounded.width/2,
- rects->bounded.y + rects->bounded.height/2);
- if (status)
- goto unwind;
-
/* TODO: support rendering to A1 surfaces (or: go add span
* compositing to pixman.) */
- renderer->mask = (cairo_image_surface_t *)
- cairo_image_surface_create (CAIRO_FORMAT_A8,
- rects->bounded.width,
- rects->bounded.height);
-
- status = cairo_surface_status (&renderer->mask->base);
-
- unwind:
- if (status != CAIRO_STATUS_SUCCESS) {
- _cairo_image_surface_span_renderer_destroy (renderer);
- return _cairo_span_renderer_create_in_error (status);
+ renderer->mask = pixman_image_create_bits (PIXMAN_a8,
+ rects->bounded.width,
+ rects->bounded.height,
+ NULL, 0);
+ if (renderer->mask == NULL) {
+ free (renderer);
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
- renderer->mask_data = renderer->mask->data - rects->bounded.x - rects->bounded.y * renderer->mask->stride;
- renderer->mask_stride = renderer->mask->stride;
- return &renderer->base;
-}
-
-static cairo_bool_t
-_cairo_image_surface_get_extents (void *abstract_surface,
- cairo_rectangle_int_t *rectangle)
-{
- cairo_image_surface_t *surface = abstract_surface;
-
- rectangle->x = 0;
- rectangle->y = 0;
- rectangle->width = surface->width;
- rectangle->height = surface->height;
-
- return TRUE;
-}
-
-static void
-_cairo_image_surface_get_font_options (void *abstract_surface,
- cairo_font_options_t *options)
-{
- _cairo_font_options_init_default (options);
+ renderer->mask_stride = pixman_image_get_stride (renderer->mask);
+ renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride;
- cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+ return &renderer->base;
}
/**
@@ -1647,6 +4327,7 @@ const cairo_surface_backend_t _cairo_image_surface_backend = {
_cairo_image_surface_composite_trapezoids,
_cairo_image_surface_create_span_renderer,
_cairo_image_surface_check_span_renderer,
+
NULL, /* copy_page */
NULL, /* show_page */
_cairo_image_surface_get_extents,
@@ -1657,11 +4338,12 @@ const cairo_surface_backend_t _cairo_image_surface_backend = {
NULL, /* font_fini */
NULL, /* glyph_fini */
- NULL, /* paint */
- NULL, /* mask */
- NULL, /* stroke */
- NULL, /* fill */
- NULL, /* show_glyphs */
+ _cairo_image_surface_paint,
+ _cairo_image_surface_mask,
+ _cairo_image_surface_stroke,
+ _cairo_image_surface_fill,
+ _cairo_image_surface_glyphs,
+ NULL, /* show_text_glyphs */
NULL, /* snapshot */
NULL, /* is_similar */
};
@@ -1673,7 +4355,6 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface,
cairo_format_t format)
{
cairo_image_surface_t *clone;
- cairo_surface_pattern_t pattern;
cairo_status_t status;
status = surface->base.status;
@@ -1688,17 +4369,13 @@ _cairo_image_surface_coerce (cairo_image_surface_t *surface,
if (unlikely (clone->base.status))
return clone;
- _cairo_pattern_init_for_surface (&pattern, &surface->base);
- status = _cairo_surface_paint (&clone->base,
- CAIRO_OPERATOR_SOURCE,
- &pattern.base,
- NULL);
- _cairo_pattern_fini (&pattern.base);
-
- if (unlikely (status)) {
- cairo_surface_destroy (&clone->base);
- return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
- }
+ pixman_image_composite (PIXMAN_OP_SRC,
+ surface->pixman_image, NULL, clone->pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ surface->width, surface->height);
+ clone->base.is_clear = FALSE;
clone->base.device_transform =
surface->base.device_transform;