Fix up clip at pop_group time, to keep it in surface backend coordinates

We keep the clip in surface-backend coordinates always, so it needs
fixing whenever we change the target surface out in the gstate.  The
only place this happens is in push_group, so fix it as part of
gstate_redirect().
This commit is contained in:
Vladimir Vukicevic 2006-03-14 15:56:08 -08:00 committed by Carl Worth
parent 7fa3c6eee5
commit fb7f7c2f27
4 changed files with 92 additions and 29 deletions

View file

@ -87,6 +87,11 @@ _cairo_clip_fini (cairo_clip_t *clip);
cairo_private void
_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other);
cairo_private void
_cairo_clip_init_deep_copy (cairo_clip_t *clip,
cairo_clip_t *other,
cairo_surface_t *target);
cairo_private cairo_status_t
_cairo_clip_reset (cairo_clip_t *clip);

View file

@ -234,8 +234,7 @@ _cairo_clip_intersect_path (cairo_clip_t *clip,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_surface_t *target)
cairo_antialias_t antialias)
{
cairo_clip_path_t *clip_path;
cairo_status_t status;
@ -259,7 +258,6 @@ _cairo_clip_intersect_path (cairo_clip_t *clip,
clip_path->antialias = antialias;
clip_path->prev = clip->path;
clip->path = clip_path;
clip->serial = _cairo_surface_allocate_clip_serial (target);
return CAIRO_STATUS_SUCCESS;
}
@ -447,10 +445,14 @@ _cairo_clip_clip (cairo_clip_t *clip,
status = _cairo_clip_intersect_path (clip,
path, fill_rule, tolerance,
antialias, target);
antialias);
if (status == CAIRO_STATUS_SUCCESS)
clip->serial = _cairo_surface_allocate_clip_serial (target);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (path,
fill_rule,
@ -472,3 +474,70 @@ _cairo_clip_clip (cairo_clip_t *clip,
return status;
}
void
_cairo_clip_translate (cairo_clip_t *clip,
cairo_fixed_t tx,
cairo_fixed_t ty)
{
if (clip->region) {
pixman_region_translate (clip->region,
_cairo_fixed_integer_part (tx),
_cairo_fixed_integer_part (ty));
}
if (clip->surface) {
clip->surface_rect.x += _cairo_fixed_integer_part (tx);
clip->surface_rect.y += _cairo_fixed_integer_part (ty);
}
if (clip->path) {
cairo_clip_path_t *clip_path = clip->path;
while (clip_path) {
_cairo_path_fixed_offset (&clip_path->path, tx, ty);
clip_path = clip_path->prev;
}
}
}
static void
_cairo_clip_path_reapply_clip_path (cairo_clip_t *clip,
cairo_clip_path_t *clip_path)
{
if (clip_path->prev)
_cairo_clip_path_reapply_clip_path (clip, clip_path->prev);
_cairo_clip_intersect_path (clip,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias);
}
void
_cairo_clip_init_deep_copy (cairo_clip_t *clip,
cairo_clip_t *other,
cairo_surface_t *target)
{
_cairo_clip_init (clip, target);
if (other->mode != clip->mode) {
/* We should reapply the original clip path in this case, and let
* whatever the right handling is happen */
} else {
if (other->region) {
clip->region = pixman_region_create ();
pixman_region_copy (clip->region, other->region);
}
if (other->surface) {
_cairo_surface_clone_similar (target, clip->surface, &clip->surface);
clip->surface_rect = other->surface_rect;
}
if (other->path) {
_cairo_clip_path_reapply_clip_path (clip, other->path);
}
}
}

View file

@ -303,30 +303,14 @@ _cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
* since its ref is now owned by gstate->parent_target */
gstate->target = cairo_surface_reference (child);
/* Check that the new surface's clip mode is compatible */
if (gstate->clip.mode != _cairo_surface_get_clip_mode (child)) {
/* clip is not compatible; try to recreate it */
/* XXX - saving the clip path always might be useful here,
* so that we could recover non-CLIP_MODE_PATH clips */
if (gstate->clip.mode == CAIRO_CLIP_MODE_PATH) {
cairo_clip_t saved_clip = gstate->clip;
_cairo_clip_fini (&gstate->clip);
_cairo_clip_init_deep_copy (&gstate->clip, &gstate->next->clip, child);
_cairo_clip_init (&gstate->clip, child);
/* unwind the path and re-apply */
_cairo_gstate_recursive_apply_clip_path (gstate, saved_clip.path);
_cairo_clip_fini (&saved_clip);
} else {
/* uh, not sure what to do here.. */
_cairo_clip_fini (&gstate->clip);
_cairo_clip_init (&gstate->clip, child);
}
} else {
/* clip is compatible; allocate a new serial for the new surface. */
if (gstate->clip.serial)
gstate->clip.serial = _cairo_surface_allocate_clip_serial (child);
}
/* The clip is in surface backend coordinates for the previous target;
* translate it into the child's backend coordinates. */
_cairo_clip_translate (&gstate->clip,
_cairo_fixed_from_double (child->device_x_offset - gstate->parent_target->device_x_offset),
_cairo_fixed_from_double (child->device_y_offset - gstate->parent_target->device_y_offset));
}
/**

View file

@ -385,8 +385,13 @@ cairo_push_group_with_content (cairo_t *cr, cairo_content_t content)
goto bail;
/* Set device offsets on the new surface so that logically it appears at
* the same location on the parent surface. */
cairo_surface_set_device_offset (group_surface, -extents.x, -extents.y);
* the same location on the parent surface -- when we pop_group this,
* the source pattern will get fixed up for the appropriate target surface
* device offsets, so we want to set our own surface offsets from /that/,
* and not from the device origin. */
cairo_surface_set_device_offset (group_surface,
cr->gstate->target->device_x_offset - extents.x,
cr->gstate->target->device_y_offset - extents.y);
/* create a new gstate for the redirect */
cairo_save (cr);