diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h index 91cc9e975..fb5f61581 100644 --- a/src/cairo-clip-private.h +++ b/src/cairo-clip-private.h @@ -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); diff --git a/src/cairo-clip.c b/src/cairo-clip.c index 7edb91507..669c8b054 100644 --- a/src/cairo-clip.c +++ b/src/cairo-clip.c @@ -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); + } + } +} + diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index bf027e3a3..6251a3540 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -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)); } /** diff --git a/src/cairo.c b/src/cairo.c index 4619e545f..ba3b7bf5a 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -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);