summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@redhat.com>2005-06-14 19:45:22 +0000
committerKristian Høgsberg <krh@redhat.com>2005-06-14 19:45:22 +0000
commit476fe9a66e2be74e65fe2f4e42bda5f4c8b244a0 (patch)
tree4d83e890d040cc75d7205089f99f49df0a1a798c
parent663e39a63c6ae223dc3095d29fd95bc8885c74f2 (diff)
Implement path clipping and refactor _cairo_gstate_clip() out in three different functions corresponding to the three different clipping modes.
Add NULL pointers for intersect_clip_path. New test case to exercise PDF clipping code.
-rw-r--r--ChangeLog36
-rw-r--r--src/cairo-glitz-surface.c1
-rw-r--r--src/cairo-gstate-private.h16
-rw-r--r--src/cairo-gstate.c222
-rw-r--r--src/cairo-image-surface.c1
-rw-r--r--src/cairo-pdf-surface.c101
-rw-r--r--src/cairo-ps-surface.c1
-rw-r--r--src/cairo-quartz-surface.c1
-rw-r--r--src/cairo-surface.c116
-rw-r--r--src/cairo-win32-surface.c1
-rw-r--r--src/cairo-xcb-surface.c1
-rw-r--r--src/cairo-xlib-surface.c1
-rw-r--r--src/cairoint.h71
-rw-r--r--test/Makefile.am11
-rw-r--r--test/pdf-clip.c134
15 files changed, 583 insertions, 131 deletions
diff --git a/ChangeLog b/ChangeLog
index 57876fd2..b91fe2bf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+2005-06-14 Kristian Høgsberg <krh@redhat.com>
+
+ * src/cairo-gstate-private.h:
+ * src/cairo-gstate.c: (_cairo_gstate_init),
+ (_cairo_gstate_init_copy), (_cairo_gstate_fini),
+ (_cairo_gstate_set_clip), (_composite_trap_region),
+ (_cairo_gstate_fill), (_cairo_gstate_reset_clip),
+ (_cairo_gstate_intersect_clip_path), (_cairo_clip_path_reference),
+ (_cairo_clip_path_destroy), (_cairo_gstate_intersect_clip_region),
+ (_cairo_gstate_intersect_clip_mask), (_cairo_gstate_clip):
+ * src/cairo-pdf-surface.c:
+ (_cairo_pdf_surface_create_for_document),
+ (_cairo_pdf_path_move_to), (_cairo_pdf_path_line_to),
+ (_cairo_pdf_path_close_path), (_cairo_pdf_surface_fill_path),
+ (_cairo_pdf_surface_intersect_clip_path),
+ (_cairo_pdf_document_add_page):
+ * src/cairo-surface.c: (_cairo_surface_get_clip_mode),
+ (_cairo_surface_fill_path), (_cairo_surface_reset_clip),
+ (_cairo_surface_set_clip_path_recursive),
+ (_cairo_surface_set_clip_path):
+ * src/cairoint.h: Implement path clipping and refactor
+ _cairo_gstate_clip() out in three different functions
+ corresponding to the three different clipping modes.
+
+ * src/cairo-glitz-surface.c:
+ * src/cairo-ps-surface.c:
+ * src/cairo-win32-surface.c:
+ * src/cairo-xcb-surface.c:
+ * src/cairo-xlib-surface.c:
+ * src/cairo-image-surface.c:
+ * src/cairo-quartz-surface.c: Add NULL pointers for
+ intersect_clip_path.
+
+ * test/Makefile.am:
+ * test/pdf-clip.c: New test case to exercise PDF clipping code.
+
2005-06-14 Carl Worth <cworth@cworth.org>
* src/cairo-glitz-surface.c: (_cairo_glitz_surface_create_similar),
diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c
index 8c97699c..76bb25ae 100644
--- a/src/cairo-glitz-surface.c
+++ b/src/cairo-glitz-surface.c
@@ -2132,6 +2132,7 @@ static const cairo_surface_backend_t cairo_glitz_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
_cairo_glitz_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_glitz_surface_get_extents,
_cairo_glitz_surface_show_glyphs
};
diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h
index 8a744856..7e2c8597 100644
--- a/src/cairo-gstate-private.h
+++ b/src/cairo-gstate-private.h
@@ -36,7 +36,19 @@
#ifndef CAIRO_GSTATE_PRIVATE_H
#define CAIRO_GSTATE_PRIVATE_H
+#include "cairo-path-fixed-private.h"
+
+struct _cairo_clip_path {
+ unsigned int ref_count;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_clip_path_t *prev;
+};
+
typedef struct _cairo_clip {
+ cairo_clip_mode_t mode;
+
/*
* Mask-based clipping for cases where the backend
* clipping isn't sufficiently able.
@@ -59,8 +71,10 @@ typedef struct _cairo_clip {
*/
pixman_region16_t *region;
/*
- * XXX add clip paths here
+ * If the surface supports path clipping, we store the list of
+ * clipping paths that has been set here as a linked list.
*/
+ cairo_clip_path_t *path;
} cairo_clip_t;
struct _cairo_gstate {
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index 90ef78f5..7cbe192e 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -70,6 +70,12 @@ _cairo_gstate_unset_font (cairo_gstate_t *gstate);
static void
_cairo_rectangle_intersect (cairo_rectangle_t *dest, cairo_rectangle_t *src);
+static void
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path);
+
+static void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
+
cairo_gstate_t *
_cairo_gstate_create (cairo_surface_t *target)
{
@@ -116,9 +122,11 @@ _cairo_gstate_init (cairo_gstate_t *gstate,
CAIRO_GSTATE_DEFAULT_FONT_SIZE,
CAIRO_GSTATE_DEFAULT_FONT_SIZE);
+ gstate->clip.mode = _cairo_surface_get_clip_mode (target);
gstate->clip.region = NULL;
gstate->clip.surface = NULL;
gstate->clip.serial = 0;
+ gstate->clip.path = NULL;
_cairo_gstate_identity_matrix (gstate);
@@ -168,6 +176,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
cairo_surface_reference (gstate->target);
cairo_surface_reference (gstate->clip.surface);
+ _cairo_clip_path_reference (gstate->clip.path);
cairo_pattern_reference (gstate->source);
@@ -205,6 +214,10 @@ _cairo_gstate_fini (cairo_gstate_t *gstate)
cairo_surface_destroy (gstate->clip.surface);
gstate->clip.surface = NULL;
+ if (gstate->clip.path)
+ _cairo_clip_path_destroy (gstate->clip.path);
+ gstate->clip.path = NULL;
+
if (gstate->clip.region)
pixman_region_destroy (gstate->clip.region);
gstate->clip.region = NULL;
@@ -338,7 +351,10 @@ _cairo_gstate_set_clip (cairo_gstate_t *gstate)
if (gstate->clip.serial == _cairo_surface_get_current_clip_serial (surface))
return CAIRO_STATUS_SUCCESS;
- /* check for path clipping here */
+ if (gstate->clip.path)
+ return _cairo_surface_set_clip_path (surface,
+ gstate->clip.path,
+ gstate->clip.serial);
if (gstate->clip.region)
return _cairo_surface_set_clip_region (surface,
@@ -1142,9 +1158,8 @@ _composite_trap_region (cairo_gstate_t *gstate,
if (num_rects > 1) {
- status = _cairo_surface_can_clip_region (gstate->target);
- if (status)
- return status;
+ if (gstate->clip.mode != CAIRO_CLIP_MODE_REGION)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
clip_serial = _cairo_surface_allocate_clip_serial (gstate->target);
status = _cairo_surface_set_clip_region (gstate->target,
@@ -1438,7 +1453,9 @@ _cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
status = _cairo_surface_fill_path (gstate->operator,
gstate->source,
gstate->target,
- path);
+ path,
+ gstate->fill_rule,
+ gstate->tolerance);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
@@ -1583,83 +1600,124 @@ _cairo_gstate_reset_clip (cairo_gstate_t *gstate)
if (gstate->clip.region)
pixman_region_destroy (gstate->clip.region);
gstate->clip.region = NULL;
+
+ if (gstate->clip.path)
+ _cairo_clip_path_destroy (gstate->clip.path);
+ gstate->clip.path = NULL;
+
gstate->clip.serial = 0;
return CAIRO_STATUS_SUCCESS;
}
-cairo_status_t
-_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+static cairo_status_t
+_cairo_gstate_intersect_clip_path (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path)
{
+ cairo_clip_path_t *clip_path;
cairo_status_t status;
- cairo_pattern_union_t pattern;
- cairo_traps_t traps;
- cairo_rectangle_t surface_rect;
- cairo_box_t extents;
- cairo_surface_t *surface;
- pixman_region16_t *region;
- /* Fill the clip region as traps. */
+ if (gstate->clip.mode != CAIRO_CLIP_MODE_PATH)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
- _cairo_traps_init (&traps);
- status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
- if (status) {
- _cairo_traps_fini (&traps);
+ clip_path = malloc (sizeof (cairo_clip_path_t));
+ if (clip_path == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path, path);
+ if (status)
return status;
- }
- status = _cairo_surface_can_clip_region (gstate->target);
-
- if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
- if (status)
- return status;
-
- /* Check to see if we can represent these traps as a PixRegion. */
+ clip_path->ref_count = 1;
+ clip_path->fill_rule = gstate->fill_rule;
+ clip_path->tolerance = gstate->tolerance;
+ clip_path->prev = gstate->clip.path;
+ gstate->clip.path = clip_path;
+ gstate->clip.serial = _cairo_surface_allocate_clip_serial (gstate->target);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
+{
+ if (clip_path == NULL)
+ return;
+
+ clip_path->ref_count++;
+}
+
+static void
+_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
+{
+ if (clip_path == NULL)
+ return;
+
+ clip_path->ref_count--;
+ if (clip_path->ref_count)
+ return;
+
+ _cairo_path_fixed_fini (&clip_path->path);
+ _cairo_clip_path_destroy (clip_path->prev);
+ free (clip_path);
+}
+
+static cairo_status_t
+_cairo_gstate_intersect_clip_region (cairo_gstate_t *gstate,
+ cairo_traps_t *traps)
+{
+ pixman_region16_t *region;
+ cairo_status_t status;
+
+ if (gstate->clip.mode != CAIRO_CLIP_MODE_REGION)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
- status = _cairo_traps_extract_region (&traps, &region);
- if (status) {
- _cairo_traps_fini (&traps);
- return status;
- }
+ status = _cairo_traps_extract_region (traps, &region);
+ if (status)
+ return status;
- if (region) {
- status = CAIRO_STATUS_SUCCESS;
-
- if (gstate->clip.region == NULL) {
- gstate->clip.region = region;
- } else {
- 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;
- }
- pixman_region_destroy (region);
- }
- gstate->clip.serial = _cairo_surface_allocate_clip_serial (gstate->target);
-
- _cairo_traps_fini (&traps);
-
- return status;
+ if (region == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (gstate->clip.region == NULL) {
+ gstate->clip.region = region;
+ } else {
+ 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;
}
+ pixman_region_destroy (region);
}
+ gstate->clip.serial = _cairo_surface_allocate_clip_serial (gstate->target);
+ return status;
+}
- /* Otherwise 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. */
+static cairo_status_t
+_cairo_gstate_intersect_clip_mask (cairo_gstate_t *gstate,
+ cairo_traps_t *traps)
+{
+ cairo_pattern_union_t pattern;
+ cairo_box_t extents;
+ cairo_rectangle_t surface_rect;
+ cairo_surface_t *surface;
+ cairo_status_t status;
- if (gstate->clip.surface == NULL) {
- _cairo_traps_extents (&traps, &extents);
- _cairo_box_round_to_rectangle (&extents, &surface_rect);
- } else {
- _cairo_traps_extents (&traps, &extents);
- _cairo_box_round_to_rectangle (&extents, &surface_rect);
+ /* 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. */
+
+ _cairo_traps_extents (traps, &extents);
+ _cairo_box_round_to_rectangle (&extents, &surface_rect);
+
+ if (gstate->clip.surface != NULL)
_cairo_rectangle_intersect (&surface_rect, &gstate->clip.surface_rect);
- }
surface = _cairo_surface_create_similar_solid (gstate->target,
CAIRO_FORMAT_A8,
@@ -1671,7 +1729,7 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
/* Render the new clipping path into the new mask surface. */
- translate_traps (&traps, -surface_rect.x, -surface_rect.y);
+ translate_traps (traps, -surface_rect.x, -surface_rect.y);
_cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE);
status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_IN,
@@ -1681,11 +1739,10 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
0, 0,
surface_rect.width,
surface_rect.height,
- traps.traps,
- traps.num_traps);
+ traps->traps,
+ traps->num_traps);
_cairo_pattern_fini (&pattern.base);
- _cairo_traps_fini (&traps);
if (status) {
cairo_surface_destroy (surface);
@@ -1723,7 +1780,34 @@ _cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
gstate->clip.surface = surface;
gstate->clip.surface_rect = surface_rect;
- return CAIRO_STATUS_SUCCESS;
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+ cairo_traps_t traps;
+
+ status = _cairo_gstate_intersect_clip_path (gstate, path);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ _cairo_traps_init (&traps);
+ status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
+ if (status)
+ goto bail;
+
+ status = _cairo_gstate_intersect_clip_region (gstate, &traps);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto bail;
+
+ status = _cairo_gstate_intersect_clip_mask (gstate, &traps);
+
+ bail:
+ _cairo_traps_fini (&traps);
+
+ return status;
}
static void
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 89e53e6f..6b509d6d 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -713,6 +713,7 @@ static const cairo_surface_backend_t cairo_image_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
_cairo_image_abstract_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_image_abstract_surface_get_extents,
NULL /* show_glyphs */
};
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 8b4b4ff5..52e2b419 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -188,6 +188,7 @@ struct cairo_pdf_surface {
cairo_array_t streams;
cairo_array_t alphas;
cairo_array_t fonts;
+ cairo_bool_t has_clip;
};
#define DEFAULT_DPI 300
@@ -989,6 +990,7 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document,
_cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&surface->alphas, sizeof (double));
_cairo_array_init (&surface->fonts, sizeof (cairo_pdf_resource_t));
+ surface->has_clip = FALSE;
return &surface->base;
}
@@ -1638,6 +1640,7 @@ intersect (cairo_line_t *line, cairo_fixed_t y)
typedef struct
{
cairo_output_stream_t *output_stream;
+ cairo_bool_t has_current_point;
} pdf_path_info_t;
static cairo_status_t
@@ -1649,6 +1652,7 @@ _cairo_pdf_path_move_to (void *closure, cairo_point_t *point)
"%f %f m ",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
+ info->has_current_point = TRUE;
return CAIRO_STATUS_SUCCESS;
}
@@ -1657,11 +1661,19 @@ static cairo_status_t
_cairo_pdf_path_line_to (void *closure, cairo_point_t *point)
{
pdf_path_info_t *info = closure;
+ const char *pdf_operator;
+
+ if (info->has_current_point)
+ pdf_operator = "l";
+ else
+ pdf_operator = "m";
_cairo_output_stream_printf (info->output_stream,
- "%f %f l ",
+ "%f %f %s ",
_cairo_fixed_to_double (point->x),
- _cairo_fixed_to_double (point->y));
+ _cairo_fixed_to_double (point->y),
+ pdf_operator);
+ info->has_current_point = TRUE;
return CAIRO_STATUS_SUCCESS;
}
@@ -1693,6 +1705,7 @@ _cairo_pdf_path_close_path (void *closure)
_cairo_output_stream_printf (info->output_stream,
"h\r\n");
+ info->has_current_point = FALSE;
return CAIRO_STATUS_SUCCESS;
}
@@ -1701,15 +1714,16 @@ static cairo_int_status_t
_cairo_pdf_surface_fill_path (cairo_operator_t operator,
cairo_pattern_t *pattern,
void *abstract_dst,
- cairo_path_fixed_t *path)
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
{
cairo_pdf_surface_t *surface = abstract_dst;
cairo_pdf_document_t *document = surface->document;
+ const char *pdf_operator;
cairo_status_t status;
pdf_path_info_t info;
- return CAIRO_INT_STATUS_UNSUPPORTED;
-
emit_pattern (surface, pattern);
/* After the above switch the current stream should belong to this
@@ -1718,6 +1732,7 @@ _cairo_pdf_surface_fill_path (cairo_operator_t operator,
document->current_stream == surface->current_stream);
info.output_stream = document->output_stream;
+ info.has_current_point = FALSE;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
@@ -1727,8 +1742,20 @@ _cairo_pdf_surface_fill_path (cairo_operator_t operator,
_cairo_pdf_path_close_path,
&info);
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ pdf_operator = "f";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ pdf_operator = "f*";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
_cairo_output_stream_printf (document->output_stream,
- "f\r\n");
+ "%s\r\n",
+ pdf_operator);
return status;
}
@@ -1905,6 +1932,62 @@ _cairo_pdf_surface_show_glyphs (cairo_scaled_font_t *scaled_font,
return CAIRO_STATUS_SUCCESS;
}
+static cairo_int_status_t
+_cairo_pdf_surface_intersect_clip_path (void *dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
+{
+ cairo_pdf_surface_t *surface = dst;
+ cairo_pdf_document_t *document = surface->document;
+ cairo_output_stream_t *output = document->output_stream;
+ cairo_status_t status;
+ pdf_path_info_t info;
+ const char *pdf_operator;
+
+ _cairo_pdf_surface_ensure_stream (surface);
+
+ if (path == NULL) {
+ if (surface->has_clip)
+ _cairo_output_stream_printf (output, "Q\r\n");
+ surface->has_clip = FALSE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (!surface->has_clip) {
+ _cairo_output_stream_printf (output, "q ");
+ surface->has_clip = TRUE;
+ }
+
+ info.output_stream = document->output_stream;
+ info.has_current_point = FALSE;
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_pdf_path_move_to,
+ _cairo_pdf_path_line_to,
+ _cairo_pdf_path_curve_to,
+ _cairo_pdf_path_close_path,
+ &info);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ pdf_operator = "W";
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ pdf_operator = "W*";
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_output_stream_printf (document->output_stream,
+ "%s n\r\n",
+ pdf_operator);
+
+ return status;
+}
+
static const cairo_surface_backend_t cairo_pdf_surface_backend = {
_cairo_pdf_surface_create_similar,
_cairo_pdf_surface_finish,
@@ -1919,6 +2002,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = {
_cairo_pdf_surface_copy_page,
_cairo_pdf_surface_show_page,
NULL, /* set_clip_region */
+ _cairo_pdf_surface_intersect_clip_path,
_cairo_pdf_surface_get_extents,
_cairo_pdf_surface_show_glyphs,
_cairo_pdf_surface_fill_path
@@ -2237,6 +2321,11 @@ _cairo_pdf_document_add_page (cairo_pdf_document_t *document,
assert (!document->finished);
+ _cairo_pdf_surface_ensure_stream (surface);
+
+ if (surface->has_clip)
+ _cairo_output_stream_printf (output, "Q\r\n");
+
_cairo_pdf_document_close_stream (document);
page_id = _cairo_pdf_document_new_object (document);
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index da27108e..dd12c2ca 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -372,6 +372,7 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = {
_cairo_ps_surface_copy_page,
_cairo_ps_surface_show_page,
_cairo_ps_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_ps_surface_get_extents,
NULL /* show_glyphs */
};
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index b7ea0d60..56684251 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -226,6 +226,7 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
_cairo_quartz_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_quartz_surface_get_extents,
NULL /* show_glyphs */
};
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 870d4c21..c6424b3b 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -38,7 +38,7 @@
#include <stdlib.h>
#include "cairoint.h"
-
+#include "cairo-gstate-private.h"
void
_cairo_surface_init (cairo_surface_t *surface,
@@ -115,6 +115,17 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other,
return surface;
}
+cairo_clip_mode_t
+_cairo_surface_get_clip_mode (cairo_surface_t *surface)
+{
+ if (surface->backend->intersect_clip_path != NULL)
+ return CAIRO_CLIP_MODE_PATH;
+ else if (surface->backend->set_clip_region != NULL)
+ return CAIRO_CLIP_MODE_REGION;
+ else
+ return CAIRO_CLIP_MODE_MASK;
+}
+
void
cairo_surface_reference (cairo_surface_t *surface)
{
@@ -644,13 +655,16 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface,
}
cairo_private cairo_int_status_t
-_cairo_surface_fill_path (cairo_operator_t operator,
- cairo_pattern_t *pattern,
- cairo_surface_t *dst,
- cairo_path_fixed_t *path)
+_cairo_surface_fill_path (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance)
{
if (dst->backend->fill_path)
- return dst->backend->fill_path (operator, pattern, dst, path);
+ return dst->backend->fill_path (operator, pattern, dst, path,
+ fill_rule, tolerance);
else
return CAIRO_INT_STATUS_UNSUPPORTED;
}
@@ -837,13 +851,16 @@ _cairo_surface_reset_clip (cairo_surface_t *surface)
return CAIRO_STATUS_SURFACE_FINISHED;
surface->current_clip_serial = 0;
-#if 0
- if (surface->backend->clip_path) {
- status = surface->backend->clip_path (surface, NULL);
+
+ if (surface->backend->intersect_clip_path) {
+ status = surface->backend->intersect_clip_path (surface,
+ NULL,
+ CAIRO_FILL_RULE_WINDING,
+ 0);
if (status)
return status;
}
-#endif
+
if (surface->backend->set_clip_region != NULL) {
status = surface->backend->set_clip_region (surface, NULL);
if (status)
@@ -853,23 +870,6 @@ _cairo_surface_reset_clip (cairo_surface_t *surface)
}
/**
- * _cairo_surface_can_clip_region:
- * @surface: the #cairo_surface_t to check for region clipping support
- *
- * This function checks whether the specified surface can
- * support region-based clipping.
- */
-cairo_private cairo_status_t
-_cairo_surface_can_clip_region (cairo_surface_t *surface)
-{
- if (surface->finished)
- return CAIRO_STATUS_SURFACE_FINISHED;
- if (surface->backend->set_clip_region == NULL)
- return CAIRO_INT_STATUS_UNSUPPORTED;
- return CAIRO_STATUS_SUCCESS;
-}
-
-/**
* _cairo_surface_set_clip_region:
* @surface: the #cairo_surface_t to reset the clip on
* @region: the #pixman_region16_t to use for clipping
@@ -893,22 +893,64 @@ _cairo_surface_set_clip_region (cairo_surface_t *surface,
return surface->backend->set_clip_region (surface, region);
}
-#if 0
-/* new interfaces for path-based clipping */
-cairo_private cairo_status_t
-_cairo_surface_can_clip_path (cairo_surface_t *surface)
+static cairo_status_t
+_cairo_surface_set_clip_path_recursive (cairo_surface_t *surface,
+ cairo_clip_path_t *clip_path)
{
+ cairo_status_t status;
+
+ if (clip_path == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_surface_set_clip_path_recursive (surface, clip_path->prev);
+ if (status)
+ return status;
+
+ return surface->backend->intersect_clip_path (surface,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance);
}
+
+/**
+ * _cairo_surface_set_clip_path:
+ * @surface: the #cairo_surface_t to reset the clip on
+ * @path: the path to intersect against the current clipping path
+ * @fill_rule: fill rule to use for clipping
+ * @tolerance: tesselation to use for tesselating clipping path
+ * @serial: the clip serial number associated with the region
+ *
+ * Sets the clipping path to be the intersection of the current
+ * clipping path of the surface and the given path.
+ **/
cairo_private cairo_status_t
-_cairo_surface_clip_path (cairo_surface_t *surface,
- cairo_path_fixed_t *path,
- unsigned int serial)
+_cairo_surface_set_clip_path (cairo_surface_t *surface,
+ cairo_clip_path_t *clip_path,
+ unsigned int serial)
{
- surface->current_clip_serial = clip_serial;
- return surface->backend->clip_path (surface, path);
+ cairo_status_t status;
+
+ if (surface->finished)
+ return CAIRO_STATUS_SURFACE_FINISHED;
+
+ assert (surface->backend->intersect_clip_path != NULL);
+
+ status = surface->backend->intersect_clip_path (surface,
+ NULL,
+ CAIRO_FILL_RULE_WINDING,
+ 0);
+ if (status)
+ return status;
+
+ status = _cairo_surface_set_clip_path_recursive (surface, clip_path);
+ if (status)
+ return status;
+
+ surface->current_clip_serial = serial;
+
+ return CAIRO_STATUS_SUCCESS;
}
-#endif
/**
* _cairo_surface_get_extents:
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 4f1a97a7..8d63adbf 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -941,6 +941,7 @@ static const cairo_surface_backend_t cairo_win32_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
_cairo_win32_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_win32_surface_get_extents,
NULL /* show_glyphs */
};
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index 26b82c67..fa99e160 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -1012,6 +1012,7 @@ static const cairo_surface_backend_t cairo_xcb_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
NULL, /* _cairo_xcb_surface_set_clip_region */
+ NULL, /* intersect_clip_path */
_cairo_xcb_surface_get_extents,
NULL /* show_glyphs */
};
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 2eb778e4..5603c603 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1046,6 +1046,7 @@ static const cairo_surface_backend_t cairo_xlib_surface_backend = {
NULL, /* copy_page */
NULL, /* show_page */
_cairo_xlib_surface_set_clip_region,
+ NULL, /* intersect_clip_path */
_cairo_xlib_surface_get_extents,
_cairo_xlib_surface_show_glyphs
};
diff --git a/src/cairoint.h b/src/cairoint.h
index 95e778a0..77d387ba 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -642,10 +642,43 @@ typedef struct _cairo_surface_backend {
cairo_int_status_t
(*show_page) (void *surface);
+ /* Set given region as the clip region for the surface, replacing
+ * any previously set clip region. Passing in a NULL region will
+ * clear the surface clip region.
+ *
+ * The surface is expected to store the clip region and clip all
+ * following drawing operations against it until the clip region
+ * is cleared of replaced by another clip region.
+ *
+ * Cairo will call this function whenever a clip path can be
+ * represented as a device pixel aligned set of rectangles. When
+ * this is not possible, cairo will use mask surfaces for
+ * clipping.
+ */
cairo_int_status_t
(*set_clip_region) (void *surface,
pixman_region16_t *region);
+ /* Intersect the given path against the clip path currently set in
+ * the surface, using the given fill_rule and tolerance, and set
+ * the result as the new clipping path for the surface. Passing
+ * in a NULL path will clear the surface clipping path.
+ *
+ * The surface is expected to store the resulting clip path and
+ * clip all following drawing operations against it until the clip
+ * path cleared or intersected with a new path.
+ *
+ * If a surface implements this function, set_clip_region() will
+ * never be called and should not be implemented. If this
+ * function is not implemented cairo will use set_clip_region()
+ * (if available) and mask surfaces for clipping.
+ */
+ cairo_int_status_t
+ (*intersect_clip_path) (void *dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance);
+
/* Get the extents of the current surface. For many surface types
* this will be as simple as { x=0, y=0, width=surface->width,
* height=surface->height}.
@@ -683,7 +716,9 @@ typedef struct _cairo_surface_backend {
(*fill_path) (cairo_operator_t operator,
cairo_pattern_t *pattern,
void *dst,
- cairo_path_fixed_t *path);
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance);
} cairo_surface_backend_t;
@@ -695,6 +730,12 @@ typedef struct _cairo_format_masks {
unsigned long blue_mask;
} cairo_format_masks_t;
+typedef enum _cairo_clip_mode {
+ CAIRO_CLIP_MODE_PATH,
+ CAIRO_CLIP_MODE_REGION,
+ CAIRO_CLIP_MODE_MASK
+} cairo_clip_mode_t;
+
struct _cairo_surface {
const cairo_surface_backend_t *backend;
@@ -1381,6 +1422,9 @@ cairo_private void
_cairo_surface_init (cairo_surface_t *surface,
const cairo_surface_backend_t *backend);
+cairo_private cairo_clip_mode_t
+_cairo_surface_get_clip_mode (cairo_surface_t *surface);
+
cairo_private cairo_status_t
_cairo_surface_fill_rectangle (cairo_surface_t *surface,
cairo_operator_t operator,
@@ -1412,10 +1456,12 @@ _cairo_surface_fill_rectangles (cairo_surface_t *surface,
int num_rects);
cairo_private cairo_int_status_t
-_cairo_surface_fill_path (cairo_operator_t operator,
- cairo_pattern_t *pattern,
- cairo_surface_t *dst,
- cairo_path_fixed_t *path);
+_cairo_surface_fill_path (cairo_operator_t operator,
+ cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance);
cairo_private cairo_status_t
_cairo_surface_composite_trapezoids (cairo_operator_t operator,
@@ -1475,23 +1521,16 @@ cairo_private cairo_status_t
_cairo_surface_reset_clip (cairo_surface_t *surface);
cairo_private cairo_status_t
-_cairo_surface_can_clip_region (cairo_surface_t *surface);
-
-cairo_private cairo_status_t
_cairo_surface_set_clip_region (cairo_surface_t *surface,
pixman_region16_t *region,
unsigned int serial);
-#if 0
-/* new interfaces for path-based clipping */
-cairo_private cairo_status_t
-_cairo_surface_can_clip_path (cairo_surface_t *surface);
+typedef struct _cairo_clip_path cairo_clip_path_t;
cairo_private cairo_status_t
-_cairo_surface_clip_path (cairo_surface_t *surface,
- cairo_path_fixed_t *path,
- unsigned int serial);
-#endif
+_cairo_surface_set_clip_path (cairo_surface_t *surface,
+ cairo_clip_path_t *clip_path,
+ unsigned int serial);
cairo_private cairo_status_t
_cairo_surface_get_extents (cairo_surface_t *surface,
diff --git a/test/Makefile.am b/test/Makefile.am
index c75e3f06..48204192 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -35,7 +35,7 @@ user-data \
rel-path
if CAIRO_HAS_PDF_SURFACE
-TESTS += pdf-surface
+TESTS += pdf-surface pdf-clip
endif
if CAIRO_HAS_PS_SURFACE
@@ -148,6 +148,7 @@ paint_LDADD = $(LDADDS)
paint_with_alpha_LDADD = $(LDADDS)
path_data_LDADD = $(LDADDS)
pdf_surface_LDADD = $(LDADDS)
+pdf_clip_LDADD = $(LDADDS)
ps_surface_LDADD = $(LDADDS)
pixman_rotate_LDADD = $(LDADDS)
scale_source_surface_paint_LDADD = $(LDADDS)
@@ -171,4 +172,10 @@ xlib_surface_LDADD = $(LDADDS)
noinst_PROGRAMS = imagediff
imagediff_LDADD = $(LDADDS)
-CLEANFILES = *-out.png *-diff.png *.log ps-surface.ps pdf-surface.pdf
+CLEANFILES = \
+ *-out.png \
+ *-diff.png \
+ *.log \
+ ps-surface.ps \
+ pdf-surface.pdf \
+ pdf-clip.pdf
diff --git a/test/pdf-clip.c b/test/pdf-clip.c
new file mode 100644
index 00000000..4d6d75cf
--- /dev/null
+++ b/test/pdf-clip.c
@@ -0,0 +1,134 @@
+/*
+ * 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 <stdio.h>
+
+#include <cairo-pdf.h>
+#include "cairo-test.h"
+
+/* Test PDF clipping */
+
+#define WIDTH_IN_POINTS 600
+#define HEIGHT_IN_POINTS 600
+
+static void
+test_clip (cairo_t *cr, double width, double height)
+{
+ cairo_t *cr2;
+
+ /* Basic test; set a square clip and draw a circle to be clipped
+ * against it.*/
+
+ cairo_rectangle (cr, 100, 100, 400, 400);
+ cairo_clip (cr);
+ cairo_arc (cr, 300, 300, 210, 0, 2 * M_PI);
+ cairo_set_source_rgb (cr, 1, 0, 0);
+ cairo_fill (cr);
+
+ /* Add a plus shaped clip path to the square clip and draw a big
+ * green square to test the new clip path. */
+
+ cairo_save (cr);
+
+ cairo_rectangle (cr, 250, 100, 100, 400);
+ cairo_rectangle (cr, 100, 250, 400, 100);
+ cairo_clip (cr);
+
+ cairo_rectangle (cr, 0, 0, 600, 600);
+ cairo_set_source_rgb (cr, 0, 1, 0);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+
+ /* Set a bezier shape in addition to the rectangle clip set before
+ * the cairo_save() to verify that we successfully removed the
+ * plus shaped clip path and can set a new clip.*/
+
+ cairo_move_to (cr, 600, 0);
+ cairo_curve_to (cr, 300, 600, 0, 300, 600, 0);
+ cairo_clip (cr);
+
+ cairo_rectangle (cr, 0, 0, 600, 600);
+ cairo_set_source_rgb (cr, 0, 0, 1);
+ cairo_fill (cr);
+
+ /* Create a new context for this surface to test overlapped
+ * drawing from two contexts */
+ cr2 = cairo_create (cairo_get_target (cr));
+
+ /* Using the new context, draw a black vertical line, which should
+ * appear unclipped on top of everything drawn so far. */
+ cairo_move_to (cr2, 110, 0);
+ cairo_line_to (cr2, 110, 600);
+ cairo_stroke (cr2);
+
+ /* Using the first context, draw another black vertical line.
+ * This line should be clipped agaist the bezier clipping path set
+ * earlier. */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_move_to (cr, 400, 0);
+ cairo_line_to (cr, 400, 600);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr2);
+
+ /* Test reset clip. Draw a transparent black circle over
+ * everything. Specifically, make sure the circle extends outside
+ * the square clip set at the top of this function. */
+ cairo_reset_clip (cr);
+ cairo_arc (cr, 300, 300, 220, 0, 2 * M_PI);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
+ cairo_fill (cr);
+}
+
+int
+main (void)
+{
+ cairo_t *cr;
+ const char *filename = "pdf-clip.pdf";
+ cairo_surface_t *surface;
+
+ printf("\n");
+
+ surface = cairo_pdf_surface_create (filename,
+ WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+ if (surface == NULL) {
+ fprintf (stderr, "Failed to create pdf surface for file %s\n", filename);
+ return CAIRO_TEST_FAILURE;
+ }
+
+ cr = cairo_create (surface);
+
+ test_clip (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+ cairo_show_page (cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+
+ printf ("pdf-surface: Please check %s to make sure it looks happy.\n",
+ filename);
+
+ return 0;
+}