summaryrefslogtreecommitdiff
path: root/src/cairo-clip.c
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2009-07-23 15:32:13 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2009-07-23 15:32:14 +0100
commitbed2701e1c89095878d549cbca8f22d84f3dda3c (patch)
tree974807761b6d839839ecad9961eae8d567898dcc /src/cairo-clip.c
parentf5a1cdf283a6aa1f4409ccbf3c2274fb587724fe (diff)
Remove clip handling from generic surface layer.
Handling clip as part of the surface state, as opposed to being part of the operation state, is cumbersome and a hindrance to providing true proxy surface support. For example, the clip must be copied from the surface onto the fallback image, but this was forgotten causing undue hassle in each backend. Another example is the contortion the meta surface endures to ensure the clip is correctly recorded. By contrast passing the clip along with the operation is quite simple and enables us to write generic handlers for providing surface wrappers. (And in the future, we should be able to write more esoteric wrappers, e.g. automatic 2x FSAA, trivially.) In brief, instead of the surface automatically applying the clip before calling the backend, the backend can call into a generic helper to apply clipping. For raster surfaces, clip regions are handled automatically as part of the composite interface. For vector surfaces, a clip helper is introduced to replay and callback into an intersect_clip_path() function as necessary. Whilst this is not primarily a performance related change (the change should just move the computation of the clip from the moment it is applied by the user to the moment it is required by the backend), it is important to track any potential regression: ppc: Speedups ======== image-rgba evolution-20090607-0 1026085.22 0.18% -> 672972.07 0.77%: 1.52x speedup ▌ image-rgba evolution-20090618-0 680579.98 0.12% -> 573237.66 0.16%: 1.19x speedup ▎ image-rgba swfdec-fill-rate-4xaa-0 460296.92 0.36% -> 407464.63 0.42%: 1.13x speedup ▏ image-rgba swfdec-fill-rate-2xaa-0 128431.95 0.47% -> 115051.86 0.42%: 1.12x speedup ▏ Slowdowns ========= image-rgba firefox-periodic-table-0 56837.61 0.78% -> 66055.17 3.20%: 1.09x slowdown ▏
Diffstat (limited to 'src/cairo-clip.c')
-rw-r--r--src/cairo-clip.c1290
1 files changed, 660 insertions, 630 deletions
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 79e4cbe6..93348945 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -3,6 +3,7 @@
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
+ * Copyright © 2009 Chris Wilson
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -35,802 +36,777 @@
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Kristian Høgsberg <krh@redhat.com>
+ * Chris Wilson <chris@chris-wilson.co.uk>
*/
#include "cairoint.h"
#include "cairo-clip-private.h"
+#include "cairo-path-fixed-private.h"
-static cairo_clip_path_t *
-_cairo_clip_path_reference (cairo_clip_path_t *clip_path);
+/* Keep a stash of recently freed clip_paths, since we need to
+ * reallocate them frequently.
+ */
+#define MAX_FREED_POOL_SIZE 4
+typedef struct {
+ void *pool[MAX_FREED_POOL_SIZE];
+ int top;
+} freed_pool_t;
-static void
-_cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
+static freed_pool_t clip_path_pool;
-void
-_cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target)
+static void *
+_atomic_fetch (void **slot)
{
- if (target && target->backend)
- clip->mode = _cairo_surface_get_clip_mode (target);
- else
- clip->mode = CAIRO_CLIP_MODE_MASK;
-
- clip->all_clipped = FALSE;
+ return _cairo_atomic_ptr_cmpxchg (slot, *slot, NULL);
+}
- clip->surface = NULL;
- clip->surface_rect.x = 0;
- clip->surface_rect.y = 0;
- clip->surface_rect.width = 0;
- clip->surface_rect.height = 0;
+static cairo_bool_t
+_atomic_store (void **slot, void *ptr)
+{
+ return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr) == NULL;
+}
- clip->serial = 0;
+static void *
+_freed_pool_get (freed_pool_t *pool)
+{
+ void *ptr;
+ int i;
- clip->region = NULL;
+ i = pool->top - 1;
+ if (i < 0)
+ i = 0;
- clip->path = NULL;
-}
+ ptr = _atomic_fetch (&pool->pool[i]);
+ if (ptr != NULL) {
+ pool->top = i;
+ return ptr;
+ }
-cairo_status_t
-_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
-{
- clip->mode = other->mode;
-
- clip->all_clipped = other->all_clipped;
-
- clip->surface = cairo_surface_reference (other->surface);
- clip->surface_rect = other->surface_rect;
-
- clip->serial = other->serial;
-
- if (other->region) {
- cairo_status_t status;
-
- clip->region = cairo_region_copy (other->region);
-
- status = cairo_region_status (clip->region);
- if (unlikely (status)) {
- cairo_surface_destroy (clip->surface);
- cairo_region_destroy (clip->region);
- clip->region = NULL;
-
- return status;
+ /* either empty or contended */
+ for (i = ARRAY_LENGTH (pool->pool); i--;) {
+ ptr = _atomic_fetch (&pool->pool[i]);
+ if (ptr != NULL) {
+ pool->top = i;
+ return ptr;
}
- } else {
- clip->region = NULL;
}
-
- clip->path = _cairo_clip_path_reference (other->path);
- return CAIRO_STATUS_SUCCESS;
+ /* empty */
+ pool->top = 0;
+ return NULL;
}
-void
-_cairo_clip_reset (cairo_clip_t *clip)
+static void
+_freed_pool_put (freed_pool_t *pool, void *ptr)
{
- clip->all_clipped = FALSE;
-
- /* destroy any existing clip-region artifacts */
- cairo_surface_destroy (clip->surface);
- clip->surface = NULL;
-
- clip->serial = 0;
+ int i = pool->top;
- if (clip->region) {
- cairo_region_destroy (clip->region);
+ if (_atomic_store (&pool->pool[i], ptr)) {
+ pool->top = i + 1;
+ return;
+ }
- clip->region = NULL;
+ /* either full or contended */
+ for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
+ if (_atomic_store (&pool->pool[i], ptr)) {
+ pool->top = i + 1;
+ return;
+ }
}
- _cairo_clip_path_destroy (clip->path);
- clip->path = NULL;
+ /* full */
+ pool->top = ARRAY_LENGTH (pool->pool);
+ free (ptr);
}
static void
-_cairo_clip_set_all_clipped (cairo_clip_t *clip, cairo_surface_t *target)
+_freed_pool_reset (freed_pool_t *pool)
{
- _cairo_clip_reset (clip);
+ int i;
- clip->all_clipped = TRUE;
- clip->serial = _cairo_surface_allocate_clip_serial (target);
+ for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
+ free (pool->pool[i]);
+ pool->pool[i] = NULL;
+ }
}
-
-static cairo_status_t
-_cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t *clip_path,
- cairo_rectangle_int_t *rectangle)
+static cairo_clip_path_t *
+_cairo_clip_path_create (cairo_clip_t *clip)
{
- while (clip_path) {
- cairo_rectangle_int_t extents;
+ cairo_clip_path_t *clip_path;
- _cairo_path_fixed_approximate_clip_extents (&clip_path->path, &extents);
+ clip_path = _freed_pool_get (&clip_path_pool);
+ if (unlikely (clip_path == NULL)) {
+ clip_path = malloc (sizeof (cairo_clip_path_t));
+ if (unlikely (clip_path == NULL))
+ return NULL;
+ }
- if (! _cairo_rectangle_intersect (rectangle, &extents))
- return CAIRO_STATUS_SUCCESS;
+ CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
- clip_path = clip_path->prev;
- }
+ clip_path->flags = 0;
+ clip_path->region = NULL;
+ clip_path->surface = NULL;
- return CAIRO_STATUS_SUCCESS;
+ clip_path->prev = clip->path;
+ clip->path = clip_path;
+
+ return clip_path;
}
-cairo_status_t
-_cairo_clip_intersect_to_rectangle (cairo_clip_t *clip,
- cairo_rectangle_int_t *rectangle)
+static cairo_clip_path_t *
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
{
- cairo_status_t status;
- cairo_bool_t is_empty;
+ if (clip_path == NULL)
+ return NULL;
- if (!clip)
- return CAIRO_STATUS_SUCCESS;
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
- if (clip->all_clipped) {
- *rectangle = clip->surface_rect;
- return CAIRO_STATUS_SUCCESS;
- }
+ _cairo_reference_count_inc (&clip_path->ref_count);
- if (clip->path) {
- status = _cairo_clip_path_intersect_to_rectangle (clip->path,
- rectangle);
- if (unlikely (status))
- return status;
- }
+ return clip_path;
+}
+
+static void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
+{
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
- if (clip->region) {
- cairo_rectangle_int_t extents;
+ if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
+ return;
- cairo_region_get_extents (clip->region, &extents);
- is_empty = _cairo_rectangle_intersect (rectangle, &extents);
- if (is_empty)
- return CAIRO_STATUS_SUCCESS;
- }
+ _cairo_path_fixed_fini (&clip_path->path);
+ if (clip_path->region != NULL)
+ cairo_region_destroy (clip_path->region);
+ if (clip_path->surface != NULL)
+ cairo_surface_destroy (clip_path->surface);
- if (clip->surface)
- is_empty = _cairo_rectangle_intersect (rectangle, &clip->surface_rect);
+ if (clip_path->prev != NULL)
+ _cairo_clip_path_destroy (clip_path->prev);
- return CAIRO_STATUS_SUCCESS;
+ _freed_pool_put (&clip_path_pool, clip_path);
+}
+
+void
+_cairo_clip_init (cairo_clip_t *clip)
+{
+ clip->all_clipped = FALSE;
+ clip->path = NULL;
}
+static void
+_cairo_clip_set_all_clipped (cairo_clip_t *clip)
+{
+ clip->all_clipped = TRUE;
+ if (clip->path != NULL) {
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ }
+}
+
+/* XXX consider accepting a matrix, no users yet. */
cairo_status_t
-_cairo_clip_intersect_to_region (cairo_clip_t *clip,
- cairo_region_t *region)
+_cairo_clip_init_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect)
{
+ cairo_clip_path_t *clip_path;
cairo_status_t status;
- if (!clip)
+ _cairo_clip_init (clip);
+ if (rect == NULL)
return CAIRO_STATUS_SUCCESS;
- if (clip->all_clipped)
- return cairo_region_intersect_rectangle (region, &clip->surface_rect);
-
- if (clip->path) {
- /* Intersect clip path into region. */
+ if (rect->width == 0 || rect->height == 0) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
}
- if (clip->region) {
- status = cairo_region_intersect (region, clip->region);
- if (unlikely (status))
- return status;
- }
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- if (clip->surface)
- return cairo_region_intersect_rectangle (region, &clip->surface_rect);
+ _cairo_path_fixed_init (&clip_path->path);
+
+ status = _cairo_path_fixed_move_to (&clip_path->path,
+ _cairo_fixed_from_int (rect->x),
+ _cairo_fixed_from_int (rect->y));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (0),
+ _cairo_fixed_from_int (rect->height));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (-rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_close_path (&clip_path->path);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ clip_path->extents = *rect;
+ clip_path->fill_rule = CAIRO_FILL_RULE_WINDING;
+ clip_path->tolerance = 1;
+ clip_path->antialias = CAIRO_ANTIALIAS_NONE;
+
+ /* could preallocate the region if it proves worthwhile */
return CAIRO_STATUS_SUCCESS;
}
-/* Combines the region of clip->surface given by extents in
- * device backend coordinates into the given temporary surface,
- * which has its origin at dst_x, dst_y in backend coordinates
- */
-cairo_status_t
-_cairo_clip_combine_to_surface (cairo_clip_t *clip,
- cairo_operator_t op,
- cairo_surface_t *dst,
- int dst_x,
- int dst_y,
- const cairo_rectangle_int_t *extents)
+void
+_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
{
- cairo_surface_pattern_t pattern;
- cairo_status_t status;
-
- if (clip->all_clipped)
- return CAIRO_STATUS_SUCCESS;
-
- _cairo_pattern_init_for_surface (&pattern, clip->surface);
-
- status = _cairo_surface_composite (op,
- &pattern.base,
- NULL,
- dst,
- extents->x - clip->surface_rect.x,
- extents->y - clip->surface_rect.y,
- 0, 0,
- extents->x - dst_x,
- extents->y - dst_y,
- extents->width, extents->height);
-
- _cairo_pattern_fini (&pattern.base);
+ if (other != NULL) {
+ clip->all_clipped = other->all_clipped;
+ clip->path = _cairo_clip_path_reference (other->path);
+ } else {
+ clip->all_clipped = FALSE;
+ clip->path = NULL;
+ }
+}
- return status;
+void
+_cairo_clip_reset (cairo_clip_t *clip)
+{
+ clip->all_clipped = FALSE;
+ if (clip->path != NULL) {
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ }
}
static cairo_status_t
_cairo_clip_intersect_path (cairo_clip_t *clip,
- cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_clip_path_t *clip_path;
cairo_status_t status;
+ cairo_rectangle_int_t extents;
+
+ if (clip->path != NULL) {
+ if (_cairo_path_fixed_equal (&clip->path->path, path)) {
+ if (clip->path->fill_rule == fill_rule) {
+ if (path->is_rectilinear ||
+ (tolerance == clip->path->tolerance &&
+ antialias == clip->path->antialias))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
+ }
- if (clip->mode != CAIRO_CLIP_MODE_PATH)
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ _cairo_path_fixed_approximate_clip_extents (path, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (clip->path != NULL) {
+ cairo_box_t box;
+
+ if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* does this clip wholly subsume the others? */
+ if (_cairo_path_fixed_is_box (path, &box)) {
+ if (box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) &&
+ box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) &&
+ box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) &&
+ box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
- clip_path = malloc (sizeof (cairo_clip_path_t));
+ clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
status = _cairo_path_fixed_init_copy (&clip_path->path, path);
if (unlikely (status)) {
- free (clip_path);
+ clip->path = clip->path->prev;
+ _cairo_clip_path_destroy (clip_path);
return status;
}
- CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
+ clip_path->extents = extents;
clip_path->fill_rule = fill_rule;
clip_path->tolerance = tolerance;
clip_path->antialias = antialias;
- clip_path->prev = clip->path;
- clip->path = clip_path;
return CAIRO_STATUS_SUCCESS;
}
-static cairo_clip_path_t *
-_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
-{
- if (clip_path == NULL)
- return NULL;
-
- assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
-
- _cairo_reference_count_inc (&clip_path->ref_count);
-
- return clip_path;
-}
-
-static void
-_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
-{
- if (clip_path == NULL)
- return;
-
- assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
-
- if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
- return;
-
- _cairo_path_fixed_fini (&clip_path->path);
- _cairo_clip_path_destroy (clip_path->prev);
- free (clip_path);
-}
-
-
-static cairo_int_status_t
-_cairo_clip_intersect_region (cairo_clip_t *clip,
- cairo_traps_t *traps,
- cairo_surface_t *target)
+cairo_status_t
+_cairo_clip_clip (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
{
- cairo_region_t *region;
- cairo_int_status_t status;
-
if (clip->all_clipped)
return CAIRO_STATUS_SUCCESS;
- if (clip->mode != CAIRO_CLIP_MODE_REGION)
- return CAIRO_INT_STATUS_UNSUPPORTED;
-
- status = _cairo_traps_extract_region (traps, &region);
- if (status)
- return status;
-
- if (clip->region) {
- status = cairo_region_intersect (clip->region, region);
- cairo_region_destroy (region);
- } else {
- clip->region = region;
+ /* catch the empty clip path */
+ if (_cairo_path_fixed_fill_is_empty (path)) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
}
- clip->serial = _cairo_surface_allocate_clip_serial (target);
-
- if (!clip->region || cairo_region_is_empty (clip->region))
- _cairo_clip_set_all_clipped (clip, target);
-
- return status;
+ return _cairo_clip_intersect_path (clip,
+ path, fill_rule, tolerance,
+ antialias);
}
static cairo_status_t
-_cairo_clip_intersect_mask (cairo_clip_t *clip,
- cairo_traps_t *traps,
- cairo_antialias_t antialias,
- cairo_surface_t *target)
+_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip,
+ cairo_clip_path_t *other_path,
+ const cairo_matrix_t *matrix)
{
- cairo_pattern_union_t pattern;
- cairo_box_t extents;
- cairo_rectangle_int_t surface_rect, target_rect;
- cairo_surface_t *surface = NULL;
- cairo_status_t status = CAIRO_STATUS_SUCCESS;
-
- if (clip->all_clipped)
- return CAIRO_STATUS_SUCCESS;
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
+ cairo_bool_t is_empty;
- /* Represent the clip as a mask surface. We create a new surface
- * the size of the intersection of the old mask surface and the
- * extents of the new clip path. */
+ if (other_path->prev != NULL) {
+ status = _cairo_clip_path_reapply_clip_path_transform (clip,
+ other_path->prev,
+ matrix);
+ if (unlikely (status))
+ return status;
+ }
- _cairo_traps_extents (traps, &extents);
- _cairo_box_round_to_rectangle (&extents, &surface_rect);
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- if (clip->surface != NULL) {
- if (! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect))
- goto DONE;
+ status = _cairo_path_fixed_init_copy (&clip_path->path,
+ &other_path->path);
+ if (unlikely (status)) {
+ _cairo_clip_path_destroy (clip_path);
+ return status;
}
- /* Intersect with the target surface rectangle so we don't use
- * more memory and time than we need to. */
- status = _cairo_surface_get_extents (target, &target_rect);
- if (status == CAIRO_STATUS_SUCCESS) {
- if (! _cairo_rectangle_intersect (&surface_rect, &target_rect))
- goto DONE;
+ _cairo_path_fixed_transform (&clip_path->path, matrix);
+ _cairo_path_fixed_approximate_clip_extents (&clip_path->path,
+ &clip_path->extents);
+ if (clip_path->prev != NULL) {
+ is_empty = _cairo_rectangle_intersect (&clip_path->extents,
+ &clip_path->prev->extents);
}
- if (surface_rect.width == 0 || surface_rect.height == 0)
- goto DONE;
+ clip_path->fill_rule = other_path->fill_rule;
+ clip_path->tolerance = other_path->tolerance;
+ clip_path->antialias = other_path->antialias;
- _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
- CAIRO_CONTENT_COLOR);
- /* The clipping operation should ideally be something like the following to
- * avoid having to do as many passes over the data
+ return CAIRO_STATUS_SUCCESS;
+}
- if (clip->surface != NULL) {
- _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
- } else {
- _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
- CAIRO_CONTENT_COLOR);
- }
- status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
- &pattern.base,
- surface,
- antialias,
- 0, 0,
- 0, 0,
- surface_rect.width,
- surface_rect.height,
- traps->traps,
- traps->num_traps);
-
- However this operation is not accelerated by pixman
-
- I believe the best possible operation would probably an unbounded SRC
- operator. Using SRC we could potentially avoid having to initialize
- the surface which would be ideal from an efficiency point of view.
- However, CAIRO_OPERATOR_SOURCE is bounded by the trapezoid mask and
- _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_SOURCE) will assert
- because it assumes CAIRO_OPERATOR_SOURCE has been converted into other
- operations.
- */
+static cairo_status_t
+_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip,
+ cairo_clip_path_t *other_path,
+ int tx, int ty)
+{
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
- surface = _cairo_surface_create_similar_solid (target,
- CAIRO_CONTENT_ALPHA,
- surface_rect.width,
- surface_rect.height,
- CAIRO_COLOR_TRANSPARENT);
- if (surface->status) {
- _cairo_pattern_fini (&pattern.base);
- return surface->status;
+ if (other_path->prev != NULL) {
+ status = _cairo_clip_path_reapply_clip_path_translate (clip,
+ other_path->prev,
+ tx, ty);
+ if (unlikely (status))
+ return status;
}
- /* Render the new clipping path into the new mask surface. */
-
- _cairo_traps_translate (traps, -surface_rect.x, -surface_rect.y);
-
- status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD,
- &pattern.base,
- surface,
- antialias,
- 0, 0,
- 0, 0,
- surface_rect.width,
- surface_rect.height,
- traps->traps,
- traps->num_traps);
-
- _cairo_pattern_fini (&pattern.base);
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ status = _cairo_path_fixed_init_copy (&clip_path->path,
+ &other_path->path);
if (unlikely (status)) {
- cairo_surface_destroy (surface);
+ _cairo_clip_path_destroy (clip_path);
return status;
}
- /* If there was a clip surface already, combine it with the new
- * mask surface using the IN operator, so we get the intersection
- * of the old and new clipping paths. */
-
- if (clip->surface != NULL) {
- _cairo_pattern_init_for_surface (&pattern.surface, clip->surface);
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (tx),
+ _cairo_fixed_from_int (ty));
- status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
- &pattern.base,
- NULL,
- surface,
- surface_rect.x - clip->surface_rect.x,
- surface_rect.y - clip->surface_rect.y,
- 0, 0,
- 0, 0,
- surface_rect.width,
- surface_rect.height);
+ clip_path->fill_rule = other_path->fill_rule;
+ clip_path->tolerance = other_path->tolerance;
+ clip_path->antialias = other_path->antialias;
- _cairo_pattern_fini (&pattern.base);
-
- if (unlikely (status)) {
- cairo_surface_destroy (surface);
- return status;
- }
+ clip_path->flags = other_path->flags;
+ if (other_path->region != NULL) {
+ clip_path->region = cairo_region_copy (other_path->region);
+ cairo_region_translate (clip_path->region, tx, ty);
}
+ clip_path->surface = cairo_surface_reference (other_path->surface);
- DONE:
- cairo_surface_destroy (clip->surface);
- clip->surface = surface;
- clip->surface_rect = surface_rect;
- clip->serial = _cairo_surface_allocate_clip_serial (target);
+ clip_path->extents = other_path->extents;
+ clip_path->extents.x += tx;
+ clip_path->extents.y += ty;
- if (surface_rect.width == 0 || surface_rect.height == 0)
- _cairo_clip_set_all_clipped (clip, target);
-
- return status;
+ return CAIRO_STATUS_SUCCESS;
}
-static cairo_status_t
-_cairo_clip_intersect_mask_using_spans (cairo_clip_t *clip,
- cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- double tolerance,
- cairo_antialias_t antialias,
- cairo_surface_t *target)
+cairo_status_t
+_cairo_clip_init_copy_transformed (cairo_clip_t *clip,
+ cairo_clip_t *other,
+ const cairo_matrix_t *matrix)
{
- cairo_span_renderer_t *renderer = NULL;
- cairo_pattern_union_t pattern;
- cairo_rectangle_int_t surface_rect;
- cairo_surface_t *surface = NULL;
- cairo_status_t status;
- cairo_operator_t op;
- cairo_composite_rectangles_t rects;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ int tx, ty;
- if (clip->all_clipped)
+ if (other == NULL) {
+ _cairo_clip_init (clip);
return CAIRO_STATUS_SUCCESS;
-
- _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
- CAIRO_CONTENT_COLOR);
-
- /* If we have a clip surface we're going to use IN to combine our
- * new clip with the old clip. The ADD is done to a transparent
- * surface, as that's a fast way of doing it currently. We should
- * really be using SOURCE instead, but _cairo_surface_composite()
- * checks that it's not called with SOURCE or DEST. */
- op = clip->surface ? CAIRO_OPERATOR_IN : CAIRO_OPERATOR_ADD;
-
- /* Test if the target can composite spans. We're going to assume
- * this is a good indicator of whether a similar surface is going
- * to be able to composite spans too. */
- if ( !_cairo_surface_check_span_renderer (op,
- &pattern.base,
- target,
- antialias,
- NULL))
- {
- status = CAIRO_INT_STATUS_UNSUPPORTED;
- goto BAIL;
}
- status = _cairo_surface_get_extents (target, &surface_rect);
- if (status)
- goto BAIL;
+ if (other->all_clipped) {
+ _cairo_clip_init (clip);
+ clip->all_clipped = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+ }
- /* We'll create a new surface the size of the intersection of the
- * old mask surface and the extents of the new clip path. */
- {
- cairo_rectangle_int_t extents;
+ if (_cairo_matrix_is_identity (matrix)) {
+ _cairo_clip_init_copy (clip, other);
+ return CAIRO_STATUS_SUCCESS;
+ }
- _cairo_path_fixed_approximate_clip_extents (path, &extents);
- if (! _cairo_rectangle_intersect (&surface_rect, &extents))
- goto SUCCESS;
+ if (other->path != NULL) {
+ _cairo_clip_init (clip);
- if (clip->surface != NULL &&
- ! _cairo_rectangle_intersect (&surface_rect, &clip->surface_rect))
- goto SUCCESS;
+ /* if we only need to translate, so we can reuse the caches... */
+ /* XXX we still loose the benefit of constructs when the copy is
+ * deleted though. Indirect clip_paths?
+ */
+ if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) {
+ status = _cairo_clip_path_reapply_clip_path_translate (clip,
+ other->path,
+ tx, ty);
+ } else {
+ status = _cairo_clip_path_reapply_clip_path_transform (clip,
+ other->path,
+ matrix);
+ if (clip->path->extents.width == 0 &&
+ clip->path->extents.height == 0)
+ {
+ _cairo_clip_set_all_clipped (clip);
+ }
+ }
}
- /* Make the new mask surface and optionally initialise it from the
- * previous clip if we have one. */
- surface = _cairo_surface_create_similar_solid (target,
- CAIRO_CONTENT_ALPHA,
- surface_rect.width,
- surface_rect.height,
- CAIRO_COLOR_TRANSPARENT);
- if (surface->status) {
- _cairo_pattern_fini (&pattern.base);
- return surface->status;
- }
-
- if (clip->surface) {
- cairo_surface_pattern_t old_clip;
- _cairo_pattern_init_for_surface (&old_clip, clip->surface);
- status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
- &old_clip.base,
- NULL,
- surface,
- surface_rect.x - clip->surface_rect.x,
- surface_rect.y - clip->surface_rect.y,
- 0, 0,
- 0, 0,
- surface_rect.width,
- surface_rect.height);
- _cairo_pattern_fini (&old_clip.base);
- if (status)
- goto BAIL;
- }
+ return status;
+}
- _cairo_composite_rectangles_init (&rects,
- surface_rect.x,
- surface_rect.y,
- surface_rect.width,
- surface_rect.height);
- rects.dst.x = 0;
- rects.dst.y = 0;
-
- /* Render the new clipping path into the new mask surface. We've
- * chosen op to either combine the new clip path with the existing
- * clip mask (if there is one) or just render it. */
- status =_cairo_path_fixed_fill_using_spans (op, &pattern.base,
- path, surface,
- fill_rule, tolerance,
- antialias, &rects);
- if (status)
- goto BAIL;
+static cairo_status_t
+_cairo_clip_apply_clip_path (cairo_clip_t *clip,
+ const cairo_clip_path_t *path)
+{
+ cairo_status_t status;
- SUCCESS:
- if (clip->surface != NULL)
- cairo_surface_destroy (clip->surface);
- clip->surface = surface;
- clip->surface_rect = surface_rect;
- clip->serial = _cairo_surface_allocate_clip_serial (target);
- surface = NULL;
-
- if (surface_rect.width == 0 || surface_rect.height == 0)
- _cairo_clip_set_all_clipped (clip, target);
-
- BAIL:
- if (renderer)
- renderer->destroy(renderer);
- if (surface)
- cairo_surface_destroy (surface);
- _cairo_pattern_fini (&pattern.base);
- return status;
+ if (path->prev != NULL)
+ status = _cairo_clip_apply_clip_path (clip, path->prev);
+
+ return _cairo_clip_intersect_path (clip,
+ &path->path,
+ path->fill_rule,
+ path->tolerance,
+ path->antialias);
}
cairo_status_t
-_cairo_clip_clip (cairo_clip_t *clip,
- cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- double tolerance,
- cairo_antialias_t antialias,
- cairo_surface_t *target)
+_cairo_clip_apply_clip (cairo_clip_t *clip,
+ const cairo_clip_t *other)
{
cairo_status_t status;
- cairo_rectangle_int_t limits, extents;
- cairo_traps_t traps;
- cairo_box_t ignored_box;
- cairo_bool_t have_limits;
if (clip->all_clipped)
return CAIRO_STATUS_SUCCESS;
- /* catch the empty clip path */
- if (! path->has_current_point) {
- _cairo_clip_set_all_clipped (clip, target);
+ if (other->all_clipped) {
+ _cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
}
- status = _cairo_clip_intersect_path (clip,
- path, fill_rule, tolerance,
- antialias);
- if (status == CAIRO_STATUS_SUCCESS)
- clip->serial = _cairo_surface_allocate_clip_serial (target);
+ status = CAIRO_STATUS_SUCCESS;
+ if (other->path != NULL)
+ status = _cairo_clip_apply_clip_path (clip, other->path);
- if (status != CAIRO_INT_STATUS_UNSUPPORTED)
- return status;
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
+{
+ cairo_int_status_t status;
+ cairo_traps_t traps;
+ cairo_region_t *prev = NULL;
+ cairo_box_t box;
- /* TODO: allow ANTIALIAS_NONE when we have a mono scan converter
- * again. */
- if (antialias != CAIRO_ANTIALIAS_NONE &&
- !_cairo_path_fixed_is_box (path, &ignored_box) &&
- !_cairo_path_fixed_is_region (path))
+ if (clip_path->flags &
+ (CAIRO_CLIP_PATH_HAS_REGION |
+ CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
{
- status = _cairo_clip_intersect_mask_using_spans (
- clip, path, fill_rule, tolerance, antialias, target);
- if (status != CAIRO_INT_STATUS_UNSUPPORTED)
- return status;
+ return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
+ CAIRO_INT_STATUS_UNSUPPORTED :
+ CAIRO_STATUS_SUCCESS;
}
- _cairo_traps_init (&traps);
-
- /* Limit the traps to the target surface and current clip
- * - so we don't add more traps than needed. */
- have_limits = FALSE;
- if (clip->region != NULL) {
- cairo_region_get_extents (clip->region, &limits);
- have_limits = TRUE;
+ if (! clip_path->path.maybe_fill_region) {
+ clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
}
- if (clip->surface != NULL) {
- if (have_limits) {
- if (! _cairo_rectangle_intersect (&limits, &clip->surface_rect)) {
- _cairo_clip_set_all_clipped (clip, target);
- return CAIRO_STATUS_SUCCESS;
- }
- } else {
- limits = clip->surface_rect;
- have_limits = TRUE;
+ /* first retrieve the region for our antecedents */
+ if (clip_path->prev != NULL) {
+ status = _cairo_clip_path_to_region (clip_path->prev);
+ if (status) {
+ clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+ return status;
}
- }
- status = _cairo_surface_get_extents (target, &extents);
- if (status == CAIRO_STATUS_SUCCESS) {
- if (have_limits) {
- if (! _cairo_rectangle_intersect (&limits, &extents)) {
- _cairo_clip_set_all_clipped (clip, target);
- return CAIRO_STATUS_SUCCESS;
- }
- } else {
- limits = extents;
- have_limits = TRUE;
- }
+ prev = clip_path->prev->region;
}
- if (have_limits) {
- cairo_box_t box;
+ /* now extract the region for ourselves */
- _cairo_box_from_rectangle (&box, &limits);
- _cairo_traps_limit (&traps, &box);
- }
+ /* XXX fixed fill to region */
+ _cairo_traps_init (&traps);
+
+ _cairo_box_from_rectangle (&box, &clip_path->extents);
+ _cairo_traps_limit (&traps, &box);
- status = _cairo_path_fixed_fill_to_traps (path,
- fill_rule,
- tolerance,
+ status = _cairo_path_fixed_fill_to_traps (&clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
&traps);
if (unlikely (status))
- goto bail;
+ return status;
- status = _cairo_clip_intersect_region (clip, &traps, target);
- if (status != CAIRO_INT_STATUS_UNSUPPORTED)
- goto bail;
+ status = _cairo_traps_extract_region (&traps, &clip_path->region);
+ _cairo_traps_fini (&traps);
- status = _cairo_clip_intersect_mask (clip, &traps, antialias, target);
+ if (status) {
+ clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+ return status;
+ }
- bail:
- _cairo_traps_fini (&traps);
+ if (prev != NULL) {
+ status = cairo_region_intersect (clip_path->region, prev);
+ if (unlikely (status))
+ return status;
+ }
- return status;
+ clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+ return CAIRO_STATUS_SUCCESS;
}
-void
-_cairo_clip_translate (cairo_clip_t *clip,
- cairo_fixed_t tx,
- cairo_fixed_t ty)
+cairo_surface_t *
+_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target)
{
- if (clip->all_clipped)
- return;
+ cairo_surface_t *surface;
+ cairo_pattern_union_t pattern;
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path = clip->path;
+ const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
+ cairo_region_t *clip_region;
+ cairo_bool_t need_translate;
- if (clip->region) {
- cairo_region_translate (clip->region,
- _cairo_fixed_integer_part (tx),
- _cairo_fixed_integer_part (ty));
- }
+ assert (clip_path != NULL);
- if (clip->surface) {
- clip->surface_rect.x += _cairo_fixed_integer_part (tx);
- clip->surface_rect.y += _cairo_fixed_integer_part (ty);
+ if (clip_path->surface != NULL &&
+ clip_path->surface->backend == target->backend)
+ {
+ return cairo_surface_reference (clip_path->surface);
}
- if (clip->path) {
- cairo_clip_path_t *clip_path = clip->path;
- cairo_matrix_t matrix;
-
- cairo_matrix_init_translate (&matrix,
- _cairo_fixed_to_double (tx),
- _cairo_fixed_to_double (ty));
+ surface = _cairo_surface_create_similar_solid (target,
+ CAIRO_CONTENT_ALPHA,
+ clip_extents->width,
+ clip_extents->height,
+ CAIRO_COLOR_TRANSPARENT,
+ FALSE);
+ if (surface == NULL) {
+ if (clip_path->surface != NULL &&
+ clip_path->surface->backend == &_cairo_image_surface_backend)
+ {
+ return cairo_surface_reference (clip_path->surface);
+ }
- while (clip_path) {
- _cairo_path_fixed_transform (&clip_path->path, &matrix);
- clip_path = clip_path->prev;
- }
+ surface =
+ _cairo_image_surface_create_with_content (CAIRO_CONTENT_ALPHA,
+ clip_extents->width,
+ clip_extents->height);
}
-}
+ if (unlikely (surface->status))
+ return surface;
-static cairo_status_t
-_cairo_clip_path_reapply_clip_path (cairo_clip_t *clip,
- cairo_clip_path_t *clip_path)
-{
- cairo_status_t status;
+ _cairo_pattern_init_solid (&pattern.solid,
+ CAIRO_COLOR_WHITE,
+ CAIRO_CONTENT_COLOR);
- if (clip_path->prev) {
- status = _cairo_clip_path_reapply_clip_path (clip, clip_path->prev);
- if (_cairo_status_is_error (status))
- return status;
- }
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (_cairo_status_is_error (status))
+ goto BAIL;
- return _cairo_clip_intersect_path (clip,
- &clip_path->path,
- clip_path->fill_rule,
- clip_path->tolerance,
- clip_path->antialias);
-}
+ need_translate = clip_extents->x || clip_extents->y;
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (need_translate) {
+ cairo_region_translate (clip_region,
+ -clip_extents->x, -clip_extents->y);
+ }
+ status = _cairo_surface_fill_region (surface,
+ CAIRO_OPERATOR_ADD,
+ CAIRO_COLOR_WHITE,
+ clip_region);
+ if (need_translate) {
+ cairo_region_translate (clip_region,
+ clip_extents->x, clip_extents->y);
+ }
+ if (unlikely (status))
+ goto BAIL;
-cairo_status_t
-_cairo_clip_init_deep_copy (cairo_clip_t *clip,
- cairo_clip_t *other,
- cairo_surface_t *target)
-{
- cairo_status_t status;
+ goto DONE;
+ } else {
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (-clip_extents->x),
+ _cairo_fixed_from_int (-clip_extents->y));
+ }
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_ADD,
+ &pattern.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (clip_extents->x),
+ _cairo_fixed_from_int (clip_extents->y));
+ }
- _cairo_clip_init (clip, target);
+ if (unlikely (status))
+ goto BAIL;
+ }
- if (other->mode != clip->mode) {
- /* We should reapply the original clip path in this case, and let
- * whatever the right handling is happen */
- } else {
- if (other->region) {
- clip->region = cairo_region_copy (other->region);
- if (unlikely ((status = cairo_region_status (clip->region))))
- goto BAIL;
- }
+ while ((clip_path = clip_path->prev) != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (_cairo_status_is_error (status))
+ goto BAIL;
- if (other->surface) {
- int dx, dy;
- status = _cairo_surface_clone_similar (target, other->surface,
- CAIRO_CONTENT_ALPHA,
- 0,
- 0,
- other->surface_rect.width,
- other->surface_rect.height,
- &dx, &dy,
- &clip->surface);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_region_translate (clip_region,
+ -clip_extents->x, -clip_extents->y);
+ status = _cairo_surface_fill_region (surface,
+ CAIRO_OPERATOR_IN,
+ CAIRO_COLOR_WHITE,
+ clip_region);
+ cairo_region_translate (clip_region,
+ clip_extents->x, clip_extents->y);
if (unlikely (status))
goto BAIL;
- clip->surface_rect = other->surface_rect;
-
- /* src offset was 0, so we expect an exact replica of the surface */
- assert (dx == 0);
- assert (dy == 0);
- }
-
- if (other->path) {
- status = _cairo_clip_path_reapply_clip_path (clip, other->path);
- if (_cairo_status_is_error (status))
+ goto DONE;
+ } else {
+ if (clip_path->surface != NULL &&
+ clip_path->surface->backend == surface->backend)
+ {
+ _cairo_pattern_init_for_surface (&pattern.surface,
+ clip_path->surface);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ -clip_path->extents.x + clip_extents->x,
+ -clip_path->extents.y + clip_extents->y);
+ status = _cairo_surface_paint (surface,
+ CAIRO_OPERATOR_IN,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ goto DONE;
+ } else {
+ /* XXX build intermediate surfaces? */
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (-clip_extents->x),
+ _cairo_fixed_from_int (-clip_extents->y));
+ }
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_IN,
+ &pattern.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (clip_extents->x),
+ _cairo_fixed_from_int (clip_extents->y));
+ }
+ }
+ if (unlikely (status))
goto BAIL;
- }
+ }
}
- return CAIRO_STATUS_SUCCESS;
+DONE:
+ cairo_surface_destroy (clip->path->surface);
+ return clip->path->surface = cairo_surface_reference (surface);
BAIL:
- if (clip->region)
- cairo_region_destroy (clip->region);
- if (clip->surface)
- cairo_surface_destroy (clip->surface);
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+}
- return status;
+const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip)
+{
+ if (clip->path == NULL)
+ return NULL;
+
+ return &clip->path->extents;
+}
+
+void
+_cairo_clip_drop_cache (cairo_clip_t *clip)
+{
+ cairo_clip_path_t *clip_path;
+
+ if (clip->path == NULL)
+ return;
+
+ clip_path = clip->path;
+ do {
+ if (clip_path->region != NULL) {
+ cairo_region_destroy (clip_path->region);
+ clip_path->region = NULL;
+ }
+
+ if (clip_path->surface != NULL) {
+ cairo_surface_destroy (clip_path->surface);
+ clip_path->surface = NULL;
+ }
+
+ clip_path->flags = 0;
+ } while ((clip_path = clip_path->prev) != NULL);
}
const cairo_rectangle_list_t _cairo_rectangles_nil =
@@ -862,42 +838,90 @@ _cairo_clip_int_rect_to_user (cairo_gstate_t *gstate,
return is_tight;
}
+cairo_int_status_t
+_cairo_clip_get_region (cairo_clip_t *clip,
+ cairo_region_t **region)
+{
+ cairo_int_status_t status;
+
+ *region = NULL;
+ if (clip->all_clipped)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (clip->path == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_clip_path_to_region (clip->path);
+ if (status)
+ return status;
+
+ *region = clip->path->region;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status)
+{
+ cairo_rectangle_list_t *list;
+
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+
+ list = malloc (sizeof (*list));
+ if (unlikely (list == NULL)) {
+ _cairo_error_throw (status);
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ }
+
+ list->status = status;
+ list->rectangles = NULL;
+ list->num_rectangles = 0;
+
+ return list;
+}
+
cairo_rectangle_list_t *
_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
{
+#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S));
+
cairo_rectangle_list_t *list;
cairo_rectangle_t *rectangles = NULL;
+ cairo_int_status_t status;
+ cairo_region_t *region;
int n_rects = 0;
+ int i;
- if (clip->all_clipped)
+ status = _cairo_clip_get_region (clip, &region);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
goto DONE;
-
- if (clip->path || clip->surface) {
- _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+ } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+ } else if (unlikely (status)) {
+ return ERROR_LIST (status);
}
- if (clip->region) {
- int i;
-
- n_rects = cairo_region_num_rectangles (clip->region);
-
+ if (region != NULL) {
+ n_rects = cairo_region_num_rectangles (region);
if (n_rects) {
rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
if (unlikely (rectangles == NULL)) {
- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < n_rects; ++i) {
cairo_rectangle_int_t clip_rect;
- cairo_region_get_rectangle (clip->region, i, &clip_rect);
-
- if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) {
- _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+ cairo_region_get_rectangle (region, i, &clip_rect);
+
+ if (! _cairo_clip_int_rect_to_user (gstate,
+ &clip_rect,
+ &rectangles[i]))
+ {
free (rectangles);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
}
}
}
@@ -908,31 +932,31 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
rectangles = malloc(sizeof (cairo_rectangle_t));
if (unlikely (rectangles == NULL)) {
- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
}
- if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate), &extents) ||
- !_cairo_clip_int_rect_to_user(gstate, &extents, rectangles))
+ if (_cairo_surface_get_extents (_cairo_gstate_get_target (gstate),
+ &extents) ||
+ ! _cairo_clip_int_rect_to_user (gstate, &extents, rectangles))
{
- _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
free (rectangles);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
}
}
DONE:
list = malloc (sizeof (cairo_rectangle_list_t));
if (unlikely (list == NULL)) {
- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
free (rectangles);
- return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
}
list->status = CAIRO_STATUS_SUCCESS;
list->rectangles = rectangles;
list->num_rectangles = n_rects;
return list;
+
+#undef ERROR_LIST
}
/**
@@ -955,3 +979,9 @@ cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list)
free (rectangle_list->rectangles);
free (rectangle_list);
}
+
+void
+_cairo_clip_reset_static_data (void)
+{
+ _freed_pool_reset (&clip_path_pool);
+}