[cairo] [PATCH] rectangle clipping regions

graydon hoare graydon at redhat.com
Mon Dec 15 11:49:15 PST 2003


hi,

this patch implements some fast (surface-side) rectangular clipping 
functionality:

   - new call: cairo_init_clip (cairo_t *cr); which resets the clip
     region to the entire device. mimics what postscript does here,
     generally not useful except when you're trying to implement
     "setclip" on top of cairo

   - new surface backend function:

     cairo_int_status_t
     (*set_clip_region)		(void			*surface,
				 pixman_region16_t	*region);

     which imperatively sets a clipping region in the surface.

   - new functionality in cairo_gstate: gstate holds both a clip
     mask and a clip region (possibly NULL). when cairo_clip runs,
     the clip path is examined to see if it can be represented as a
     simple rectangle in device space, and if so it is intersected
     with the current clip region, otherwise intersected with the clip
     mask. compositing still happens under the clip mask, but is
     proceeded by a call to backend->set_clip_region with the current
     region. the clip region is reset to empty after compositing.

there are a couple FIXMEs: I don't know what to do on the postscript 
backend, and I don't have a particularly good epsilon available for 
measuring pixel alignment of the clip path (I'm currently checking to 
see if bits 8-15 are clear). I'm also not sure if:

    - the clip should always be added to the mask *as well* as the region

    - the bounding box of non-rectangular paths should be added to the
      region, perhaps to speed up compositing (?)

if either of these is "yes", then there's still a little more work to 
do. also, it appears as though glyphs aren't subject to clipping 
properly under this scheme. I don't know why yet.

any comments on the code at this early stage?

-graydon
-------------- next part --------------
Index: src/cairo.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo.c,v
retrieving revision 1.30
diff -u -b -w -r1.30 cairo.c
--- src/cairo.c	11 Dec 2003 19:12:59 -0000	1.30
+++ src/cairo.c	15 Dec 2003 19:35:19 -0000
@@ -636,6 +636,15 @@
 }
 
 void
+cairo_init_clip (cairo_t *cr)
+{
+    if (cr->status)
+	return;
+
+    cr->status = _cairo_gstate_init_clip (cr->gstate);
+}
+
+void
 cairo_clip (cairo_t *cr)
 {
     if (cr->status)
Index: src/cairo.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo.h,v
retrieving revision 1.36
diff -u -b -w -r1.36 cairo.h
--- src/cairo.h	12 Dec 2003 19:44:16 -0000	1.36
+++ src/cairo.h	15 Dec 2003 19:35:19 -0000
@@ -335,6 +335,9 @@
 
 /* Clipping */
 extern void __external_linkage
+cairo_init_clip (cairo_t *cr);
+
+extern void __external_linkage
 cairo_clip (cairo_t *cr);
 
 /* Font/Text functions */
Index: src/cairo_gstate.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_gstate.c,v
retrieving revision 1.34
diff -u -b -w -r1.34 cairo_gstate.c
--- src/cairo_gstate.c	11 Dec 2003 19:12:59 -0000	1.34
+++ src/cairo_gstate.c	15 Dec 2003 19:35:19 -0000
@@ -84,6 +84,7 @@
     gstate->source_offset.y = 0.0;
     gstate->source_is_solid = 1;
 
+    gstate->clip.region = NULL;
     gstate->clip.surface = NULL;
 
     gstate->alpha = 1.0;
@@ -130,6 +131,12 @@
 	}
     }
 
+    if (other->clip.region)
+    {	
+	gstate->clip.region = pixman_region_create ();
+	pixman_region_copy (gstate->clip.region, other->clip.region);
+    }
+
     cairo_surface_reference (gstate->surface);
     cairo_surface_reference (gstate->source);
     cairo_surface_reference (gstate->clip.surface);
@@ -173,6 +180,10 @@
 	cairo_surface_destroy (gstate->clip.surface);
     gstate->clip.surface = NULL;
 
+    if (gstate->clip.region)
+	pixman_region_destroy (gstate->clip.region);
+    gstate->clip.region = NULL;
+
     _cairo_color_fini (&gstate->color);
 
     _cairo_matrix_fini (&gstate->ctm);
@@ -1233,10 +1244,19 @@
 					     cairo_traps_t *traps)
 {
     cairo_status_t status;
+    pixman_region16_t *empty_region;
 
     if (traps->num_traps == 0)
 	return CAIRO_STATUS_SUCCESS;
 
+    if (gstate->clip.region) {
+	status = _cairo_surface_set_clip_region (gstate->surface, 
+						 gstate->clip.region);
+	if (status)
+	    return status;
+	empty_region = pixman_region_create ();
+    }
+
     if (gstate->clip.surface) {
 	cairo_fixed_t xoff, yoff;
 	cairo_trapezoid_t *t;
@@ -1317,6 +1337,15 @@
     BAIL1:
 	cairo_surface_destroy (white);
     BAIL0:
+
+	if (gstate->clip.region) {
+	    if (status)
+		_cairo_surface_set_clip_region (gstate->surface, empty_region);
+	    else
+		status = _cairo_surface_set_clip_region (gstate->surface, empty_region);
+	    pixman_region_destroy (empty_region);
+	}
+	
 	if (status)
 	    return status;
 
@@ -1337,6 +1366,14 @@
 						      yoff - gstate->source_offset.y,
 						      traps->traps,
 						      traps->num_traps);
+	if (gstate->clip.region) {
+	    if (status)
+		_cairo_surface_set_clip_region (gstate->surface, empty_region);
+	    else
+		status = _cairo_surface_set_clip_region (gstate->surface, empty_region);
+	pixman_region_destroy (empty_region);
+	}
+	
 	if (status)
 	    return status;
     }
@@ -1429,6 +1466,67 @@
     return _cairo_surface_show_page (gstate->surface);
 }
 
+
+cairo_status_t
+_cairo_gstate_init_clip (cairo_gstate_t *gstate)
+{
+    if (gstate->clip.surface)
+	cairo_surface_destroy (gstate->clip.surface);
+    gstate->clip.surface = NULL;
+
+    if (gstate->clip.region)
+	pixman_region_destroy (gstate->clip.region);
+    gstate->clip.region = NULL;
+    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_status_t st;
+
+    st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty);    
+    if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.))
+	return 0;
+
+/* FIXME: get a real epsilon worked out here */
+#define ALIGNED_TO_PIXEL(x) (((x) & 0xFF00) == 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
+	&& ALIGNED_TO_PIXEL(tr->traps[0].left.p1.x)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].left.p1.y)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].left.p2.x)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].left.p2.y)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].right.p1.x)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].right.p1.y)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].right.p2.x)
+	&& ALIGNED_TO_PIXEL(tr->traps[0].right.p2.y)) {
+
+#undef ALIGNED_TO_PIXEL
+
+	double x1, x2, y1, y2;
+	x1 = _cairo_fixed_to_double (tr->traps[0].left.p1.x);
+	x2 = _cairo_fixed_to_double (tr->traps[0].right.p1.x);
+	y1 = _cairo_fixed_to_double (tr->traps[0].left.p1.y);
+	y2 = _cairo_fixed_to_double (tr->traps[0].left.p2.y);
+  	cairo_matrix_transform_point (mat, &x1, &y1);
+  	cairo_matrix_transform_point (mat, &x2, &y2);
+	box->x1 = (short) x1;
+	box->x2 = (short) x2;
+	box->y1 = (short) y1;
+	box->y2 = (short) y2;
+	return 1;
+    }
+    return 0;
+}
+
 cairo_status_t
 _cairo_gstate_clip (cairo_gstate_t *gstate)
 {
@@ -1436,6 +1534,47 @@
     cairo_surface_t *alpha_one;
     cairo_traps_t traps;
     cairo_color_t white_color;
+    pixman_box16_t box;
+
+    /* Fill the clip region as traps. */
+
+    _cairo_traps_init (&traps);
+    status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps);
+    if (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;
+
+	status = CAIRO_STATUS_SUCCESS;
+	rect = pixman_region_create_simple (&box);
+	
+	if (rect == NULL) {
+	    status = CAIRO_STATUS_NO_MEMORY;
+
+	} else {
+	    
+	    if (gstate->clip.region == NULL) {
+		gstate->clip.region = rect;		
+
+	    } else {
+		if (pixman_region_intersect (gstate->clip.region, 
+					     gstate->clip.region, rect)
+		    != PIXMAN_REGION_STATUS_SUCCESS)
+		    status = CAIRO_STATUS_NO_MEMORY;		
+		pixman_region_destroy (rect);
+	    }
+	}
+	_cairo_traps_fini (&traps);
+	return status;
+    }
+
+    /* Otherwise represent the clip as a mask surface. */
 
     _cairo_color_init (&white_color);
 
@@ -1464,13 +1603,6 @@
 
     cairo_surface_set_repeat (alpha_one, 1);
 
-    _cairo_traps_init (&traps);
-    status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps);
-    if (status) {
-	_cairo_traps_fini (&traps);
-	return status;
-    }
-
     _cairo_gstate_clip_and_composite_trapezoids (gstate,
 						 alpha_one,
 						 CAIRO_OPERATOR_IN,
Index: src/cairo_image_surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_image_surface.c,v
retrieving revision 1.7
diff -u -b -w -r1.7 cairo_image_surface.c
--- src/cairo_image_surface.c	11 Dec 2003 21:04:39 -0000	1.7
+++ src/cairo_image_surface.c	15 Dec 2003 19:35:19 -0000
@@ -449,6 +449,15 @@
     return CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
+static cairo_int_status_t
+_cairo_image_surface_set_clip_region (void *abstract_surface,
+				      pixman_region16_t *region)
+{
+    cairo_image_surface_t *surf = (cairo_image_surface_t *) abstract_surface;
+    pixman_image_set_clip_region (surf->pixman_image, region);
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static const cairo_surface_backend_t cairo_image_surface_backend = {
     _cairo_image_surface_create_similar,
     _cairo_image_abstract_surface_destroy,
@@ -462,5 +471,6 @@
     _cairo_image_surface_fill_rectangles,
     _cairo_image_surface_composite_trapezoids,
     _cairo_image_surface_copy_page,
-    _cairo_image_surface_show_page
+    _cairo_image_surface_show_page,
+    _cairo_image_surface_set_clip_region
 };
Index: src/cairo_ps_surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_ps_surface.c,v
retrieving revision 1.6
diff -u -b -w -r1.6 cairo_ps_surface.c
--- src/cairo_ps_surface.c	11 Dec 2003 20:43:58 -0000	1.6
+++ src/cairo_ps_surface.c	15 Dec 2003 19:35:19 -0000
@@ -394,6 +394,15 @@
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_int_status_t
+_cairo_ps_surface_set_clip_region (void *abstract_surface,
+				   pixman_region16_t *region)
+{
+    /* FIXME: I don't really understand this backend. */
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
 static const cairo_surface_backend_t cairo_ps_surface_backend = {
     _cairo_ps_surface_create_similar,
     _cairo_ps_surface_destroy,
@@ -407,5 +416,6 @@
     _cairo_ps_surface_fill_rectangles,
     _cairo_ps_surface_composite_trapezoids,
     _cairo_ps_surface_copy_page,
-    _cairo_ps_surface_show_page
+    _cairo_ps_surface_show_page,
+    _cairo_ps_surface_set_clip_region
 };
Index: src/cairo_surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_surface.c,v
retrieving revision 1.21
diff -u -b -w -r1.21 cairo_surface.c
--- src/cairo_surface.c	4 Nov 2003 03:17:31 -0000	1.21
+++ src/cairo_surface.c	15 Dec 2003 19:35:19 -0000
@@ -364,3 +364,8 @@
     return CAIRO_STATUS_SUCCESS;
 }
 
+cairo_status_t
+_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region)
+{
+    return surface->backend->set_clip_region (surface, region);
+}
Index: src/cairo_xlib_surface.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo_xlib_surface.c,v
retrieving revision 1.13
diff -u -b -w -r1.13 cairo_xlib_surface.c
--- src/cairo_xlib_surface.c	11 Dec 2003 18:01:10 -0000	1.13
+++ src/cairo_xlib_surface.c	15 Dec 2003 19:35:19 -0000
@@ -567,6 +567,35 @@
     return CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
+static cairo_int_status_t
+_cairo_xlib_surface_set_clip_region (void *abstract_surface,
+				     pixman_region16_t *region)
+{
+    Region xregion;
+    pixman_box16_t *box;
+    int n, m;
+
+    cairo_xlib_surface_t *surf = (cairo_xlib_surface_t *) abstract_surface;
+    xregion = XCreateRegion();
+
+    box = pixman_region_rects (region);
+    n = pixman_region_num_rects (region);
+    m = n;
+    for (; n > 0; --n, ++box)
+    {
+	XRectangle xr;
+	xr.x = (short) box->x1;
+	xr.y = (short) box->y1;
+	xr.width = (unsigned short) (box->x2 - box->x1);
+	xr.height = (unsigned short) (box->y2 - box->y1);
+	XUnionRectWithRegion (&xr, xregion, xregion);
+    }    
+    
+    XRenderSetPictureClipRegion (surf->dpy, surf->picture, xregion);
+    XDestroyRegion(xregion);
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static const struct cairo_surface_backend cairo_xlib_surface_backend = {
     _cairo_xlib_surface_create_similar,
     _cairo_xlib_surface_destroy,
@@ -580,7 +609,8 @@
     _cairo_xlib_surface_fill_rectangles,
     _cairo_xlib_surface_composite_trapezoids,
     _cairo_xlib_surface_copy_page,
-    _cairo_xlib_surface_show_page
+    _cairo_xlib_surface_show_page,
+    _cairo_xlib_surface_set_clip_region
 };
 
 cairo_surface_t *
Index: src/cairoint.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairoint.h,v
retrieving revision 1.44
diff -u -b -w -r1.44 cairoint.h
--- src/cairoint.h	11 Dec 2003 21:04:39 -0000	1.44
+++ src/cairoint.h	15 Dec 2003 19:35:19 -0000
@@ -70,7 +70,6 @@
 #define __internal_linkage
 #endif
 
-
 /* These macros allow us to deprecate a function by providing an alias
    for the old function name to the new function name. With this
    macro, binary compatibility is preserved. The macro only works on
@@ -378,6 +377,10 @@
 
     cairo_int_status_t
     (*show_page)		(void			*surface);
+
+    cairo_int_status_t
+    (*set_clip_region)		(void			*surface,
+				 pixman_region16_t	*region);
 } cairo_surface_backend_t;
 
 struct cairo_matrix {
@@ -468,6 +471,7 @@
     int y;
     int width;
     int height;
+    pixman_region16_t *region;
     cairo_surface_t *surface;
 } cairo_clip_rec_t;
 
@@ -750,6 +754,9 @@
 		       int		*inside_ret);
 
 extern cairo_status_t __internal_linkage
+_cairo_gstate_init_clip (cairo_gstate_t *gstate);
+
+extern cairo_status_t __internal_linkage
 _cairo_gstate_clip (cairo_gstate_t *gstate);
 
 extern cairo_status_t __internal_linkage
@@ -1002,6 +1009,9 @@
 extern cairo_status_t __internal_linkage
 _cairo_surface_show_page (cairo_surface_t *surface);
 
+extern cairo_status_t __internal_linkage
+_cairo_surface_set_clip_region (cairo_surface_t *surface, pixman_region16_t *region);
+
 extern double __internal_linkage
 _cairo_surface_pixels_per_inch (cairo_surface_t *surface);
 


More information about the cairo mailing list