diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-08-14 00:25:15 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2011-08-14 12:37:57 +0100 |
commit | 8a90b22897b6460b3396b9959383131039bd9ce2 (patch) | |
tree | 74752284a1a523a81b1d33634601655022fd82ba | |
parent | 7971c678f18b9a078dc921e8c9a9d8175038cd1c (diff) |
subsurface+recording: handle recursion
Ouch, a nasty bug surfaces after rearranging code to fix the others.
Another self-copy loop this time through a subsurface of a recording
surface.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | src/cairo-recording-surface.c | 111 | ||||
-rw-r--r-- | src/cairo-script-surface.c | 129 | ||||
-rw-r--r-- | src/cairo-surface-snapshot.c | 12 | ||||
-rw-r--r-- | src/cairo-surface-subsurface.c | 105 |
4 files changed, 238 insertions, 119 deletions
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index eb12624d..1b00c725 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -528,35 +528,118 @@ _cairo_recording_surface_acquire_source_image_transformed (void *abstract_s return CAIRO_STATUS_SUCCESS; } +struct proxy { + cairo_surface_t base; + cairo_surface_t *image; +}; + +static cairo_status_t +proxy_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + struct proxy *proxy = abstract_surface; + return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra); +} + +static void +proxy_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + struct proxy *proxy = abstract_surface; + _cairo_surface_release_source_image (proxy->image, image, image_extra); +} + +static cairo_status_t +proxy_finish (void *abstract_surface) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t proxy_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + proxy_finish, + NULL, + + NULL, /* create similar */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + proxy_acquire_source_image, + proxy_release_source_image, +}; + +static cairo_surface_t * +attach_proxy (cairo_surface_t *source, + cairo_surface_t *image) +{ + struct proxy *proxy; + + proxy = malloc (sizeof (*proxy)); + if (unlikely (proxy == NULL)) + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content); + + proxy->image = image; + _cairo_surface_attach_snapshot (source, &proxy->base, NULL); + + return &proxy->base; +} + +static void +detach_proxy (cairo_surface_t *source, + cairo_surface_t *proxy) +{ + cairo_surface_finish (proxy); + cairo_surface_destroy (proxy); +} + +static cairo_surface_t * +get_proxy (cairo_surface_t *proxy) +{ + return ((struct proxy *)proxy)->image; +} + static cairo_status_t _cairo_recording_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) { - cairo_matrix_t identity; - cairo_surface_t *image; + cairo_recording_surface_t *surface = abstract_surface; + cairo_surface_t *image, *proxy; cairo_status_t status; - image = _cairo_surface_has_snapshot (abstract_surface, - &_cairo_image_surface_backend); - if (image != NULL) { - *image_out = (cairo_image_surface_t *) cairo_surface_reference (image); + proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend); + if (proxy != NULL) { + *image_out = (cairo_image_surface_t *) + cairo_surface_reference (get_proxy (proxy)); *image_extra = NULL; return CAIRO_STATUS_SUCCESS; } - cairo_matrix_init_identity (&identity); + assert (! surface->unbounded); + image = _cairo_image_surface_create_with_content (surface->base.content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->status)) + return image->status; - status = - _cairo_recording_surface_acquire_source_image_transformed (abstract_surface, &identity, image_out, image_extra); - if (unlikely (status)) + /* Handle recursion by returning future reads from the current image */ + proxy = attach_proxy (abstract_surface, image); + status = _cairo_recording_surface_replay (&surface->base, image); + detach_proxy (abstract_surface, proxy); + + if (unlikely (status)) { + cairo_surface_destroy (image); return status; + } - _cairo_surface_attach_snapshot (abstract_surface, - &(*image_out)->base, - NULL); + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; return CAIRO_STATUS_SUCCESS; - } static void diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index 2724f8f9..a93b0caf 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -1035,20 +1035,77 @@ _emit_mesh_pattern (cairo_script_surface_t *surface, return CAIRO_STATUS_SUCCESS; } +struct script_snapshot { + cairo_surface_t base; +}; + +static cairo_status_t +script_snapshot_finish (void *abstract_surface) +{ + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t script_snapshot_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + script_snapshot_finish, +}; + +static void +detach_snapshot (cairo_surface_t *abstract_surface) +{ + cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + + _cairo_output_stream_printf (ctx->stream, + "/s%d undef\n ", + surface->base.unique_id); +} + +static void +attach_snapshot (cairo_script_context_t *ctx, + cairo_surface_t *source) +{ + struct script_snapshot *surface; + + surface = malloc (sizeof (*surface)); + if (unlikely (surface == NULL)) + return; + + _cairo_surface_init (&surface->base, + &script_snapshot_backend, + &ctx->base, + source->content); + + _cairo_output_stream_printf (ctx->stream, + "dup /s%d exch def ", + surface->base.unique_id); + + _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot); + cairo_surface_destroy (&surface->base); +} + static cairo_status_t _emit_recording_surface_pattern (cairo_script_surface_t *surface, cairo_recording_surface_t *source) { cairo_script_implicit_context_t old_cr; + cairo_script_context_t *ctx = to_context (surface); cairo_script_surface_t *similar; + cairo_surface_t *snapshot; cairo_rectangle_t r, *extents; cairo_status_t status; + snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend); + if (snapshot) { + _cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id); + return CAIRO_INT_STATUS_SUCCESS; + } + extents = NULL; if (_cairo_recording_surface_get_bounds (&source->base, &r)) extents = &r; - similar = _cairo_script_surface_create_internal (to_context (surface), + similar = _cairo_script_surface_create_internal (ctx, source->base.content, extents, NULL); @@ -1057,21 +1114,24 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface, similar->base.is_clear = TRUE; - _cairo_output_stream_printf (to_context (surface)->stream, - "//%s ", + _cairo_output_stream_printf (ctx->stream, "//%s ", _content_to_string (source->base.content)); if (extents) { - _cairo_output_stream_printf (to_context (surface)->stream, - "[%f %f %f %f]", + _cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]", extents->x, extents->y, extents->width, extents->height); } else - _cairo_output_stream_puts (to_context (surface)->stream, "[]"); - _cairo_output_stream_puts (to_context (surface)->stream, - " record dup context\n"); + _cairo_output_stream_puts (ctx->stream, "[]"); + _cairo_output_stream_puts (ctx->stream, " record\n"); + + attach_snapshot (ctx, &source->base); + + _cairo_output_stream_puts (ctx->stream, "dup context\n"); + target_push (similar); similar->emitted = TRUE; + old_cr = surface->cr; _cairo_script_implicit_context_init (&surface->cr); status = _cairo_recording_surface_replay (&source->base, &similar->base); @@ -1085,7 +1145,7 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface, cairo_list_del (&similar->operand.link); assert (target_is_active (surface)); - _cairo_output_stream_puts (to_context (surface)->stream, "pop "); + _cairo_output_stream_puts (ctx->stream, "pop "); cairo_surface_destroy (&similar->base); return CAIRO_STATUS_SUCCESS; @@ -1497,55 +1557,6 @@ _emit_subsurface_pattern (cairo_script_surface_t *surface, return CAIRO_INT_STATUS_SUCCESS; } -struct script_snapshot { - cairo_surface_t base; -}; - -static cairo_status_t -script_snapshot_finish (void *abstract_surface) -{ - return CAIRO_STATUS_SUCCESS; -} - -static const cairo_surface_backend_t script_snapshot_backend = { - CAIRO_SURFACE_TYPE_SCRIPT, - script_snapshot_finish, -}; - -static void -detach_snapshot (cairo_surface_t *abstract_surface) -{ - cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface; - cairo_script_context_t *ctx = to_context (surface); - - _cairo_output_stream_printf (ctx->stream, - "/s%d undef\n ", - surface->base.unique_id); -} - -static void -attach_snapshot (cairo_script_context_t *ctx, - cairo_surface_t *source) -{ - struct script_snapshot *surface; - - surface = malloc (sizeof (*surface)); - if (unlikely (surface == NULL)) - return; - - _cairo_surface_init (&surface->base, - &script_snapshot_backend, - &ctx->base, - source->content); - - _cairo_output_stream_printf (ctx->stream, - "dup /s%d exch def\n ", - surface->base.unique_id); - - _cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot); - cairo_surface_destroy (&surface->base); -} - static cairo_int_status_t _emit_surface_pattern (cairo_script_surface_t *surface, const cairo_pattern_t *pattern) @@ -1563,7 +1574,7 @@ _emit_surface_pattern (cairo_script_surface_t *surface, snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend); if (snapshot) { _cairo_output_stream_printf (ctx->stream, - "s%d pattern", + "s%d pattern ", snapshot->unique_id); return CAIRO_INT_STATUS_SUCCESS; } diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c index 1e6455e8..adaf7e4c 100644 --- a/src/cairo-surface-snapshot.c +++ b/src/cairo-surface-snapshot.c @@ -60,6 +60,15 @@ _cairo_surface_snapshot_finish (void *abstract_surface) } static cairo_status_t +_cairo_surface_snapshot_flush (void *abstract_surface) +{ + cairo_surface_snapshot_t *surface = abstract_surface; + + cairo_surface_flush (surface->target); + return surface->target->status; +} + +static cairo_status_t _cairo_surface_snapshot_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **extra_out) @@ -110,6 +119,9 @@ static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { NULL, /* copy_page */ NULL, /* show_page */ _cairo_surface_snapshot_get_extents, + NULL, /* old-show-glyphs */ + NULL, /* get-font-options */ + _cairo_surface_snapshot_flush, }; static void diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c index 2fa55401..b57c97b7 100644 --- a/src/cairo-surface-subsurface.c +++ b/src/cairo-surface-subsurface.c @@ -39,6 +39,7 @@ #include "cairo-image-surface-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-offset-private.h" +#include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" static const cairo_surface_backend_t _cairo_surface_subsurface_backend; @@ -290,24 +291,6 @@ struct extra { void *image_extra; }; -static void -cairo_surface_paint_to_target (cairo_surface_t *target, - cairo_surface_subsurface_t *subsurface) -{ - cairo_t *cr; - - cr = cairo_create (target); - - cairo_set_source_surface (cr, - subsurface->target, - - subsurface->extents.x, - - subsurface->extents.y); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - cairo_destroy (cr); -} - static cairo_status_t _cairo_surface_subsurface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, @@ -322,8 +305,7 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac cairo_bool_t ret; if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) { - cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target; - cairo_surface_t *snapshot; + cairo_surface_t *meta, *snapshot; snapshot = _cairo_surface_has_snapshot (&surface->base, &_cairo_image_surface_backend); @@ -333,17 +315,32 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac return CAIRO_STATUS_SUCCESS; } - if (! _cairo_surface_has_snapshot (&meta->base, - &_cairo_image_surface_backend)) - { + meta = surface->target; + if (surface->target->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + meta = _cairo_surface_snapshot_get_target (meta); + + if (! _cairo_surface_has_snapshot (meta, &_cairo_image_surface_backend)) { + cairo_surface_pattern_t pattern; + image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (meta->base.content, + _cairo_image_surface_create_with_content (meta->content, surface->extents.width, surface->extents.height); if (unlikely (image->base.status)) return image->base.status; - cairo_surface_paint_to_target (&image->base, surface); + _cairo_pattern_init_for_surface (&pattern, &image->base); + cairo_matrix_init_translate (&pattern.base.matrix, + -surface->extents.x, -surface->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return status; + } _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL); @@ -387,6 +384,8 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac image->base.is_clear = FALSE; } else { + cairo_surface_pattern_t pattern; + image = (cairo_image_surface_t *) _cairo_image_surface_create_with_pixman_format (NULL, extra->image->pixman_format, @@ -396,7 +395,18 @@ _cairo_surface_subsurface_acquire_source_image (void *abstrac if (unlikely ((status = image->base.status))) goto CLEANUP_IMAGE; - cairo_surface_paint_to_target (&image->base, surface); + _cairo_pattern_init_for_surface (&pattern, &image->base); + cairo_matrix_init_translate (&pattern.base.matrix, + -surface->extents.x, -surface->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (&image->base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return status; + } } *image_out = image; @@ -431,29 +441,32 @@ static cairo_surface_t * _cairo_surface_subsurface_snapshot (void *abstract_surface) { cairo_surface_subsurface_t *surface = abstract_surface; - cairo_surface_subsurface_t *snapshot; - - snapshot = malloc (sizeof (cairo_surface_subsurface_t)); - if (unlikely (snapshot == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + cairo_surface_pattern_t pattern; + cairo_surface_t *clone; + cairo_status_t status; - _cairo_surface_init (&snapshot->base, - &_cairo_surface_subsurface_backend, - NULL, /* device */ - surface->target->content); - snapshot->target = _cairo_surface_snapshot (surface->target); - if (unlikely (snapshot->target->status)) { - cairo_status_t status; - - status = snapshot->target->status; - free (snapshot); - return _cairo_surface_create_in_error (status); + clone = _cairo_surface_create_similar_scratch (surface->target, + surface->target->content, + surface->extents.width, + surface->extents.height); + if (unlikely (clone->status)) + return clone; + + _cairo_pattern_init_for_surface (&pattern, surface->target); + cairo_matrix_init_translate (&pattern.base.matrix, + -surface->extents.x, -surface->extents.y); + pattern.base.filter = CAIRO_FILTER_NEAREST; + status = _cairo_surface_paint (clone, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (clone); + clone = _cairo_surface_create_in_error (status); } - snapshot->base.type = snapshot->target->type; - snapshot->extents = surface->extents; - - return &snapshot->base; + return clone; } static cairo_t * |