[cairo] [PATCH] Path clipping

Kristian Høgsberg krh at bitplanet.net
Tue Jun 14 09:14:25 PDT 2005


> I'll send an updated patch.

Okay, here's a new patch.

I decided to keep _cairo_surface_set_clip_path(), because as I was
moving it to cairo-gstate.c I remembered another reason why I put it in
cairo-surface.c in the first place.  By having 
_cairo_surface_set_clip_path() we can set the clip path and clip serial 
into the surface in one atomic surface function call.  If we move this 
functionality to cairo-gstate.c and build the clip path in a number of 
steps, we leave the surface inconsistent since the clip serial won't 
match the clip path until we're done.

I'm not arguing correctness or thread safety, but rather that from an 
API point of view, the cairo_surface_* calls shouldn't leave the surface 
  inconsistent.  Furthermore there is already precedence for doing extra 
work in the cairo_surface_* functions, for example, 
cairo_surface_reset_clip() and all the fallback handling.

Another thing; I didn't do the cairo_clip_t union.  There would only be 
two members: path clipping data and everything else.  I'm not convinced 
that it makes the code more clear to add a union to the cairo_clip_t, 
but it's easy to do, so I can add that if you still think it's worthwhile.

cheers,
Kristian

-------------- next part --------------
? doc/public/tmpl/cairo-image.sgml
? doc/public/tmpl/cairo-win3.sgml
? test/pdf-clip.c
Index: src/cairo-glitz-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-glitz-surface.c,v
retrieving revision 1.44
diff -u -p -r1.44 cairo-glitz-surface.c
--- src/cairo-glitz-surface.c	10 Jun 2005 19:18:21 -0000	1.44
+++ src/cairo-glitz-surface.c	14 Jun 2005 03:53:23 -0000
@@ -2133,6 +2133,7 @@ static const cairo_surface_backend_t cai
     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
 };
Index: src/cairo-gstate-private.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-gstate-private.h,v
retrieving revision 1.11
diff -u -p -r1.11 cairo-gstate-private.h
--- src/cairo-gstate-private.h	3 Jun 2005 22:54:40 -0000	1.11
+++ src/cairo-gstate-private.h	14 Jun 2005 03:53:23 -0000
@@ -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 {
Index: src/cairo-gstate.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-gstate.c,v
retrieving revision 1.141
diff -u -p -r1.141 cairo-gstate.c
--- src/cairo-gstate.c	13 Jun 2005 23:36:40 -0000	1.141
+++ src/cairo-gstate.c	14 Jun 2005 03:53:24 -0000
@@ -70,6 +70,12 @@ _cairo_gstate_unset_font (cairo_gstate_t
 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)
 {
@@ -119,6 +125,7 @@ _cairo_gstate_init (cairo_gstate_t  *gst
     gstate->clip.region = NULL;
     gstate->clip.surface = NULL;
     gstate->clip.serial = 0;
+    gstate->clip.path = NULL;
 
     _cairo_gstate_identity_matrix (gstate);
 
@@ -168,6 +175,7 @@ _cairo_gstate_init_copy (cairo_gstate_t 
     
     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 +213,10 @@ _cairo_gstate_fini (cairo_gstate_t *gsta
 	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 +350,10 @@ _cairo_gstate_set_clip (cairo_gstate_t *
     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 +1157,8 @@ _composite_trap_region (cairo_gstate_t  
     
     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 +1452,9 @@ _cairo_gstate_fill (cairo_gstate_t *gsta
     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 +1599,124 @@ _cairo_gstate_reset_clip (cairo_gstate_t
     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. */
-
-    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);
+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;
+
+    /* 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 +1728,7 @@ _cairo_gstate_clip (cairo_gstate_t *gsta
 
     /* 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 +1738,10 @@ _cairo_gstate_clip (cairo_gstate_t *gsta
 						  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 +1779,34 @@ _cairo_gstate_clip (cairo_gstate_t *gsta
     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
Index: src/cairo-image-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-image-surface.c,v
retrieving revision 1.44
diff -u -p -r1.44 cairo-image-surface.c
--- src/cairo-image-surface.c	10 Jun 2005 19:18:21 -0000	1.44
+++ src/cairo-image-surface.c	14 Jun 2005 03:53:25 -0000
@@ -714,6 +714,7 @@ static const cairo_surface_backend_t cai
     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 */
 };
Index: src/cairo-pdf-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-pdf-surface.c,v
retrieving revision 1.44
diff -u -p -r1.44 cairo-pdf-surface.c
--- src/cairo-pdf-surface.c	10 Jun 2005 19:46:49 -0000	1.44
+++ src/cairo-pdf-surface.c	14 Jun 2005 03:53:27 -0000
@@ -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_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;
 }
@@ -1639,6 +1641,7 @@ intersect (cairo_line_t *line, cairo_fix
 typedef struct
 {
     cairo_output_stream_t *output_stream;
+    cairo_bool_t has_current_point;
 } pdf_path_info_t;
 
 static cairo_status_t
@@ -1650,6 +1653,7 @@ _cairo_pdf_path_move_to (void *closure, 
 				 "%f %f m ",
 				 _cairo_fixed_to_double (point->x),
 				 _cairo_fixed_to_double (point->y));
+    info->has_current_point = TRUE;
     
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1658,11 +1662,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;
 }
@@ -1694,6 +1706,7 @@ _cairo_pdf_path_close_path (void *closur
     
     _cairo_output_stream_printf (info->output_stream,
 				 "h\r\n");
+    info->has_current_point = FALSE;
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1702,15 +1715,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
@@ -1719,6 +1733,7 @@ _cairo_pdf_surface_fill_path (cairo_oper
 	    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,
@@ -1728,8 +1743,20 @@ _cairo_pdf_surface_fill_path (cairo_oper
 					  _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;
 }
@@ -1906,6 +1933,62 @@ _cairo_pdf_surface_show_glyphs (cairo_sc
     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,
@@ -1920,6 +2003,7 @@ static const cairo_surface_backend_t cai
     _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
@@ -2238,6 +2322,11 @@ _cairo_pdf_document_add_page (cairo_pdf_
 
     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);
Index: src/cairo-ps-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-ps-surface.c,v
retrieving revision 1.35
diff -u -p -r1.35 cairo-ps-surface.c
--- src/cairo-ps-surface.c	17 May 2005 12:58:02 -0000	1.35
+++ src/cairo-ps-surface.c	14 Jun 2005 03:53:27 -0000
@@ -373,6 +373,7 @@ static const cairo_surface_backend_t cai
     _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 */
 };
Index: src/cairo-quartz-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-quartz-surface.c,v
retrieving revision 1.10
diff -u -p -r1.10 cairo-quartz-surface.c
--- src/cairo-quartz-surface.c	11 May 2005 22:39:26 -0000	1.10
+++ src/cairo-quartz-surface.c	14 Jun 2005 03:53:27 -0000
@@ -227,6 +227,7 @@ static const struct _cairo_surface_backe
     NULL, /* copy_page */
     NULL, /* show_page */
     _cairo_quartz_surface_set_clip_region,
+    NULL, /* intersect_clip_path */
     _cairo_quartz_surface_get_extents,
     NULL  /* show_glyphs */
 };
Index: src/cairo-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-surface.c,v
retrieving revision 1.72
diff -u -p -r1.72 cairo-surface.c
--- src/cairo-surface.c	10 Jun 2005 19:18:21 -0000	1.72
+++ src/cairo-surface.c	14 Jun 2005 03:53:27 -0000
@@ -38,7 +38,7 @@
 #include <stdlib.h>
 
 #include "cairoint.h"
-
+#include "cairo-gstate-private.h"
 
 void
 _cairo_surface_init (cairo_surface_t			*surface,
@@ -117,6 +117,17 @@ _cairo_surface_create_similar_solid (cai
     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)
 {
@@ -646,13 +657,16 @@ _cairo_surface_fill_rectangles (cairo_su
 }
 
 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;
 }
@@ -839,13 +853,16 @@ _cairo_surface_reset_clip (cairo_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)
@@ -855,23 +872,6 @@ _cairo_surface_reset_clip (cairo_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
@@ -895,22 +895,64 @@ _cairo_surface_set_clip_region (cairo_su
     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:
Index: src/cairo-win32-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-win32-surface.c,v
retrieving revision 1.24
diff -u -p -r1.24 cairo-win32-surface.c
--- src/cairo-win32-surface.c	10 Jun 2005 19:18:21 -0000	1.24
+++ src/cairo-win32-surface.c	14 Jun 2005 03:53:28 -0000
@@ -943,6 +943,7 @@ static const cairo_surface_backend_t cai
     NULL, /* copy_page */
     NULL, /* show_page */
     _cairo_win32_surface_set_clip_region,
+    NULL, /* intersect_clip_path */
     _cairo_win32_surface_get_extents,
     NULL  /* show_glyphs */
 };
Index: src/cairo-xcb-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-xcb-surface.c,v
retrieving revision 1.34
diff -u -p -r1.34 cairo-xcb-surface.c
--- src/cairo-xcb-surface.c	10 Jun 2005 19:46:49 -0000	1.34
+++ src/cairo-xcb-surface.c	14 Jun 2005 03:53:29 -0000
@@ -1013,6 +1013,7 @@ static const cairo_surface_backend_t cai
     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 */
 };
Index: src/cairo-xlib-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-xlib-surface.c,v
retrieving revision 1.77
diff -u -p -r1.77 cairo-xlib-surface.c
--- src/cairo-xlib-surface.c	10 Jun 2005 19:46:49 -0000	1.77
+++ src/cairo-xlib-surface.c	14 Jun 2005 03:53:30 -0000
@@ -1047,6 +1047,7 @@ static const cairo_surface_backend_t cai
     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
 };
Index: src/cairoint.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairoint.h,v
retrieving revision 1.153
diff -u -p -r1.153 cairoint.h
--- src/cairoint.h	13 Jun 2005 23:53:52 -0000	1.153
+++ src/cairoint.h	14 Jun 2005 03:53:31 -0000
@@ -643,10 +643,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}.
@@ -684,7 +717,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;
 
@@ -696,6 +731,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;
 
@@ -1383,6 +1424,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,
@@ -1414,10 +1458,12 @@ _cairo_surface_fill_rectangles (cairo_su
 				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,
@@ -1477,23 +1523,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,
Index: test/pdf-surface.c
===================================================================
RCS file: /cvs/cairo/cairo/test/pdf-surface.c,v
retrieving revision 1.9
diff -u -p -r1.9 pdf-surface.c
--- test/pdf-surface.c	17 May 2005 12:58:02 -0000	1.9
+++ test/pdf-surface.c	14 Jun 2005 03:53:31 -0000
@@ -33,18 +33,18 @@
  * But you can manually view the image to make sure it looks happy.
  */
 
-#define WIDTH_IN_INCHES  3
-#define HEIGHT_IN_INCHES 3
-#define WIDTH_IN_POINTS  (WIDTH_IN_INCHES  * 72.0)
-#define HEIGHT_IN_POINTS (HEIGHT_IN_INCHES * 72.0)
+#define WIDTH_IN_POINTS 600
+#define HEIGHT_IN_POINTS 600
 
 static void
-draw (cairo_t *cr, double width, double height)
+test_draw (cairo_t *cr, double width, double height)
 {
 #define STROKE_WIDTH .04
 
     double size;
 
+    cairo_save (cr);
+
     if (width > height)
 	size = height;
     else
@@ -82,6 +82,63 @@ draw (cairo_t *cr, double width, double 
 		    0.6, 0.8,
 		    0.7, 0.7);
     cairo_stroke (cr);
+
+    cairo_restore (cr);
+}
+
+static void
+test_clip (cairo_t *cr, double width, double height)
+{
+    cairo_t *cr2;
+
+    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);
+
+    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(). */
+    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 new surface to test overlapped drawing from two
+     * contexts */
+    cr2 = cairo_create (cairo_get_target (cr));
+
+    cairo_move_to (cr2, 110, 0);
+    cairo_line_to (cr2, 110, 600);
+    cairo_stroke (cr2);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_move_to (cr, 120, 0);
+    cairo_line_to (cr, 120, 600);
+    cairo_stroke (cr);
+
+    cairo_destroy (cr2);
+
+    /* Test reset clip */
+    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
@@ -102,8 +159,10 @@ main (void)
 
     cr = cairo_create (surface);
 
-    draw (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+    test_draw (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
+    cairo_show_page (cr);
 
+    test_clip (cr, WIDTH_IN_POINTS, HEIGHT_IN_POINTS);
     cairo_show_page (cr);
 
     cairo_destroy (cr);


More information about the cairo mailing list