diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2011-08-13 20:07:57 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2011-08-14 12:37:56 +0100 |
commit | 99fa5ff6c211b96326484f80fe91ead0860c3a23 (patch) | |
tree | 92cf942c2080bfe2217ef04382bb118b088ebe3b | |
parent | 79aa04fd50463629b3ab2e2efbcd8084038f6c09 (diff) |
snapshot: Defer acquisition
Fixes 'xlib-expose-event' but triggers an infinite loop in self-copy.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | src/cairo-analysis-surface.c | 3 | ||||
-rw-r--r-- | src/cairo-image-surface.c | 33 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 19 | ||||
-rw-r--r-- | src/cairo-script-surface.c | 128 | ||||
-rw-r--r-- | src/cairo-surface-snapshot-private.h | 12 | ||||
-rw-r--r-- | src/cairo-surface-snapshot.c | 73 | ||||
-rw-r--r-- | src/cairo-surface.c | 6 |
7 files changed, 181 insertions, 93 deletions
diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index 459bc8163..0ae09a8c1 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -41,6 +41,7 @@ #include "cairo-error-private.h" #include "cairo-paginated-private.h" #include "cairo-recording-surface-private.h" +#include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" #include "cairo-region-private.h" @@ -119,6 +120,8 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); source = surface_pattern->surface; + if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = _cairo_surface_snapshot_get_target (source); if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source; source = sub->target; diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 7d8a303d8..7efcc7804 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -738,6 +738,36 @@ _cairo_image_surface_create_similar (void *abstract_other, } static cairo_surface_t * +_cairo_image_surface_snapshot (void *abstract_surface) +{ + cairo_image_surface_t *image = abstract_surface; + cairo_image_surface_t *clone; + + clone = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + image->pixman_format, + image->width, + image->height, + 0); + if (unlikely (clone->base.status)) + return &clone->base; + + if (clone->stride == image->stride) { + memcpy (clone->data, image->data, clone->stride * clone->height); + } else { + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + image->width, image->height); + } + clone->base.is_clear = FALSE; + return &clone->base; +} + + +static cairo_surface_t * _cairo_image_surface_map_to_image (void *abstract_other, const cairo_rectangle_int_t *extents) { @@ -4823,8 +4853,7 @@ const cairo_surface_backend_t _cairo_image_surface_backend = { _cairo_image_surface_stroke, _cairo_image_surface_fill, _cairo_image_surface_glyphs, - NULL, /* show_text_glyphs */ - NULL, /* snapshot */ + _cairo_image_surface_snapshot, NULL, /* is_similar */ }; diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index dd5f68c19..0e531701d 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -56,6 +56,7 @@ #include "cairo-paginated-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-clipper-private.h" +#include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" #include "cairo-type3-glyph-surface-private.h" @@ -1110,9 +1111,13 @@ _get_source_surface_size (cairo_surface_t *source, *height = sub->extents.height; } else { - cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; + cairo_recording_surface_t *recording_surface; cairo_box_t bbox; + if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = _cairo_surface_snapshot_get_target (source); + + recording_surface = (cairo_recording_surface_t *) source; status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); if (unlikely (status)) return status; @@ -2366,7 +2371,7 @@ BAIL: static cairo_status_t _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, - cairo_surface_t *recording_surface, + cairo_surface_t *source, cairo_pdf_resource_t resource) { double old_width, old_height; @@ -2376,7 +2381,11 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, cairo_int_status_t status; int alpha = 0; - is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents); + if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) + source = _cairo_surface_snapshot_get_target (source); + + is_bounded = _cairo_surface_get_extents (source, + &recording_extents); assert (is_bounded); old_width = surface->width; @@ -2396,7 +2405,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) { + if (cairo_surface_get_content (source) == CAIRO_CONTENT_COLOR) { status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); if (unlikely (status)) return status; @@ -2408,7 +2417,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface, surface->height); } - status = _cairo_recording_surface_replay_region (recording_surface, + status = _cairo_recording_surface_replay_region (source, NULL, &surface->base, CAIRO_RECORDING_REGION_NATIVE); diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index e62f31f76..c1d8c8343 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -1280,6 +1280,31 @@ _undef (void *data) free (def); } +static cairo_status_t +attach_undef_tag (cairo_script_context_t *ctx, cairo_surface_t *surface) +{ + struct def *tag; + cairo_status_t status; + + tag = malloc (sizeof (*tag)); + if (unlikely (tag == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + tag->ctx = ctx; + tag->tag = surface->unique_id; + tag->user_data = &surface->user_data; + cairo_list_add (&tag->link, &ctx->defines); + status = _cairo_user_data_array_set_data (&surface->user_data, + (cairo_user_data_key_t *) ctx, + tag, _undef); + if (unlikely (status)) { + free (tag); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + static cairo_int_status_t _emit_image_surface (cairo_script_surface_t *surface, cairo_image_surface_t *image) @@ -1290,7 +1315,6 @@ _emit_image_surface (cairo_script_surface_t *surface, cairo_int_status_t status, status2; const uint8_t *mime_data; unsigned long mime_data_length; - struct def *tag; if (_cairo_user_data_array_get_data (&image->base.user_data, (cairo_user_data_key_t *) ctx)) @@ -1384,21 +1408,9 @@ _emit_image_surface (cairo_script_surface_t *surface, cairo_surface_destroy (&clone->base); } - tag = malloc (sizeof (*tag)); - if (unlikely (tag == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - tag->ctx = ctx; - tag->tag = image->base.unique_id; - tag->user_data = &image->base.user_data; - cairo_list_add (&tag->link, &ctx->defines); - status = _cairo_user_data_array_set_data (&image->base.user_data, - (cairo_user_data_key_t *) ctx, - tag, _undef); - if (unlikely (status)) { - free (tag); + status = attach_undef_tag (ctx, &image->base); + if (unlikely (status)) return status; - } _cairo_output_stream_printf (ctx->stream, "dup /s%u exch def ", @@ -1443,21 +1455,15 @@ static cairo_int_status_t _emit_image_surface_pattern (cairo_script_surface_t *surface, cairo_surface_t *source) { - cairo_surface_t *snapshot; cairo_image_surface_t *image; cairo_status_t status; void *extra; - /* XXX keeping a copy is nasty, but we want to hook into the surface's - * lifetime. Using a snapshot is a convenient method. - */ - snapshot = _cairo_surface_snapshot (source); - status = _cairo_surface_acquire_source_image (snapshot, &image, &extra); + status = _cairo_surface_acquire_source_image (source, &image, &extra); if (likely (status == CAIRO_STATUS_SUCCESS)) { status = _emit_image_surface (surface, image); - _cairo_surface_release_source_image (snapshot, image, extra); + _cairo_surface_release_source_image (source, image, extra); } - cairo_surface_destroy (snapshot); return status; } @@ -1492,19 +1498,82 @@ _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) { + cairo_script_context_t *ctx = to_context (surface); cairo_surface_pattern_t *surface_pattern; - cairo_surface_t *source; + cairo_surface_t *source, *snapshot; + cairo_surface_t *take_snapshot = NULL; cairo_int_status_t status; surface_pattern = (cairo_surface_pattern_t *) pattern; source = surface_pattern->surface; - if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) - source = ((cairo_surface_snapshot_t *) source)->target; + if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) { + snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend); + if (snapshot) { + _cairo_output_stream_printf (ctx->stream, + "s%d pattern", + snapshot->unique_id); + return CAIRO_INT_STATUS_SUCCESS; + } + + if (_cairo_surface_snapshot_is_reused (source)) + take_snapshot = source; + + source = _cairo_surface_snapshot_get_target (source); + } switch ((int) source->backend->type) { case CAIRO_SURFACE_TYPE_RECORDING: @@ -1523,7 +1592,10 @@ _emit_surface_pattern (cairo_script_surface_t *surface, if (unlikely (status)) return status; - _cairo_output_stream_puts (to_context (surface)->stream, "pattern"); + if (take_snapshot) + attach_snapshot (ctx, take_snapshot); + + _cairo_output_stream_puts (ctx->stream, "pattern"); return CAIRO_INT_STATUS_SUCCESS; } @@ -1797,6 +1869,8 @@ _emit_path (cairo_script_surface_t *surface, double x2 = _cairo_fixed_to_double (box.p2.x); double y2 = _cairo_fixed_to_double (box.p2.y); + assert (x1 > -9999); + _cairo_output_stream_printf (ctx->stream, " %f %f %f %f rectangle", x1, y1, x2 - x1, y2 - y1); diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h index bbb2bf2a0..4d88b68e7 100644 --- a/src/cairo-surface-snapshot-private.h +++ b/src/cairo-surface-snapshot-private.h @@ -45,4 +45,16 @@ struct _cairo_surface_snapshot { cairo_surface_t *clone; }; +static inline cairo_bool_t +_cairo_surface_snapshot_is_reused (cairo_surface_t *surface) +{ + return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2; +} + +static inline cairo_surface_t * +_cairo_surface_snapshot_get_target (cairo_surface_t *surface) +{ + return ((cairo_surface_snapshot_t *) surface)->target; +} + #endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */ diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c index 56d108bd1..1e6455e8e 100644 --- a/src/cairo-surface-snapshot.c +++ b/src/cairo-surface-snapshot.c @@ -117,7 +117,7 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) { cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface; cairo_image_surface_t *image; - cairo_image_surface_t *clone; + cairo_surface_t *clone; void *extra; cairo_status_t status; @@ -127,41 +127,26 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) * been lost. */ + if (snapshot->target->backend->snapshot != NULL) { + clone = snapshot->target->backend->snapshot (snapshot->target); + if (clone != NULL) + goto done; + } + + /* XXX copy to a similar surface, leave acquisition till later? */ status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra); if (unlikely (status)) { snapshot->target = _cairo_surface_create_in_error (status); status = _cairo_surface_set_error (surface, status); return; } - - clone = (cairo_image_surface_t *) - _cairo_image_surface_create_with_pixman_format (NULL, - image->pixman_format, - image->width, - image->height, - 0); - if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) { - if (clone->stride == image->stride) { - memcpy (clone->data, image->data, image->stride * image->height); - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - image->pixman_image, NULL, clone->pixman_image, - 0, 0, - 0, 0, - 0, 0, - image->width, image->height); - } - clone->base.is_clear = FALSE; - - snapshot->clone = &clone->base; - } else { - snapshot->clone = &clone->base; - status = _cairo_surface_set_error (surface, clone->base.status); - } - + clone = image->base.backend->snapshot (&image->base); _cairo_surface_release_source_image (snapshot->target, image, extra); - snapshot->target = snapshot->clone; - snapshot->base.type = snapshot->target->type; + +done: + status = _cairo_surface_set_error (surface, clone->status); + snapshot->target = snapshot->clone = clone; + snapshot->base.type = clone->type; } /** @@ -192,38 +177,14 @@ _cairo_surface_snapshot (cairo_surface_t *surface) if (unlikely (surface->status)) return _cairo_surface_create_in_error (surface->status); - if (surface->finished) + if (unlikely (surface->finished)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); if (surface->snapshot_of != NULL) return cairo_surface_reference (surface); - if (surface->backend->snapshot != NULL) { - cairo_surface_t *snap; - - snap = _cairo_surface_has_snapshot (surface, surface->backend); - if (snap != NULL) - return cairo_surface_reference (snap); - - snap = surface->backend->snapshot (surface); - if (snap != NULL) { - if (unlikely (snap->status)) - return snap; - - status = _cairo_surface_copy_mime_data (snap, surface); - if (unlikely (status)) { - cairo_surface_destroy (snap); - return _cairo_surface_create_in_error (status); - } - - snap->device_transform = surface->device_transform; - snap->device_transform_inverse = surface->device_transform_inverse; - - _cairo_surface_attach_snapshot (surface, snap, NULL); - - return snap; - } - } + if (surface->backend == &_cairo_surface_snapshot_backend) + return cairo_surface_reference (surface); snapshot = (cairo_surface_snapshot_t *) _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend); diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 298e96b05..7c231dae4 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -328,12 +328,12 @@ _cairo_surface_detach_snapshot (cairo_surface_t *snapshot) { assert (snapshot->snapshot_of != NULL); - snapshot->snapshot_of = NULL; - cairo_list_del (&snapshot->snapshot); - if (snapshot->snapshot_detach != NULL) snapshot->snapshot_detach (snapshot); + snapshot->snapshot_of = NULL; + cairo_list_del (&snapshot->snapshot); + cairo_surface_destroy (snapshot); } |