summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2010-01-22 17:57:41 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2010-01-22 23:01:52 +0000
commitb9407af6a4bc792c1bcb52c90aa8a618627bb618 (patch)
treed44691d72890bce53bd2a28500fe6adec5859550
parentec7cc9dea16f94e1e56720be548cf5520ec4708b (diff)
image: Implement high level interface.
Write a dedicated compositor for pixman so that we avoid the middle-layer syndrome of surface-fallback. The major upshot of this rewrite is that the image surface is now several times quicker for glyph compositing, which dramatically improves performance for text rendering by firefox and friends. It also uses a couple of the new scan convertors, such as the rectangular scan converter for rectilinear paths. Speedups ======== image-rgba firefox-talos-gfx-0 342050.17 (342155.88 0.02%) -> 69412.44 (69702.90 0.21%): 4.93x speedup ███▉ image-rgba vim-0 97518.13 (97696.23 1.21%) -> 30712.63 (31238.65 0.85%): 3.18x speedup ██▏ image-rgba evolution-0 69927.77 (110261.08 19.84%) -> 24430.05 (25368.85 1.89%): 2.86x speedup █▉ image-rgba poppler-0 41452.61 (41547.03 2.51%) -> 21195.52 (21656.85 1.08%): 1.96x speedup █ image-rgba firefox-planet-gnome-0 217512.61 (217636.80 0.06%) -> 123341.02 (123641.94 0.12%): 1.76x speedup ▊ image-rgba swfdec-youtube-0 41302.71 (41373.60 0.11%) -> 31343.93 (31488.87 0.23%): 1.32x speedup ▍ image-rgba swfdec-giant-steps-0 20699.54 (20739.52 0.10%) -> 17360.19 (17375.51 0.04%): 1.19x speedup ▎ image-rgba gvim-0 167837.47 (168027.68 0.51%) -> 151105.94 (151635.85 0.18%): 1.11x speedup ▏ image-rgba firefox-talos-svg-0 375273.43 (388250.94 1.60%) -> 356846.09 (370370.08 1.86%): 1.05x speedup
-rw-r--r--src/cairo-image-surface.c3893
-rw-r--r--src/cairo-mutex-list-private.h2
-rw-r--r--src/cairo-xcb-surface.c10
-rw-r--r--src/cairoint.h21
-rw-r--r--test/clip-fill-unbounded.argb32.ref.pngbin1615 -> 1607 bytes
-rw-r--r--test/clip-fill-unbounded.rgb24.ref.pngbin1312 -> 1304 bytes
-rw-r--r--test/clip-stroke-unbounded.argb32.ref.pngbin1703 -> 1694 bytes
-rw-r--r--test/clip-stroke-unbounded.rgb24.ref.pngbin1383 -> 1372 bytes
-rw-r--r--test/clip-stroke.ref.pngbin1451 -> 1442 bytes
-rw-r--r--test/clipped-group.ref.pngbin289 -> 289 bytes
-rw-r--r--test/leaky-dashed-rectangle.ref.pngbin347 -> 357 bytes
-rw-r--r--test/scale-offset-image.xfail.pngbin9960 -> 9961 bytes
-rw-r--r--test/scale-offset-similar.xfail.pngbin9960 -> 9961 bytes
-rw-r--r--test/self-intersecting.ref.pngbin213 -> 168 bytes
14 files changed, 3295 insertions, 631 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;
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 2f483163a..c84cebf62 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -38,6 +38,8 @@
CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock)
+CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex)
+
CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index faa566549..666abc994 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -600,14 +600,8 @@ _get_image_surface (cairo_xcb_surface_t *surface,
* which takes data in an arbitrary format and converts it
* to something supported by that library.
*/
- image = (cairo_image_surface_t *)
- _cairo_image_surface_create_with_masks (data,
- &masks,
- extents.width,
- extents.height,
- bytes_per_line);
- if (image->base.status)
- goto FAIL;
+ ASSERT_NOT_REACHED;
+ goto FAIL;
}
/* Let the surface take ownership of the data */
diff --git a/src/cairoint.h b/src/cairoint.h
index c2d36e0c8..6e059c4ca 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -833,7 +833,6 @@ struct _cairo_image_surface {
int depth;
pixman_image_t *pixman_image;
- cairo_region_t *clip_region;
unsigned owns_data : 1;
unsigned transparency : 2;
@@ -2162,9 +2161,15 @@ _cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
cairo_private cairo_format_t
_cairo_format_from_content (cairo_content_t content) cairo_const;
+cairo_private cairo_format_t
+_cairo_format_from_pixman_format (pixman_format_code_t pixman_format);
+
cairo_private cairo_content_t
_cairo_content_from_format (cairo_format_t format) cairo_const;
+cairo_private cairo_content_t
+_cairo_content_from_pixman_format (pixman_format_code_t pixman_format);
+
cairo_private cairo_surface_t *
_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
pixman_format_code_t pixman_format);
@@ -2185,24 +2190,10 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data,
int stride);
cairo_private cairo_surface_t *
-_cairo_image_surface_create_with_masks (unsigned char *data,
- cairo_format_masks_t *format,
- int width,
- int height,
- int stride);
-
-cairo_private cairo_surface_t *
_cairo_image_surface_create_with_content (cairo_content_t content,
int width,
int height);
-cairo_private cairo_surface_t *
-_cairo_image_surface_create_for_data_with_content (unsigned char *data,
- cairo_content_t content,
- int width,
- int height,
- int stride);
-
cairo_private void
_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface);
diff --git a/test/clip-fill-unbounded.argb32.ref.png b/test/clip-fill-unbounded.argb32.ref.png
index 9adf992cd..b87efd4fb 100644
--- a/test/clip-fill-unbounded.argb32.ref.png
+++ b/test/clip-fill-unbounded.argb32.ref.png
Binary files differ
diff --git a/test/clip-fill-unbounded.rgb24.ref.png b/test/clip-fill-unbounded.rgb24.ref.png
index e57e29693..915ebe19b 100644
--- a/test/clip-fill-unbounded.rgb24.ref.png
+++ b/test/clip-fill-unbounded.rgb24.ref.png
Binary files differ
diff --git a/test/clip-stroke-unbounded.argb32.ref.png b/test/clip-stroke-unbounded.argb32.ref.png
index 39b9a301b..e48537f64 100644
--- a/test/clip-stroke-unbounded.argb32.ref.png
+++ b/test/clip-stroke-unbounded.argb32.ref.png
Binary files differ
diff --git a/test/clip-stroke-unbounded.rgb24.ref.png b/test/clip-stroke-unbounded.rgb24.ref.png
index a30352ebf..d2880f10c 100644
--- a/test/clip-stroke-unbounded.rgb24.ref.png
+++ b/test/clip-stroke-unbounded.rgb24.ref.png
Binary files differ
diff --git a/test/clip-stroke.ref.png b/test/clip-stroke.ref.png
index 7af2e9bd8..dd5ae9a39 100644
--- a/test/clip-stroke.ref.png
+++ b/test/clip-stroke.ref.png
Binary files differ
diff --git a/test/clipped-group.ref.png b/test/clipped-group.ref.png
index b25c9f4db..c3fcbf81d 100644
--- a/test/clipped-group.ref.png
+++ b/test/clipped-group.ref.png
Binary files differ
diff --git a/test/leaky-dashed-rectangle.ref.png b/test/leaky-dashed-rectangle.ref.png
index c0ba7b2c0..05f45846a 100644
--- a/test/leaky-dashed-rectangle.ref.png
+++ b/test/leaky-dashed-rectangle.ref.png
Binary files differ
diff --git a/test/scale-offset-image.xfail.png b/test/scale-offset-image.xfail.png
index fef3a3995..f0db601fc 100644
--- a/test/scale-offset-image.xfail.png
+++ b/test/scale-offset-image.xfail.png
Binary files differ
diff --git a/test/scale-offset-similar.xfail.png b/test/scale-offset-similar.xfail.png
index fef3a3995..f0db601fc 100644
--- a/test/scale-offset-similar.xfail.png
+++ b/test/scale-offset-similar.xfail.png
Binary files differ
diff --git a/test/self-intersecting.ref.png b/test/self-intersecting.ref.png
index 32d143987..d554d83ee 100644
--- a/test/self-intersecting.ref.png
+++ b/test/self-intersecting.ref.png
Binary files differ