summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2011-08-13 20:07:57 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2011-08-14 12:37:56 +0100
commit99fa5ff6c211b96326484f80fe91ead0860c3a23 (patch)
tree92cf942c2080bfe2217ef04382bb118b088ebe3b
parent79aa04fd50463629b3ab2e2efbcd8084038f6c09 (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.c3
-rw-r--r--src/cairo-image-surface.c33
-rw-r--r--src/cairo-pdf-surface.c19
-rw-r--r--src/cairo-script-surface.c128
-rw-r--r--src/cairo-surface-snapshot-private.h12
-rw-r--r--src/cairo-surface-snapshot.c73
-rw-r--r--src/cairo-surface.c6
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);
}