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.
This commit is contained in:
Kristian Høgsberg 2005-06-14 19:45:22 +00:00
parent 663e39a63c
commit 476fe9a66e
15 changed files with 585 additions and 133 deletions

View file

@ -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),

View file

@ -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
};

View file

@ -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 {

View file

@ -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);
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;
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
if (status)
return status;
/* Check to see if we can represent these traps as a PixRegion. */
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;
/* 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);
status = CAIRO_STATUS_SUCCESS;
if (gstate->clip.region == NULL) {
gstate->clip.region = region;
} else {
_cairo_traps_extents (&traps, &extents);
_cairo_box_round_to_rectangle (&extents, &surface_rect);
_cairo_rectangle_intersect (&surface_rect, &gstate->clip.surface_rect);
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;
}
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 +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

View file

@ -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 */
};

View file

@ -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);

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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)
@ -852,23 +869,6 @@ _cairo_surface_reset_clip (cairo_surface_t *surface)
return CAIRO_STATUS_SUCCESS;
}
/**
* _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
@ -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:

View file

@ -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 */
};

View file

@ -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 */
};

View file

@ -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
};

View file

@ -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,
@ -1474,24 +1520,17 @@ _cairo_surface_allocate_clip_serial (cairo_surface_t *surface);
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,

View file

@ -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

134
test/pdf-clip.c Normal file
View file

@ -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;
}