summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--doc/public/cairo-docs.xml3
-rw-r--r--src/cairo-gstate.c586
-rw-r--r--src/cairo-traps.c55
-rw-r--r--src/cairoint.h4
-rw-r--r--test/Makefile.am5
-rw-r--r--test/trap-clip-ref.pngbin0 -> 71701 bytes
-rw-r--r--test/trap-clip.c213
8 files changed, 688 insertions, 196 deletions
diff --git a/ChangeLog b/ChangeLog
index 89269d10c..c175b821f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2005-04-13 Owen Taylor <otaylor@redhat.com>
+
+ * src/cairoint.h src/cairo-traps.c: Add _cairo_traps_extract_region
+ for converting trapezoids into a pixman region.
+
+ * src/cairo-gstate.c (cairo_clip): Represent all rectangular
+ pixel-aligned regions as regions, not just single rectangles.
+
+ * src/cairo-gstate.c (_cairo_gstate_clip_and_composite_trapezoid):
+ Split into manageable pieces, optimize rectangular pixel-
+ aligned regions by using _cairo_surface_fill_rectangles()
+ or _cairo_surface_set_clip_region() as appropriate.
+
+ * tests/trap-clip.c tests/trap-clip-ref.png tests/Makefile.am:
+ Add a test for trapezoids clipping.
+
+ * doc/public/cairo-docs.xml: Add an index.
+
2005-04-12 Carl Worth <cworth@cworth.org>
* test/translate-show-surface.c: Note that bug is now fixed.
diff --git a/doc/public/cairo-docs.xml b/doc/public/cairo-docs.xml
index a472521d0..172d0d9b4 100644
--- a/doc/public/cairo-docs.xml
+++ b/doc/public/cairo-docs.xml
@@ -24,6 +24,9 @@
<xi:include href="xml/cairo-xcb.xml"/>
<xi:include href="xml/cairo-xlib.xml"/>
</part>
+ <index>
+ <title>Index</title>
+ </index>
</book>
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index d73e66f75..a9d72bb77 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1438,6 +1438,165 @@ _cairo_rectangle_empty (cairo_rectangle_t *rect)
return rect->width == 0 || rect->height == 0;
}
+/* Creates a region from a cairo_rectangle_t */
+static cairo_status_t
+_region_new_from_rect (cairo_rectangle_t *rect,
+ pixman_region16_t **region)
+{
+ *region = pixman_region_create ();
+ if (pixman_region_union_rect (*region, *region,
+ rect->x, rect->y,
+ rect->width, rect->height) != PIXMAN_REGION_STATUS_SUCCESS) {
+ pixman_region_destroy (*region);
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Gets the bounding box of a region as a cairo_rectangle_t */
+static void
+_region_rect_extents (pixman_region16_t *region,
+ cairo_rectangle_t *rect)
+{
+ pixman_box16_t *region_extents = pixman_region_extents (region);
+
+ rect->x = region_extents->x1;
+ rect->y = region_extents->y1;
+ rect->width = region_extents->x2 - region_extents->x1;
+ rect->height = region_extents->y2 - region_extents->y1;
+}
+
+/* Given a region representing a set of trapezoids that will be
+ * drawn, clip the region according to the gstate and compute
+ * the overall extents.
+ */
+static cairo_status_t
+_clip_and_compute_extents_region (cairo_gstate_t *gstate,
+ pixman_region16_t *trap_region,
+ cairo_rectangle_t *extents)
+{
+ if (gstate->clip.region)
+ pixman_region_intersect (trap_region,
+ gstate->clip.region,
+ trap_region);
+
+ if (gstate->clip.surface) {
+ pixman_region16_t *clip_rect;
+ cairo_status_t status;
+
+ status = _region_new_from_rect (&gstate->clip.rect,
+ &clip_rect);
+ if (!CAIRO_OK (status))
+ return status;
+
+ if (pixman_region_intersect (trap_region,
+ clip_rect,
+ trap_region) != PIXMAN_REGION_STATUS_SUCCESS)
+ status = CAIRO_STATUS_NO_MEMORY;
+
+ pixman_region_destroy (clip_rect);
+
+ if (!CAIRO_OK (status))
+ return status;
+ }
+
+ _region_rect_extents (trap_region, extents);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Given a a set of trapezoids to draw, find a bounding box (non-exact)
+ * of the trapezoids clipped by the gstate
+ */
+static cairo_status_t
+_clip_and_compute_extents_arbitrary (cairo_gstate_t *gstate,
+ cairo_traps_t *traps,
+ cairo_rectangle_t *extents)
+{
+ cairo_box_t trap_extents;
+
+ _cairo_traps_extents (traps, &trap_extents);
+ _cairo_box_round_to_rectangle (&trap_extents, extents);
+
+ if (gstate->clip.region) {
+ pixman_region16_t *intersection;
+ cairo_status_t status;
+
+ status = _region_new_from_rect (extents, &intersection);
+ if (!CAIRO_OK (status))
+ return status;
+
+ if (pixman_region_intersect (intersection,
+ gstate->clip.region,
+ intersection) == PIXMAN_REGION_STATUS_SUCCESS)
+ _region_rect_extents (intersection, extents);
+ else
+ status = CAIRO_STATUS_NO_MEMORY;
+
+ pixman_region_destroy (intersection);
+
+ if (!CAIRO_OK (status))
+ return status;
+ }
+
+ if (gstate->clip.surface)
+ _cairo_rectangle_intersect (extents, &gstate->clip.rect);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Composites a region representing a set of trapezoids.
+ */
+static cairo_status_t
+_composite_trap_region (cairo_gstate_t *gstate,
+ cairo_pattern_t *src,
+ cairo_operator_t operator,
+ cairo_surface_t *dst,
+ pixman_region16_t *trap_region,
+ cairo_rectangle_t *extents)
+{
+ cairo_status_t status, tmp_status;
+ cairo_pattern_union_t pattern;
+ cairo_pattern_union_t mask;
+ int num_rects = pixman_region_num_rects (trap_region);
+
+ if (num_rects == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (num_rects > 1) {
+ status = _cairo_surface_set_clip_region (dst, trap_region);
+ if (!CAIRO_OK (status))
+ return status;
+ }
+
+ _cairo_gstate_pattern_init_copy (gstate, &pattern, src);
+ if (gstate->clip.surface)
+ _cairo_pattern_init_for_surface (&mask.surface, gstate->clip.surface);
+
+ status = _cairo_surface_composite (gstate->operator,
+ &pattern.base,
+ gstate->clip.surface ? &mask.base : NULL,
+ dst,
+ extents->x, extents->y,
+ extents->x - (gstate->clip.surface ? gstate->clip.rect.x : 0),
+ extents->y - (gstate->clip.surface ? gstate->clip.rect.y : 0),
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ if (gstate->clip.surface)
+ _cairo_pattern_fini (&pattern.base);
+ _cairo_pattern_fini (&mask.base);
+
+ if (num_rects > 1) {
+ tmp_status = _cairo_surface_set_clip_region (dst, gstate->clip.region);
+ if (!CAIRO_OK (tmp_status))
+ status = tmp_status;
+ }
+
+ return status;
+}
+
static void
translate_traps (cairo_traps_t *traps, int x, int y)
{
@@ -1467,6 +1626,164 @@ translate_traps (cairo_traps_t *traps, int x, int y)
}
}
+/* Composites a set of trapezoids in the case where we need to create
+ * an intermediate surface to handle gstate->clip.surface
+ *
+ * Warning: This call modifies the coordinates of traps
+ */
+static cairo_status_t
+_composite_traps_intermediate_surface (cairo_gstate_t *gstate,
+ cairo_pattern_t *src,
+ cairo_operator_t operator,
+ cairo_surface_t *dst,
+ cairo_traps_t *traps,
+ cairo_rectangle_t *extents)
+{
+ cairo_surface_t *intermediate;
+ cairo_pattern_union_t pattern;
+ cairo_surface_pattern_t intermediate_pattern;
+ cairo_color_t empty_color;
+ cairo_status_t status;
+
+ translate_traps (traps, -extents->x, -extents->y);
+
+ _cairo_color_init (&empty_color);
+ _cairo_color_set_alpha (&empty_color, 0.);
+ intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface,
+ CAIRO_FORMAT_A8,
+ extents->width,
+ extents->height,
+ &empty_color);
+ if (intermediate == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0);
+
+ status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD,
+ &pattern.base,
+ intermediate,
+ extents->x, extents->y,
+ 0, 0,
+ extents->width,
+ extents->height,
+ traps->traps,
+ traps->num_traps);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (!CAIRO_OK (status))
+ goto out;
+
+ _cairo_pattern_init_for_surface (&pattern.surface,
+ gstate->clip.surface);
+
+ status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
+ &pattern.base,
+ NULL,
+ intermediate,
+ extents->x - gstate->clip.rect.x,
+ extents->y - gstate->clip.rect.y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (!CAIRO_OK (status))
+ goto out;
+
+ _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate);
+ _cairo_gstate_pattern_init_copy (gstate, &pattern, src);
+
+ status = _cairo_surface_composite (operator,
+ &pattern.base,
+ &intermediate_pattern.base,
+ dst,
+ extents->x, extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ _cairo_pattern_fini (&pattern.base);
+ _cairo_pattern_fini (&intermediate_pattern.base);
+
+ out:
+ cairo_surface_destroy (intermediate);
+
+ return status;
+}
+
+/* Composites a region representing a set of trapezoids in the
+ * case of a solid source (so we can use
+ * _cairo_surface_fill_rectangles).
+ */
+static cairo_status_t
+_composite_trap_region_solid (cairo_gstate_t *gstate,
+ cairo_solid_pattern_t *src,
+ cairo_operator_t operator,
+ cairo_surface_t *dst,
+ pixman_region16_t *region)
+{
+ cairo_color_t color;
+ int num_rects = pixman_region_num_rects (region);
+ pixman_box16_t *boxes = pixman_region_rects (region);
+ cairo_rectangle_t *rects;
+ cairo_status_t status;
+ int i;
+
+ if (!num_rects)
+ return CAIRO_STATUS_SUCCESS;
+
+ rects = malloc (sizeof (pixman_rectangle_t) * num_rects);
+ if (!rects)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ for (i = 0; i < num_rects; i++) {
+ rects[i].x = boxes[i].x1;
+ rects[i].y = boxes[i].y1;
+ rects[i].width = boxes[i].x2 - boxes[i].x1;
+ rects[i].height = boxes[i].y2 - boxes[i].y1;
+ }
+
+ _cairo_color_init (&color);
+ _cairo_color_set_rgb (&color, src->red, src->green, src->blue);
+ _cairo_color_set_alpha (&color, gstate->alpha);
+
+ status = _cairo_surface_fill_rectangles (dst, operator,
+ &color, rects, num_rects);
+
+ free (rects);
+
+ return status;
+}
+
+/* Composites a set of trapezoids in the general case where
+ gstate->clip.surface == NULL
+ */
+static cairo_status_t
+_composite_traps (cairo_gstate_t *gstate,
+ cairo_pattern_t *src,
+ cairo_operator_t operator,
+ cairo_surface_t *dst,
+ cairo_traps_t *traps,
+ cairo_rectangle_t *extents)
+{
+ cairo_pattern_union_t pattern;
+ cairo_status_t status;
+
+ _cairo_gstate_pattern_init_copy (gstate, &pattern, src);
+
+ status = _cairo_surface_composite_trapezoids (gstate->operator,
+ &pattern.base, dst,
+ extents->x, extents->y,
+ extents->x, extents->y,
+ extents->width,
+ extents->height,
+ traps->traps,
+ traps->num_traps);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ return status;
+}
/* Warning: This call modifies the coordinates of traps */
static cairo_status_t
@@ -1477,154 +1794,71 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate,
cairo_traps_t *traps)
{
cairo_status_t status;
- cairo_pattern_union_t pattern;
+ pixman_region16_t *trap_region;
cairo_rectangle_t extents;
- cairo_box_t trap_extents;
-
+
if (traps->num_traps == 0)
return CAIRO_STATUS_SUCCESS;
if (gstate->surface == NULL)
return CAIRO_STATUS_NO_TARGET_SURFACE;
- _cairo_traps_extents (traps, &trap_extents);
- _cairo_box_round_to_rectangle (&trap_extents, &extents);
-
- if (gstate->clip.surface) {
- cairo_surface_t *intermediate;
- cairo_surface_pattern_t intermediate_pattern;
- cairo_color_t empty_color;
-
- _cairo_rectangle_intersect (&extents, &gstate->clip.rect);
-
- if (_cairo_rectangle_empty (&extents)) {
- status = CAIRO_STATUS_SUCCESS;
- goto BAIL1;
- }
-
- translate_traps (traps, -extents.x, -extents.y);
+ status = _cairo_traps_extract_region (traps, &trap_region);
+ if (!CAIRO_OK (status))
+ return status;
- _cairo_color_init (&empty_color);
- _cairo_color_set_alpha (&empty_color, 0.);
- intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface,
- CAIRO_FORMAT_A8,
- extents.width,
- extents.height,
- &empty_color);
- if (intermediate == NULL) {
- status = CAIRO_STATUS_NO_MEMORY;
- goto BAIL1;
- }
+ if (trap_region)
+ status = _clip_and_compute_extents_region (gstate, trap_region, &extents);
+ else
+ status = _clip_and_compute_extents_arbitrary (gstate, traps, &extents);
- _cairo_pattern_init_solid (&pattern.solid, 1.0, 1.0, 1.0);
-
- status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD,
- &pattern.base,
- intermediate,
- extents.x, extents.y,
- 0, 0,
- extents.width,
- extents.height,
- traps->traps,
- traps->num_traps);
- _cairo_pattern_fini (&pattern.base);
-
- if (status)
- goto BAIL2;
-
-
- _cairo_pattern_init_for_surface (&pattern.surface,
- gstate->clip.surface);
-
- status = _cairo_surface_composite (CAIRO_OPERATOR_IN,
- &pattern.base,
- NULL,
- intermediate,
- extents.x - gstate->clip.rect.x,
- extents.y - gstate->clip.rect.y,
- 0, 0,
- 0, 0,
- extents.width, extents.height);
- _cairo_pattern_fini (&pattern.base);
+ if (!CAIRO_OK (status))
+ goto out;
- if (status)
- goto BAIL2;
-
- _cairo_pattern_init_for_surface (&intermediate_pattern, intermediate);
- _cairo_gstate_pattern_init_copy (gstate, &pattern, src);
-
- status = _cairo_surface_composite (operator,
- &pattern.base,
- &intermediate_pattern.base,
- dst,
- extents.x, extents.y,
- 0, 0,
- extents.x, extents.y,
- extents.width, extents.height);
-
- _cairo_pattern_fini (&pattern.base);
- _cairo_pattern_fini (&intermediate_pattern.base);
-
- BAIL2:
- cairo_surface_destroy (intermediate);
- BAIL1:
+ if (_cairo_rectangle_empty (&extents))
+ /* Nothing to do */
+ goto out;
- if (status)
- return status;
+ if (gstate->clip.surface) {
+ if (trap_region) {
+ /* If we are compositing a set of rectangles, we can set them as the
+ * clip region for the destination surface and use the clip surface
+ * as the mask. A clip region might not be supported, in which case
+ * we fall through to the next method
+ */
+ status = _composite_trap_region (gstate, src, operator, dst,
+ trap_region, &extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto out;
+ }
+ /* Handle a clip surface by creating an intermediate surface. */
+ status = _composite_traps_intermediate_surface (gstate, src, operator,
+ dst, traps, &extents);
} else {
- if (gstate->clip.region) {
- pixman_box16_t box;
- pixman_box16_t *intersection_extents;
- pixman_region16_t *rect, *intersection;
-
- box.x1 = _cairo_fixed_integer_floor (trap_extents.p1.x);
- box.y1 = _cairo_fixed_integer_floor (trap_extents.p1.y);
- box.x2 = _cairo_fixed_integer_ceil (trap_extents.p2.x);
- box.y2 = _cairo_fixed_integer_ceil (trap_extents.p2.y);
-
- rect = pixman_region_create_simple (&box);
- if (rect == NULL)
- goto bail1;
- intersection = pixman_region_create();
- if (intersection == NULL)
- goto bail2;
-
- if (pixman_region_intersect (intersection, gstate->clip.region,
- rect) != PIXMAN_REGION_STATUS_SUCCESS)
- goto bail3;
- intersection_extents = pixman_region_extents (intersection);
-
- extents.x = intersection_extents->x1;
- extents.y = intersection_extents->y1;
- extents.width = intersection_extents->x2 - intersection_extents->x1;
- extents.height = intersection_extents->y2 - intersection_extents->y1;
- bail3:
- pixman_region_destroy (intersection);
- bail2:
- pixman_region_destroy (rect);
- bail1:
- ;
+ /* No clip surface */
+ if (trap_region && src->type == CAIRO_PATTERN_SOLID) {
+ /* Solid rectangles are handled specially */
+ status = _composite_trap_region_solid (gstate, (cairo_solid_pattern_t *)src,
+ operator, dst, trap_region);
+ } else if (trap_region && pixman_region_num_rects (trap_region) <= 1) {
+ /* For a simple rectangle, we can just use composite(), for more
+ * rectangles, we'd have to set a clip region. That might still
+ * be a win, but it's less obvious. (Depends on the backend)
+ */
+ status = _composite_trap_region (gstate, src, operator, dst,
+ trap_region, &extents);
+ } else {
+ status = _composite_traps (gstate, src, operator,
+ dst, traps, &extents);
}
-
- _cairo_gstate_pattern_init_copy (gstate, &pattern, src);
-
- status = _cairo_surface_composite_trapezoids (gstate->operator,
- &pattern.base, dst,
- extents.x, extents.y,
- extents.x, extents.y,
- extents.width,
- extents.height,
- traps->traps,
- traps->num_traps);
-
- _cairo_pattern_fini (&pattern.base);
-
- if (status)
- return status;
}
+
+ out:
+ if (trap_region)
+ pixman_region_destroy (trap_region);
- return CAIRO_STATUS_SUCCESS;
+ return status;
}
cairo_status_t
@@ -1781,40 +2015,6 @@ _cairo_gstate_reset_clip (cairo_gstate_t *gstate)
return CAIRO_STATUS_SUCCESS;
}
-static int
-extract_transformed_rectangle(cairo_matrix_t *mat,
- cairo_traps_t *tr,
- pixman_box16_t *box)
-{
- double a, b, c, d, tx, ty;
-
- cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty);
- if (!(b == 0. && c == 0.))
- return 0;
-
- if (tr->num_traps == 1
- && tr->traps[0].left.p1.x == tr->traps[0].left.p2.x
- && tr->traps[0].right.p1.x == tr->traps[0].right.p2.x
- && tr->traps[0].left.p1.y == tr->traps[0].right.p1.y
- && tr->traps[0].left.p2.y == tr->traps[0].right.p2.y
- && _cairo_fixed_is_integer(tr->traps[0].left.p1.x)
- && _cairo_fixed_is_integer(tr->traps[0].left.p1.y)
- && _cairo_fixed_is_integer(tr->traps[0].left.p2.x)
- && _cairo_fixed_is_integer(tr->traps[0].left.p2.y)
- && _cairo_fixed_is_integer(tr->traps[0].right.p1.x)
- && _cairo_fixed_is_integer(tr->traps[0].right.p1.y)
- && _cairo_fixed_is_integer(tr->traps[0].right.p2.x)
- && _cairo_fixed_is_integer(tr->traps[0].right.p2.y)) {
-
- box->x1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.x);
- box->x2 = (short) _cairo_fixed_integer_part(tr->traps[0].right.p1.x);
- box->y1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.y);
- box->y2 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p2.y);
- return 1;
- }
- return 0;
-}
-
/* Reset surface clip region to the one in the gstate */
cairo_status_t
_cairo_gstate_restore_external_state (cairo_gstate_t *gstate)
@@ -1842,51 +2042,47 @@ _cairo_gstate_clip (cairo_gstate_t *gstate)
cairo_traps_t traps;
cairo_color_t white_color;
cairo_box_t extents;
- pixman_box16_t box;
+ pixman_region16_t *region;
/* Fill the clip region as traps. */
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (&gstate->path, gstate, &traps);
- if (status) {
+ if (!CAIRO_OK (status)) {
_cairo_traps_fini (&traps);
return status;
}
/* Check to see if we can represent these traps as a PixRegion. */
- if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) {
-
- pixman_region16_t *rect = NULL;
- pixman_region16_t *intersection = NULL;
-
+ status = _cairo_traps_extract_region (&traps, &region);
+ if (!CAIRO_OK (status)) {
+ _cairo_traps_fini (&traps);
+ return status;
+ }
+
+ if (region) {
status = CAIRO_STATUS_SUCCESS;
- rect = pixman_region_create_simple (&box);
- if (rect == NULL) {
- status = CAIRO_STATUS_NO_MEMORY;
-
+ if (gstate->clip.region == NULL) {
+ gstate->clip.region = region;
} else {
-
- if (gstate->clip.region == NULL) {
- gstate->clip.region = rect;
- } else {
- intersection = pixman_region_create();
- if (pixman_region_intersect (intersection,
- gstate->clip.region, rect)
- == PIXMAN_REGION_STATUS_SUCCESS) {
- pixman_region_destroy (gstate->clip.region);
- gstate->clip.region = intersection;
- } else {
- status = CAIRO_STATUS_NO_MEMORY;
- }
- pixman_region_destroy (rect);
+ pixman_region16_t *intersection = pixman_region_create();
+
+ if (pixman_region_intersect (intersection,
+ gstate->clip.region, region)
+ == PIXMAN_REGION_STATUS_SUCCESS) {
+ pixman_region_destroy (gstate->clip.region);
+ gstate->clip.region = intersection;
+ } else {
+ status = CAIRO_STATUS_NO_MEMORY;
}
-
- if (!status)
- status = _cairo_surface_set_clip_region (gstate->surface,
- gstate->clip.region);
+ pixman_region_destroy (region);
}
+
+ if (CAIRO_OK (status))
+ status = _cairo_surface_set_clip_region (gstate->surface,
+ gstate->clip.region);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
_cairo_traps_fini (&traps);
diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index 79c7e16b6..f08929221 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -738,3 +738,58 @@ _cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents)
{
*extents = traps->extents;
}
+
+/**
+ * _cairo_traps_extract_region:
+ * @traps: a #cairo_traps_t
+ * @region: on return, %NULL is stored here if the trapezoids aren't
+ * exactly representable as a pixman region, otherwise a
+ * a pointer to such a region, newly allocated.
+ * (free with pixman region destroy)
+ *
+ * Determines if a set of trapezoids are exactly representable as a
+ * pixman region, and if so creates such a region.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
+ **/
+cairo_status_t
+_cairo_traps_extract_region (cairo_traps_t *traps,
+ pixman_region16_t **region)
+{
+ int i;
+
+ 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
+ && traps->traps[i].left.p1.y == traps->traps[i].right.p1.y
+ && traps->traps[i].left.p2.y == traps->traps[i].right.p2.y
+ && _cairo_fixed_is_integer(traps->traps[i].left.p1.x)
+ && _cairo_fixed_is_integer(traps->traps[i].left.p1.y)
+ && _cairo_fixed_is_integer(traps->traps[i].left.p2.x)
+ && _cairo_fixed_is_integer(traps->traps[i].left.p2.y)
+ && _cairo_fixed_is_integer(traps->traps[i].right.p1.x)
+ && _cairo_fixed_is_integer(traps->traps[i].right.p1.y)
+ && _cairo_fixed_is_integer(traps->traps[i].right.p2.x)
+ && _cairo_fixed_is_integer(traps->traps[i].right.p2.y))) {
+ *region = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ *region = pixman_region_create ();
+
+ for (i = 0; i < traps->num_traps; i++) {
+ int x = _cairo_fixed_integer_part(traps->traps[i].left.p1.x);
+ int y = _cairo_fixed_integer_part(traps->traps[i].left.p1.y);
+ int width = _cairo_fixed_integer_part(traps->traps[i].right.p1.x) - x;
+ int height = _cairo_fixed_integer_part(traps->traps[i].left.p2.y) - y;
+
+ if (pixman_region_union_rect (*region, *region,
+ x, y, width, height) != PIXMAN_REGION_STATUS_SUCCESS) {
+ pixman_region_destroy (*region);
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
diff --git a/src/cairoint.h b/src/cairoint.h
index 673071d5a..501f49ea3 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1625,6 +1625,10 @@ _cairo_traps_contain (cairo_traps_t *traps, double x, double y);
cairo_private void
_cairo_traps_extents (cairo_traps_t *traps, cairo_box_t *extents);
+cairo_private cairo_status_t
+_cairo_traps_extract_region (cairo_traps_t *tr,
+ pixman_region16_t **region);
+
/* cairo_slope.c */
cairo_private void
_cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b);
diff --git a/test/Makefile.am b/test/Makefile.am
index 3e5a86b0e..422327964 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -15,6 +15,7 @@ text-cache-crash \
text-rotate \
transforms \
translate-show-surface \
+trap-clip \
user-data
# And all new tests go here too. I really don't like having to repeat
@@ -33,7 +34,8 @@ path-data-ref.png \
pixman-rotate-ref.png \
romedalen.png \
transforms-ref.png \
-translate-show-surface-ref.png
+translate-show-surface-ref.png \
+trap-clip-ref.png
# Once we can draw the text-rotate.c test case correctly, we should
# create and add text-rotate-ref.png to the list of reference PNGs.
@@ -92,6 +94,7 @@ text_cache_crash_LDADD = $(LDADDS)
text_rotate_LDADD = $(LDADDS)
transforms_LDADD = $(LDADDS)
translate_show_surface_LDADD = $(LDADDS)
+trap_clip_LDADD = $(LDADDS)
user_data_LDADD = $(LDADDS)
noinst_PROGRAMS = imagediff
diff --git a/test/trap-clip-ref.png b/test/trap-clip-ref.png
new file mode 100644
index 000000000..9b5b317aa
--- /dev/null
+++ b/test/trap-clip-ref.png
Binary files differ
diff --git a/test/trap-clip.c b/test/trap-clip.c
new file mode 100644
index 000000000..4bc6f195a
--- /dev/null
+++ b/test/trap-clip.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Kristian Høgsberg <krh@redhat.com>
+ */
+
+#include <math.h>
+#include "cairo-test.h"
+#include <stdio.h>
+
+#define WIDTH 64
+#define HEIGHT 64
+#define PAD 10
+
+const char png_filename[] = "romedalen.png";
+
+static void
+set_solid_pattern (cairo_t *cr, int x, int y)
+{
+ cairo_set_rgb_color (cr, 0, 0, 0.6);
+ cairo_set_alpha (cr, 1.0);
+}
+
+static void
+set_translucent_pattern (cairo_t *cr, int x, int y)
+{
+ cairo_set_rgb_color (cr, 0, 0, 0.6);
+ cairo_set_alpha (cr, 0.5);
+}
+
+static void
+set_gradient_pattern (cairo_t *cr, int x, int y)
+{
+ cairo_pattern_t *pattern;
+
+ pattern =
+ cairo_pattern_create_linear (x, y, x + WIDTH, y + HEIGHT);
+ cairo_pattern_add_color_stop (pattern, 0, 1, 1, 1, 1);
+ cairo_pattern_add_color_stop (pattern, 1, 0, 0, 0.4, 1);
+ cairo_set_pattern (cr, pattern);
+ cairo_set_alpha (cr, 1);
+}
+
+static void
+set_image_pattern (cairo_t *cr, int x, int y)
+{
+ cairo_pattern_t *pattern;
+
+ pattern = cairo_test_create_png_pattern (cr, png_filename);
+ cairo_set_pattern (cr, pattern);
+ cairo_set_alpha (cr, 1);
+}
+
+static void
+draw_rect (cairo_t *cr, int x, int y)
+{
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
+ cairo_fill (cr);
+}
+
+static void
+draw_rects (cairo_t *cr, int x, int y)
+{
+ int width = WIDTH / 3;
+ int height = HEIGHT / 2;
+
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_rectangle (cr, x + width, y + height, width, height);
+ cairo_rectangle (cr, x + 2 * width, y, width, height);
+ cairo_fill (cr);
+}
+
+static void
+draw_polygon (cairo_t *cr, int x, int y)
+{
+ cairo_new_path (cr);
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x, y + HEIGHT);
+ cairo_line_to (cr, x + WIDTH / 2, y + 3 * HEIGHT / 4);
+ cairo_line_to (cr, x + WIDTH, y + HEIGHT);
+ cairo_line_to (cr, x + WIDTH, y);
+ cairo_line_to (cr, x + WIDTH / 2, y + HEIGHT / 4);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+}
+
+static void
+clip_none (cairo_t *cr, int x, int y)
+{
+}
+
+static void
+clip_rect (cairo_t *cr, int x, int y)
+{
+ cairo_set_alpha (cr, 1.0);
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x + (int)WIDTH / 6, y + (int)HEIGHT / 6,
+ 4 * ((int)WIDTH / 6), 4 * ((int)WIDTH / 6));
+ cairo_clip (cr);
+ cairo_new_path (cr);
+}
+
+static void
+clip_rects (cairo_t *cr, int x, int y)
+{
+ int height = HEIGHT / 3;
+
+ cairo_set_alpha (cr, 1.0);
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x, y, WIDTH, height);
+ cairo_rectangle (cr, x, y + 2 * height, WIDTH, height);
+ cairo_clip (cr);
+ cairo_new_path (cr);
+}
+
+static void
+clip_circle (cairo_t *cr, int x, int y)
+{
+ cairo_set_alpha (cr, 1.0);
+ cairo_new_path (cr);
+ cairo_arc (cr, x + WIDTH / 2, y + HEIGHT / 2,
+ WIDTH / 3, 0, 2 * M_PI);
+ cairo_clip (cr);
+ cairo_new_path (cr);
+}
+
+static void (*pattern_funcs[])(cairo_t *cr, int x, int y) = {
+ set_solid_pattern,
+ set_translucent_pattern,
+ set_gradient_pattern,
+ set_image_pattern,
+};
+
+static void (*draw_funcs[])(cairo_t *cr, int x, int y) = {
+ draw_rect,
+ draw_rects,
+ draw_polygon,
+};
+
+static void (*clip_funcs[])(cairo_t *cr, int x, int y) = {
+ clip_none,
+ clip_rect,
+ clip_rects,
+ clip_circle,
+};
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+#define IMAGE_WIDTH (ARRAY_SIZE (pattern_funcs) * (WIDTH + PAD) + PAD)
+#define IMAGE_HEIGHT (ARRAY_SIZE (draw_funcs) * ARRAY_SIZE (clip_funcs) * (HEIGHT + PAD) + PAD)
+
+static cairo_test_t test = {
+ "trap-clip",
+ "Trapezoid clipping",
+ IMAGE_WIDTH, IMAGE_HEIGHT
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+ int i, j, k, x, y;
+
+ for (k = 0; k < ARRAY_SIZE (clip_funcs); k++) {
+ for (j = 0; j < ARRAY_SIZE (draw_funcs); j++) {
+ for (i = 0; i < ARRAY_SIZE (pattern_funcs); i++) {
+ x = i * (WIDTH + PAD) + PAD;
+ y = (ARRAY_SIZE (draw_funcs) * k + j) * (HEIGHT + PAD) + PAD;
+
+ cairo_save (cr);
+
+ cairo_move_to (cr, x, y);
+ clip_funcs[k] (cr, x, y);
+ pattern_funcs[i] (cr, x, y);
+ draw_funcs[j] (cr, x, y);
+ if (cairo_status (cr))
+ fprintf (stderr, "%d %d HERE!\n", i, j);
+
+ cairo_restore (cr);
+ }
+ }
+ }
+
+ if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+ fprintf (stderr, "%d %d .HERE!\n", i, j);
+
+ return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+ return cairo_test (&test, draw);
+}