[cairo-commit] 17 commits - src/cairo-analysis-surface.c src/cairo-image-surface.c src/cairo-paginated-surface.c src/cairo-pdf-surface.c src/cairo-ps-surface.c src/cairo-recording-surface.c src/cairo-recording-surface-private.h src/cairo-script-surface.c src/cairo-surface.c src/cairo-surface-snapshot.c src/cairo-surface-snapshot-private.h src/cairo-surface-subsurface.c src/cairo-surface-subsurface-private.h src/cairo-surface-wrapper.c src/cairo-svg-surface.c src/cairo-xcb-surface-render.c src/cairo-xlib-surface.c util/cairo-script

Chris Wilson ickle at kemper.freedesktop.org
Sun Aug 14 04:38:19 PDT 2011


 src/cairo-analysis-surface.c               |   82 ++++++--
 src/cairo-image-surface.c                  |   41 +++-
 src/cairo-paginated-surface.c              |   10 -
 src/cairo-pdf-surface.c                    |   34 ++-
 src/cairo-ps-surface.c                     |   14 +
 src/cairo-recording-surface-private.h      |   14 +
 src/cairo-recording-surface.c              |  141 ++++++++++++--
 src/cairo-script-surface.c                 |  280 +++++++++++++++++++----------
 src/cairo-surface-snapshot-private.h       |   18 +
 src/cairo-surface-snapshot.c               |   86 +++-----
 src/cairo-surface-subsurface-private.h     |   12 +
 src/cairo-surface-subsurface.c             |  111 ++++++-----
 src/cairo-surface-wrapper.c                |    5 
 src/cairo-surface.c                        |    6 
 src/cairo-svg-surface.c                    |    4 
 src/cairo-xcb-surface-render.c             |    2 
 src/cairo-xlib-surface.c                   |    2 
 util/cairo-script/cairo-script-operators.c |   16 -
 18 files changed, 608 insertions(+), 270 deletions(-)

New commits:
commit 2c885a275349c65ec831738ed917ecd16fdd8c65
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 12:31:12 2011 +0100

    script: Missed break for creating unbounded recording surfaces.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 9576f20..1ac5cb6 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -6085,6 +6085,7 @@ _record (csi_t *ctx)
     switch (array->stack.len) {
     case 0:
 	r = NULL;
+	break;
     case 2:
 	extents.x = extents.y = 0;
 	extents.width = _csi_object_as_real (&array->stack.objects[0]);
commit 0c6b892ce355466b9b7098aadfece0383346de54
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 12:08:42 2011 +0100

    script: take advantage of the polymorphism of the interpreter
    
    If a function expects a surface but receives a context, it automatically
    queries the context's target. We can take advantage of this to short-cut
    a few operations.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 44a9228..a80654c 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -428,29 +428,32 @@ _get_target (cairo_script_surface_t *surface)
 {
     cairo_script_context_t *ctx = to_context (surface);
 
+    if (target_is_active (surface)) {
+	_cairo_output_stream_puts (ctx->stream, "dup ");
+	return;
+    }
+
     if (surface->defined) {
 	_cairo_output_stream_printf (ctx->stream, "s%u ",
 				     surface->base.unique_id);
     } else {
+	int depth = target_depth (surface);
+
 	assert (! cairo_list_is_empty (&surface->operand.link));
-	if (! target_is_active (surface)) {
-	    int depth = target_depth (surface);
-	    if (ctx->active) {
-		_cairo_output_stream_printf (ctx->stream, "%d index ", depth);
-		_cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
+	assert (! target_is_active (surface));
+
+	if (ctx->active) {
+	    _cairo_output_stream_printf (ctx->stream, "%d index ", depth);
+	    _cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
+	} else {
+	    if (depth == 1) {
+		_cairo_output_stream_puts (ctx->stream, "exch ");
 	    } else {
-		if (depth == 1) {
-		    _cairo_output_stream_puts (ctx->stream,
-					       "exch\n");
-		} else {
-		    _cairo_output_stream_printf (ctx->stream,
-						 "%d -1 roll\n",
-						 depth);
-		}
-		_cairo_output_stream_puts (ctx->stream, "/target get ");
+		_cairo_output_stream_printf (ctx->stream,
+					     "%d -1 roll ", depth);
 	    }
-	} else {
-	    _cairo_output_stream_puts (ctx->stream, "/target get ");
+	    target_push (surface);
+	    _cairo_output_stream_puts (ctx->stream, "dup ");
 	}
     }
 }
commit 9dc9f24884e1b580448f12ccd50909b6aee3cb53
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 11:33:27 2011 +0100

    script: leave the tail of the RGB24 data unmolested
    
    We clear past the end of the row so that we don't trigger valgrind
    warning leaving harmless uninitialised bits inside the input image.
    However, for RGB24 the input rowlen is 3*width, whereas we write 4*width
    of data, so we need to take account of that and ensure we clear beyond
    the end of the written data, not the read data.
    
    Fixes reading of RGB24 input.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 5904f5a..9576f20 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -2849,7 +2849,7 @@ _image_read_raw (csi_file_t *src,
 {
     cairo_surface_t *image;
     uint8_t *bp, *data;
-    int rem, len, ret, x, rowlen, stride;
+    int rem, len, ret, x, rowlen, instride, stride;
     cairo_status_t status;
 
     stride = cairo_format_stride_for_width (format, width);
@@ -2870,22 +2870,23 @@ _image_read_raw (csi_file_t *src,
 
     switch (format) {
     case CAIRO_FORMAT_A1:
-	rowlen = (width+7)/8;
+	instride = rowlen = (width+7)/8;
 	break;
     case CAIRO_FORMAT_A8:
-	rowlen = width;
+	instride = rowlen = width;
 	break;
     case CAIRO_FORMAT_RGB16_565:
-	rowlen = 2 * width;
+	instride = rowlen = 2 * width;
 	break;
     case CAIRO_FORMAT_RGB24:
 	rowlen = 3 * width;
+	instride = 4 *width;
 	break;
     default:
     case CAIRO_FORMAT_RGB30:
     case CAIRO_FORMAT_INVALID:
     case CAIRO_FORMAT_ARGB32:
-	rowlen = 4 * width;
+	instride = rowlen = 4 * width;
 	break;
     }
     len = rowlen * height;
@@ -2951,7 +2952,7 @@ _image_read_raw (csi_file_t *src,
 		break;
 	    }
 
-	    memset (row + rowlen, 0, stride - rowlen);
+	    memset (row + instride, 0, stride - instride);
 	}
 
 	/* need to treat last row carefully */
@@ -3039,7 +3040,7 @@ _image_read_raw (csi_file_t *src,
 	    /* stride == width */
 	    break;
 	}
-	memset (data + rowlen, 0, stride - rowlen);
+	memset (data + instride, 0, stride - instride);
     } else {
 #ifndef WORDS_BIGENDIAN
 	switch (format) {
commit 23b1a82e88aab0413f832dbf445df5e302f1c30a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 10:25:29 2011 +0100

    pdf: If the recording surface is unbounded, limit the pattern to the ink extents
    
    It is better than crashing!
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 3a4a402..89cb044 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2377,16 +2377,23 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t  *surface,
     double old_width, old_height;
     cairo_paginated_mode_t old_paginated_mode;
     cairo_rectangle_int_t recording_extents;
-    cairo_bool_t is_bounded;
     cairo_int_status_t status;
     int alpha = 0;
 
     if (_cairo_surface_is_snapshot (source))
 	source = _cairo_surface_snapshot_get_target (source);
 
-    is_bounded = _cairo_surface_get_extents (source,
-					     &recording_extents);
-    assert (is_bounded);
+    if (! _cairo_surface_get_extents (source, &recording_extents)) {
+	cairo_box_t bbox;
+
+	status =
+	    _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) source,
+					       &bbox, NULL);
+	if (unlikely (status))
+	    return status;
+
+	_cairo_box_round_to_rectangle (&bbox, &recording_extents);
+    }
 
     old_width = surface->width;
     old_height = surface->height;
commit 3db39deee2f32f005cb4824669e57c56c0e4ca03
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 09:47:04 2011 +0100

    wrapper: Use the backend->snapshot function
    
    Create the snapshot now, rather than a new lazy snapshot surface.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c
index 1682058..615deb0 100644
--- a/src/cairo-surface-wrapper.c
+++ b/src/cairo-surface-wrapper.c
@@ -573,7 +573,10 @@ _cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t    *wrapper,
 cairo_surface_t *
 _cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
 {
-    return _cairo_surface_snapshot (wrapper->target);
+    if (wrapper->target->backend->snapshot)
+	return wrapper->target->backend->snapshot (wrapper->target);
+
+    return NULL;
 }
 
 cairo_bool_t
commit 320f40ef89cc310c932399f54741987b0683af9f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 09:46:26 2011 +0100

    paginated: Use the backend->snapshot
    
    The high-level function creates a new lazy snapshot which is not what we
    what - we want a snapshot now!
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 2a6666a..d9c6c2f 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -646,7 +646,7 @@ _cairo_paginated_surface_snapshot (void *abstract_other)
 {
     cairo_paginated_surface_t *other = abstract_other;
 
-    return _cairo_surface_snapshot (other->recording_surface);
+    return other->recording_surface->backend->snapshot (other->recording_surface);
 }
 
 static cairo_t *
commit 47874aaceaa49f7b08adaf57e7accb0723668f71
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 09:46:01 2011 +0100

    snapshot: Assert that we do not generate a snapshot clone
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 8fa833e..beb9a07 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -172,10 +172,8 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
 
     if (_cairo_surface_is_snapshot (source))
 	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;
-    }
+    if (_cairo_surface_is_subsurface (source))
+	source = _cairo_surface_subsurface_get_target (source);
 
     status = _cairo_recording_surface_replay_and_create_regions (source, &tmp->base);
     detach_proxy (proxy);
diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c
index adaf7e4..c499e0b 100644
--- a/src/cairo-surface-snapshot.c
+++ b/src/cairo-surface-snapshot.c
@@ -157,6 +157,7 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface)
 
 done:
     status = _cairo_surface_set_error (surface, clone->status);
+    assert (! _cairo_surface_is_snapshot (clone));
     snapshot->target = snapshot->clone = clone;
     snapshot->base.type = clone->type;
 }
@@ -195,7 +196,7 @@ _cairo_surface_snapshot (cairo_surface_t *surface)
     if (surface->snapshot_of != NULL)
 	return cairo_surface_reference (surface);
 
-    if (surface->backend == &_cairo_surface_snapshot_backend)
+    if (_cairo_surface_is_snapshot (surface))
 	return cairo_surface_reference (surface);
 
     snapshot = (cairo_surface_snapshot_t *)
commit 161836ab499237d1b1a1c5e0a51af15997a7ea0f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 01:56:43 2011 +0100

    ps: unwrap recording surface snapshots
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 02909b2..8e9dae4 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -67,6 +67,7 @@
 #include "cairo-paginated-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-private.h"
 #include "cairo-surface-subsurface-private.h"
 #include "cairo-output-stream-private.h"
 #include "cairo-type3-glyph-surface-private.h"
@@ -2541,6 +2542,9 @@ _cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
     old_page_bbox = surface->page_bbox;
     old_cairo_to_ps = surface->cairo_to_ps;
 
+    if (_cairo_surface_is_snapshot (recording_surface))
+	recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
     status =
 	_cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
 					   &bbox,
@@ -2640,6 +2644,9 @@ _cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface,
 						  &surface->cairo_to_ps);
     _cairo_output_stream_printf (surface->stream, "  q\n");
 
+    if (_cairo_surface_is_snapshot (recording_surface))
+	recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
+
     if (recording_surface->content == CAIRO_CONTENT_COLOR) {
 	surface->content = CAIRO_CONTENT_COLOR;
 	_cairo_output_stream_printf (surface->stream,
@@ -2743,10 +2750,15 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t      *surface,
 	    *width  = sub->extents.width;
 	    *height = sub->extents.height;
 	} else {
-	    cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface;
+	    cairo_recording_surface_t *recording_surface;
 	    cairo_box_t bbox;
 	    cairo_rectangle_int_t extents;
 
+	    recording_surface = (cairo_recording_surface_t *) pattern->surface;
+	    if (_cairo_surface_is_snapshot (&recording_surface->base))
+		recording_surface = (cairo_recording_surface_t *)
+		    _cairo_surface_snapshot_get_target (&recording_surface->base);
+
 	    status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
 	    if (unlikely (status))
 		return status;
commit b8f09f08c4ca3569581a3e39056adb0b5a6752ae
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 01:41:44 2011 +0100

    analysis: prevent recursion whilst analysing recording patterns
    
    Thanks to subsurface recursion. There's a pattern here, but no clean
    solution has yet presented itself.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 10dbaf1..8fa833e 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -95,31 +95,81 @@ _cairo_analysis_surface_merge_status (cairo_int_status_t status_a,
     return CAIRO_INT_STATUS_SUCCESS;
 }
 
+struct proxy {
+    cairo_surface_t base;
+    cairo_surface_t *target;
+};
+
+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,
+};
+
+static cairo_surface_t *
+attach_proxy (cairo_surface_t *source,
+	      cairo_surface_t *target)
+{
+    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, target->content);
+
+    proxy->target = target;
+    _cairo_surface_attach_snapshot (source, &proxy->base, NULL);
+
+    return &proxy->base;
+}
+
+static void
+detach_proxy (cairo_surface_t *proxy)
+{
+    cairo_surface_finish (proxy);
+    cairo_surface_destroy (proxy);
+}
+
 static cairo_int_status_t
 _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
 				    const cairo_pattern_t    *pattern)
 {
     const cairo_surface_pattern_t *surface_pattern;
-    cairo_bool_t old_has_ctm;
-    cairo_matrix_t old_ctm, p2d;
+    cairo_analysis_surface_t *tmp;
+    cairo_surface_t *source, *proxy;
+    cairo_matrix_t p2d;
     cairo_status_t status;
-    cairo_surface_t *source;
 
     assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
     surface_pattern = (const cairo_surface_pattern_t *) pattern;
     assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
+    source = surface_pattern->surface;
+
+    proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
+    if (proxy != NULL) {
+	/* nothing untoward found so far */
+	return CAIRO_STATUS_SUCCESS;
+    }
 
-    old_ctm = surface->ctm;
-    old_has_ctm = surface->has_ctm;
+    tmp = (cairo_analysis_surface_t *)
+	_cairo_analysis_surface_create (surface->target);
+    if (unlikely (tmp->base.status))
+	return tmp->base.status;
+    proxy = attach_proxy (source, &tmp->base);
 
     p2d = pattern->matrix;
     status = cairo_matrix_invert (&p2d);
     assert (status == CAIRO_STATUS_SUCCESS);
 
-    cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm);
-    surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
+    cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm);
+    tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm);
 
-    source = surface_pattern->surface;
     if (_cairo_surface_is_snapshot (source))
 	source = _cairo_surface_snapshot_get_target (source);
     if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
@@ -127,10 +177,9 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
 	source = sub->target;
     }
 
-    status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base);
-
-    surface->ctm = old_ctm;
-    surface->has_ctm = old_has_ctm;
+    status = _cairo_recording_surface_replay_and_create_regions (source, &tmp->base);
+    detach_proxy (proxy);
+    cairo_surface_destroy (&tmp->base);
 
     return status;
 }
commit 8f99e926c8b1a8fa7f7e0d828a96bac6dc1fe39c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 01:21:42 2011 +0100

    paginated: unwrap subsurfaces during context creation
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 61c2815..2a6666a 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -50,6 +50,7 @@
 #include "cairo-analysis-surface-private.h"
 #include "cairo-error-private.h"
 #include "cairo-image-surface-private.h"
+#include "cairo-surface-subsurface-private.h"
 
 static const cairo_surface_backend_t cairo_paginated_surface_backend;
 
@@ -652,7 +653,12 @@ static cairo_t *
 _cairo_paginated_context_create (void *target)
 {
     cairo_paginated_surface_t *surface = target;
-    return surface->recording_surface->backend->create_context (surface);
+
+    if (_cairo_surface_is_subsurface (&surface->base))
+	surface = (cairo_paginated_surface_t *)
+	    _cairo_surface_subsurface_get_target (&surface->base);
+
+    return surface->recording_surface->backend->create_context (target);
 }
 
 static const cairo_surface_backend_t cairo_paginated_surface_backend = {
diff --git a/src/cairo-surface-subsurface-private.h b/src/cairo-surface-subsurface-private.h
index 435e1eb..2109a13 100644
--- a/src/cairo-surface-subsurface-private.h
+++ b/src/cairo-surface-subsurface-private.h
@@ -46,4 +46,16 @@ struct _cairo_surface_subsurface {
     cairo_surface_t *target;
 };
 
+static inline cairo_surface_t *
+_cairo_surface_subsurface_get_target (cairo_surface_t *surface)
+{
+    return ((cairo_surface_subsurface_t *) surface)->target;
+}
+
+static inline cairo_bool_t
+_cairo_surface_is_subsurface (cairo_surface_t *surface)
+{
+    return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE;
+}
+
 #endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */
commit 6b3d53646eb7aa3f13a0a6d133ec2ffcd1df8fdd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 00:43:09 2011 +0100

    image: peek through a snapshot to the recording surface behind
    
    Fixes record-* after the recent overhaul.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 0ae09a8..10dbaf1 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -120,7 +120,7 @@ _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)
+    if (_cairo_surface_is_snapshot (source))
 	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;
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 7efcc78..bda05eb 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1406,8 +1406,8 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
 	cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
 	cairo_surface_type_t type;
 
-	if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
-	    source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+	if (_cairo_surface_is_snapshot (&source->base))
+	    source = (cairo_image_surface_t *) _cairo_surface_snapshot_get_target (&source->base);
 
 	type = source->base.backend->type;
 	if (type == CAIRO_SURFACE_TYPE_IMAGE) {
@@ -2963,6 +2963,8 @@ is_recording_pattern (const cairo_pattern_t *pattern)
     surface = ((const cairo_surface_pattern_t *) pattern)->surface;
     if (_cairo_surface_is_paginated (surface))
 	surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+	surface = _cairo_surface_snapshot_get_target (surface);
     return _cairo_surface_is_recording (surface);
 }
 
@@ -2974,6 +2976,8 @@ recording_pattern_get_surface (const cairo_pattern_t *pattern)
     surface = ((const cairo_surface_pattern_t *) pattern)->surface;
     if (_cairo_surface_is_paginated (surface))
 	surface = _cairo_paginated_surface_get_recording (surface);
+    if (_cairo_surface_is_snapshot (surface))
+	surface = _cairo_surface_snapshot_get_target (surface);
     return surface;
 }
 
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 0e53170..3a4a402 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1114,7 +1114,7 @@ _get_source_surface_size (cairo_surface_t         *source,
 	    cairo_recording_surface_t *recording_surface;
 	    cairo_box_t bbox;
 
-	    if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+	    if (_cairo_surface_is_snapshot (source))
 		source = _cairo_surface_snapshot_get_target (source);
 
 	    recording_surface = (cairo_recording_surface_t *) source;
@@ -2381,7 +2381,7 @@ _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t  *surface,
     cairo_int_status_t status;
     int alpha = 0;
 
-    if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+    if (_cairo_surface_is_snapshot (source))
 	source = _cairo_surface_snapshot_get_target (source);
 
     is_bounded = _cairo_surface_get_extents (source,
@@ -5957,7 +5957,7 @@ _cairo_pdf_surface_mask (void			*abstract_surface,
 	cairo_status_t source_status, mask_status;
 
 	status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
-	if (_cairo_status_is_error (status))
+	if (_cairo_int_status_is_error (status))
 	    goto cleanup;
 	source_status = status;
 
@@ -5965,7 +5965,7 @@ _cairo_pdf_surface_mask (void			*abstract_surface,
 	    status = CAIRO_INT_STATUS_UNSUPPORTED;
 	} else {
 	    status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
-	    if (_cairo_status_is_error (status))
+	    if (_cairo_int_status_is_error (status))
 		goto cleanup;
 	}
 	mask_status = status;
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index a93b0ca..44a9228 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -1570,7 +1570,7 @@ _emit_surface_pattern (cairo_script_surface_t *surface,
     surface_pattern = (cairo_surface_pattern_t *) pattern;
     source = surface_pattern->surface;
 
-    if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+    if (_cairo_surface_is_snapshot (source)) {
 	snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
 	if (snapshot) {
 	    _cairo_output_stream_printf (ctx->stream,
diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h
index 4d88b68..7e0ace1 100644
--- a/src/cairo-surface-snapshot-private.h
+++ b/src/cairo-surface-snapshot-private.h
@@ -57,4 +57,10 @@ _cairo_surface_snapshot_get_target (cairo_surface_t *surface)
     return ((cairo_surface_snapshot_t *) surface)->target;
 }
 
+static inline cairo_bool_t
+_cairo_surface_is_snapshot (cairo_surface_t *surface)
+{
+    return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT;
+}
+
 #endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
index b57c97b..73be5f2 100644
--- a/src/cairo-surface-subsurface.c
+++ b/src/cairo-surface-subsurface.c
@@ -316,7 +316,7 @@ _cairo_surface_subsurface_acquire_source_image (void                    *abstrac
 	}
 
 	meta = surface->target;
-	if (surface->target->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+	if (_cairo_surface_is_snapshot (meta))
 	    meta = _cairo_surface_snapshot_get_target (meta);
 
 	if (! _cairo_surface_has_snapshot (meta, &_cairo_image_surface_backend)) {
diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c
index b42795e..d146fe9 100644
--- a/src/cairo-xcb-surface-render.c
+++ b/src/cairo-xcb-surface-render.c
@@ -1084,7 +1084,7 @@ _cairo_xcb_surface_picture (cairo_xcb_surface_t *target,
 		picture->width  = rect.width;
 		picture->height = rect.height;
 	    }
-	} else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+	} else if (_cairo_surface_is_snapshot (source)) {
 	    cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
 	    cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target;
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index b2fa0a7..885dc59 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2412,7 +2412,7 @@ _cairo_xlib_surface_upload(cairo_xlib_surface_t *surface,
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) {
-	if (image->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+	if (_cairo_surface_is_snapshot (&image->base)) {
 	    image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target;
 	    extents.x = extents.y = 0;
 	    extents.width = image->width;
commit 8a90b22897b6460b3396b9959383131039bd9ce2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Aug 14 00:25:15 2011 +0100

    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 at chris-wilson.co.uk>

diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index eb12624..1b00c72 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 2724f8f..a93b0ca 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 1e6455e..adaf7e4 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 2fa5540..b57c97b 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 *
commit 7971c678f18b9a078dc921e8c9a9d8175038cd1c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 21:47:19 2011 +0100

    subsurface: call the high-level cairo_surface_flush
    
    And not the backend directly as this bypasses the extra steps taken in
    the higher level to do common actions such as detach snapshots.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
index 870537e..2fa5540 100644
--- a/src/cairo-surface-subsurface.c
+++ b/src/cairo-surface-subsurface.c
@@ -224,13 +224,9 @@ static cairo_status_t
 _cairo_surface_subsurface_flush (void *abstract_surface)
 {
     cairo_surface_subsurface_t *surface = abstract_surface;
-    cairo_status_t status;
-
-    status = CAIRO_STATUS_SUCCESS;
-    if (surface->target->backend->flush != NULL)
-	status = surface->target->backend->flush (surface->target);
 
-    return status;
+    cairo_surface_flush (surface->target);
+    return surface->target->status;
 }
 
 static cairo_status_t
commit bca9400aec5c11e402758a2e06c8be560e64b78f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 21:22:21 2011 +0100

    recording: break self-copy loop
    
    This is the root cause of the issue why we never succeeded in
    implementing deferred snapshot correctly; that is we decoupled the
    source from the target in the upper layers before we make the coupling
    inside the lowest level of recording surface. By deferring the copy, we
    never saw the detach-snapshot in time.
    
    Fortunately this was only an issue for backends that implemented strong
    immutable source semantics! The oversight implies that we need to push
    down a similar flush mechanism into all backends.
    
    Fixes self-copy.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 4d562c6..eb12624 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -605,6 +605,20 @@ _cairo_recording_surface_extents (cairo_recording_surface_t *surface)
     return &surface->extents;
 }
 
+static void
+_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface)
+{
+    cairo_surface_flush (&surface->base);
+}
+
+static cairo_status_t
+_cairo_recording_surface_commit (cairo_recording_surface_t *surface,
+				 cairo_command_header_t *command)
+{
+    _cairo_recording_surface_break_self_copy_loop (surface);
+    return _cairo_array_append (&surface->commands, &command);
+}
+
 static cairo_int_status_t
 _cairo_recording_surface_paint (void			  *abstract_surface,
 				cairo_operator_t	   op,
@@ -649,7 +663,7 @@ _cairo_recording_surface_paint (void			  *abstract_surface,
     if (unlikely (status))
 	goto CLEANUP_COMMAND;
 
-    status = _cairo_array_append (&surface->commands, &command);
+    status = _cairo_recording_surface_commit (surface, &command->header);
     if (unlikely (status))
 	goto CLEANUP_SOURCE;
 
@@ -708,7 +722,7 @@ _cairo_recording_surface_mask (void			*abstract_surface,
     if (unlikely (status))
 	goto CLEANUP_SOURCE;
 
-    status = _cairo_array_append (&surface->commands, &command);
+    status = _cairo_recording_surface_commit (surface, &command->header);
     if (unlikely (status))
 	goto CLEANUP_MASK;
 
@@ -784,7 +798,7 @@ _cairo_recording_surface_stroke (void			*abstract_surface,
     command->tolerance = tolerance;
     command->antialias = antialias;
 
-    status = _cairo_array_append (&surface->commands, &command);
+    status = _cairo_recording_surface_commit (surface, &command->header);
     if (unlikely (status))
 	goto CLEANUP_STYLE;
 
@@ -854,7 +868,7 @@ _cairo_recording_surface_fill (void			*abstract_surface,
     command->tolerance = tolerance;
     command->antialias = antialias;
 
-    status = _cairo_array_append (&surface->commands, &command);
+    status = _cairo_recording_surface_commit (surface, &command->header);
     if (unlikely (status))
 	goto CLEANUP_PATH;
 
@@ -963,7 +977,7 @@ _cairo_recording_surface_show_text_glyphs (void				*abstract_surface,
 
     command->scaled_font = cairo_scaled_font_reference (scaled_font);
 
-    status = _cairo_array_append (&surface->commands, &command);
+    status = _cairo_recording_surface_commit (surface, &command->header);
     if (unlikely (status))
 	goto CLEANUP_SCALED_FONT;
 
commit 9f6428c517d222d7e222a5407e6f0b1fe1647c12
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 21:15:39 2011 +0100

    recording: remove the duplicate 'content' field
    
    Just use the member in the base class.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h
index 2c70d83..b7d0ebf 100644
--- a/src/cairo-recording-surface-private.h
+++ b/src/cairo-recording-surface-private.h
@@ -124,8 +124,6 @@ typedef union _cairo_command {
 typedef struct _cairo_recording_surface {
     cairo_surface_t base;
 
-    cairo_content_t content;
-
     /* A recording-surface is logically unbounded, but when used as a
      * source we need to render it to an image, so we need a size at
      * which to create that image. */
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 0daf675..4d562c6 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -388,7 +388,6 @@ cairo_recording_surface_create (cairo_content_t		 content,
 			 NULL, /* device */
 			 content);
 
-    surface->content = content;
 
     surface->unbounded = TRUE;
 
@@ -507,7 +506,7 @@ _cairo_recording_surface_acquire_source_image_transformed (void			   *abstract_s
 
     width = surface->extents.width * device_transform->xx;
     height = surface->extents.height * device_transform->yy;
-    image = _cairo_image_surface_create_with_content (surface->content,
+    image = _cairo_image_surface_create_with_content (surface->base.content,
 						      width, height);
     if (unlikely (image->status))
 	return image->status;
@@ -1019,7 +1018,6 @@ _cairo_recording_surface_snapshot (void *abstract_other)
     surface->extents_pixels = other->extents_pixels;
     surface->extents = other->extents;
     surface->unbounded = other->unbounded;
-    surface->content = other->content;
 
     surface->base.is_clear = TRUE;
 
@@ -1471,7 +1469,7 @@ _recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
     cairo_surface_t *analysis_surface;
     cairo_status_t status;
 
-    null_surface = _cairo_null_surface_create (surface->content);
+    null_surface = _cairo_null_surface_create (surface->base.content);
     analysis_surface = _cairo_analysis_surface_create (null_surface);
     cairo_surface_destroy (null_surface);
 
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index 2ffbe21..2724f8f 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -1049,7 +1049,7 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface,
 	extents = &r;
 
     similar = _cairo_script_surface_create_internal (to_context (surface),
-						     source->content,
+						     source->base.content,
 						     extents,
 						     NULL);
     if (unlikely (similar->base.status))
@@ -1059,7 +1059,7 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface,
 
     _cairo_output_stream_printf (to_context (surface)->stream,
 				 "//%s ",
-				 _content_to_string (source->content));
+				 _content_to_string (source->base.content));
     if (extents) {
 	_cairo_output_stream_printf (to_context (surface)->stream,
 				     "[%f %f %f %f]",
diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c
index 248c20c..870537e 100644
--- a/src/cairo-surface-subsurface.c
+++ b/src/cairo-surface-subsurface.c
@@ -341,7 +341,7 @@ _cairo_surface_subsurface_acquire_source_image (void                    *abstrac
 					   &_cairo_image_surface_backend))
 	{
 	    image = (cairo_image_surface_t *)
-		_cairo_image_surface_create_with_content (meta->content,
+		_cairo_image_surface_create_with_content (meta->base.content,
 							  surface->extents.width,
 							  surface->extents.height);
 	    if (unlikely (image->base.status))
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 88acad2..2cf91ea 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1388,7 +1388,7 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
     }
 
     paginated_surface = _cairo_svg_surface_create_for_document (document,
-								source->content,
+								source->base.content,
 								source->extents_pixels.width,
 								source->extents_pixels.height);
     if (unlikely (paginated_surface->status))
@@ -1427,7 +1427,7 @@ _cairo_svg_surface_emit_recording_surface (cairo_svg_document_t      *document,
 				     svg_surface->height);
     }
 
-    if (source->content == CAIRO_CONTENT_ALPHA) {
+    if (source->base.content == CAIRO_CONTENT_ALPHA) {
 	_cairo_svg_surface_emit_alpha_filter (document);
 	_cairo_output_stream_printf (document->xml_node_defs,
 				     "<g id=\"surface%d\" "
commit 4a990925e91a91c1d9d5a81f5ad91c1000bf5cce
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 20:29:22 2011 +0100

    script: Support unbounded native recording surfaces
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h
index c370b67..2c70d83 100644
--- a/src/cairo-recording-surface-private.h
+++ b/src/cairo-recording-surface-private.h
@@ -174,6 +174,18 @@ _cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording,
 				   cairo_box_t *bbox,
 				   const cairo_matrix_t *transform);
 
+static inline cairo_bool_t
+_cairo_recording_surface_get_bounds (cairo_surface_t *surface,
+				     cairo_rectangle_t *extents)
+{
+    cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface;
+    if (recording->unbounded)
+	return FALSE;
+
+    *extents = recording->extents_pixels;
+    return TRUE;
+}
+
 cairo_private cairo_bool_t
 _cairo_surface_is_recording (const cairo_surface_t *surface);
 
diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c
index c1d8c83..2ffbe21 100644
--- a/src/cairo-script-surface.c
+++ b/src/cairo-script-surface.c
@@ -166,8 +166,7 @@ static const cairo_surface_backend_t _cairo_script_surface_backend;
 static cairo_script_surface_t *
 _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
 				       cairo_content_t content,
-				       double width,
-				       double height,
+				       cairo_rectangle_t *extents,
 				       cairo_surface_t *passthrough);
 
 static void
@@ -1042,34 +1041,34 @@ _emit_recording_surface_pattern (cairo_script_surface_t *surface,
 {
     cairo_script_implicit_context_t old_cr;
     cairo_script_surface_t *similar;
+    cairo_rectangle_t r, *extents;
     cairo_status_t status;
-    cairo_box_t bbox;
-    cairo_rectangle_int_t rect;
 
-    /* first measure the extents */
-    status = _cairo_recording_surface_get_bbox (source, &bbox, NULL);
-    if (unlikely (status))
-	return status;
-
-    /* convert to extents so that it matches the public api */
-    _cairo_box_round_to_rectangle (&bbox, &rect);
+    extents = NULL;
+    if (_cairo_recording_surface_get_bounds (&source->base, &r))
+	extents = &r;
 
     similar = _cairo_script_surface_create_internal (to_context (surface),
 						     source->content,
-						     rect.width,
-						     rect.height,
+						     extents,
 						     NULL);
     if (unlikely (similar->base.status))
 	return similar->base.status;
 
-    cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y);
     similar->base.is_clear = TRUE;
 
-    _get_target (surface);
     _cairo_output_stream_printf (to_context (surface)->stream,
-				 "%d %d //%s similar dup context\n",
-				 rect.width, rect.height,
+				 "//%s ",
 				 _content_to_string (source->content));
+    if (extents) {
+	_cairo_output_stream_printf (to_context (surface)->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");
     target_push (similar);
     similar->emitted = TRUE;
 
@@ -1977,6 +1976,7 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
     cairo_script_surface_t *surface, *other = abstract_surface;
     cairo_surface_t *passthrough = NULL;
     cairo_script_context_t *ctx;
+    cairo_rectangle_t extents;
     cairo_status_t status;
 
     ctx = to_context (other);
@@ -2005,10 +2005,11 @@ _cairo_script_surface_create_similar (void	       *abstract_surface,
 	}
     }
 
-    surface = _cairo_script_surface_create_internal (ctx,
-						     content,
-						     width, height,
-						     passthrough);
+    extents.x = extents.y = 0;
+    extents.width = width;
+    extents.height = height;
+    surface = _cairo_script_surface_create_internal (ctx, content,
+						     &extents, passthrough);
     cairo_surface_destroy (passthrough);
 
     if (unlikely (surface->base.status)) {
@@ -3617,8 +3618,7 @@ _cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
 static cairo_script_surface_t *
 _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
 				       cairo_content_t content,
-				       double width,
-				       double height,
+				       cairo_rectangle_t *extents,
 				       cairo_surface_t *passthrough)
 {
     cairo_script_surface_t *surface;
@@ -3640,8 +3640,13 @@ _cairo_script_surface_create_internal (cairo_script_context_t *ctx,
     _cairo_surface_clipper_init (&surface->clipper,
 				 _cairo_script_surface_clipper_intersect_clip_path);
 
-    surface->width = width;
-    surface->height = height;
+    surface->width = surface->height = -1;
+    if (extents) {
+	surface->width = extents->width;
+	surface->height = extents->height;
+	cairo_surface_set_device_offset (&surface->base,
+					 -extents->x, -extents->y);
+    }
 
     surface->emitted = FALSE;
     surface->defined = FALSE;
@@ -3755,15 +3760,23 @@ cairo_script_surface_create (cairo_device_t *device,
 			     double width,
 			     double height)
 {
+    cairo_rectangle_t *extents, r;
+
     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
 	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
     if (unlikely (device->status))
 	return _cairo_surface_create_in_error (device->status);
 
+    extents = NULL;
+    if (width > 0 && height > 0) {
+	r.x = r.y = 0;
+	r.width  = width;
+	r.height = height;
+	extents = &r;
+    }
     return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
-						   content,
-						   width, height,
+						   content, extents,
 						   NULL)->base;
 }
 
@@ -3772,6 +3785,7 @@ cairo_script_surface_create_for_target (cairo_device_t *device,
 					cairo_surface_t *target)
 {
     cairo_rectangle_int_t extents;
+    cairo_rectangle_t rect, *r;
 
     if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
 	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
@@ -3782,13 +3796,15 @@ cairo_script_surface_create_for_target (cairo_device_t *device,
     if (unlikely (target->status))
 	return _cairo_surface_create_in_error (target->status);
 
-    if (! _cairo_surface_get_extents (target, &extents))
-	extents.width = extents.height = -1;
-
+    r = NULL;
+    if (_cairo_surface_get_extents (target, &extents)) {
+	rect.x = rect.y = 0;
+	rect.width = extents.width;
+	rect.height = extents.height;
+	r= &rect;
+    }
     return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
-						   target->content,
-						   extents.width,
-						   extents.height,
+						   target->content, r,
 						   target)->base;
 }
 
@@ -3796,8 +3812,7 @@ cairo_status_t
 cairo_script_from_recording_surface (cairo_device_t *device,
 				     cairo_surface_t *recording_surface)
 {
-    cairo_box_t bbox;
-    cairo_rectangle_int_t extents;
+    cairo_rectangle_t r, *extents;
     cairo_surface_t *surface;
     cairo_status_t status;
 
@@ -3813,22 +3828,17 @@ cairo_script_from_recording_surface (cairo_device_t *device,
     if (unlikely (! _cairo_surface_is_recording (recording_surface)))
 	return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
-    status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
-					   &bbox, NULL);
-    if (unlikely (status))
-	return status;
-
-    _cairo_box_round_to_rectangle (&bbox, &extents);
+    extents = NULL;
+    if (_cairo_recording_surface_get_bounds (recording_surface, &r))
+	extents = &r;
 
     surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
 						      recording_surface->content,
-						      extents.width,
-						      extents.height,
+						      extents,
 						      NULL)->base;
     if (unlikely (surface->status))
 	return surface->status;
 
-    cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
     status = _cairo_recording_surface_replay (recording_surface, surface);
     cairo_surface_destroy (surface);
 
commit 99fa5ff6c211b96326484f80fe91ead0860c3a23
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Aug 13 20:07:57 2011 +0100

    snapshot: Defer acquisition
    
    Fixes 'xlib-expose-event' but triggers an infinite loop in self-copy.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index 459bc81..0ae09a8 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 7d8a303..7efcc78 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 dd5f68c..0e53170 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 e62f31f..c1d8c83 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 bbb2bf2..4d88b68 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 56d108b..1e6455e 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 298e96b..7c231da 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);
 }
 


More information about the cairo-commit mailing list