diff options
author | Kristian Høgsberg <krh@redhat.com> | 2005-06-14 19:45:22 +0000 |
---|---|---|
committer | Kristian Høgsberg <krh@redhat.com> | 2005-06-14 19:45:22 +0000 |
commit | 476fe9a66e2be74e65fe2f4e42bda5f4c8b244a0 (patch) | |
tree | 4d83e890d040cc75d7205089f99f49df0a1a798c | |
parent | 663e39a63c6ae223dc3095d29fd95bc8885c74f2 (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-- | ChangeLog | 36 | ||||
-rw-r--r-- | src/cairo-glitz-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-gstate-private.h | 16 | ||||
-rw-r--r-- | src/cairo-gstate.c | 222 | ||||
-rw-r--r-- | src/cairo-image-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 101 | ||||
-rw-r--r-- | src/cairo-ps-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-quartz-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-surface.c | 116 | ||||
-rw-r--r-- | src/cairo-win32-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-xcb-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-xlib-surface.c | 1 | ||||
-rw-r--r-- | src/cairoint.h | 71 | ||||
-rw-r--r-- | test/Makefile.am | 11 | ||||
-rw-r--r-- | test/pdf-clip.c | 134 |
15 files changed, 583 insertions, 131 deletions
@@ -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, ®ion); - if (status) { - _cairo_traps_fini (&traps); - return status; - } + status = _cairo_traps_extract_region (traps, ®ion); + 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; +} |