[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