[cairo-commit] 3 commits - boilerplate/Makefile.win32.features build/configure.ac.features build/Makefile.win32.features-h configure.ac src/cairo.c src/cairo-composite-rectangles-private.h src/cairo.h src/cairo-mime-surface.c src/cairo-pattern-private.h src/cairo-pdf-operators-private.h src/cairo-recording-surface.c src/cairo-rtree-private.h src/cairo-surface-observer-private.h src/cairo-surface-snapshot-private.h src/cairo-surface-subsurface-private.h src/Makefile.sources src/Makefile.win32.features test/Makefile.refs test/Makefile.sources test/mime-surface.c test/mime-surface.ref.png util/cairo-trace

Chris Wilson ickle at kemper.freedesktop.org
Sun Aug 14 16:37:27 PDT 2011


 boilerplate/Makefile.win32.features      |   10 
 build/Makefile.win32.features-h          |    1 
 build/configure.ac.features              |    1 
 configure.ac                             |    1 
 src/Makefile.sources                     |    1 
 src/Makefile.win32.features              |   10 
 src/cairo-composite-rectangles-private.h |    1 
 src/cairo-mime-surface.c                 |  411 +++++++++++++++++++++++++++++++
 src/cairo-pattern-private.h              |    1 
 src/cairo-pdf-operators-private.h        |    1 
 src/cairo-recording-surface.c            |    2 
 src/cairo-rtree-private.h                |    1 
 src/cairo-surface-observer-private.h     |    2 
 src/cairo-surface-snapshot-private.h     |    2 
 src/cairo-surface-subsurface-private.h   |    2 
 src/cairo.c                              |   11 
 src/cairo.h                              |   89 +++++-
 test/Makefile.refs                       |    1 
 test/Makefile.sources                    |    1 
 test/mime-surface.c                      |  174 +++++++++++++
 test/mime-surface.ref.png                |binary
 util/cairo-trace/trace.c                 |   49 +++
 22 files changed, 750 insertions(+), 22 deletions(-)

New commits:
commit 2e1726a05b7283bc515e215a10c1bfa6ffe33a17
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 15 00:34:35 2011 +0100

    recording: Defend against bad user-input
    
    Adjusting the _cairo_surface_is_recording() to check the value
    in the backend exposed us to a potential NULL pointer dereference
    on validating user-input. So add an explicit status check which
    has the added bonus of being more correct!
    
    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 0a83375..1b46877 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -1596,7 +1596,7 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface,
 
     memset (&bbox, 0, sizeof (bbox));
 
-    if (! _cairo_surface_is_recording (surface)) {
+    if (surface->status || ! _cairo_surface_is_recording (surface)) {
 	_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 	goto DONE;
     }
commit edf2d3e8b21e2bcc1bebb7159d71a4f9e4979a6c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Aug 15 00:26:09 2011 +0100

    check: make check-headers happy
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-composite-rectangles-private.h b/src/cairo-composite-rectangles-private.h
index dd5bd41..f0553ab 100644
--- a/src/cairo-composite-rectangles-private.h
+++ b/src/cairo-composite-rectangles-private.h
@@ -38,6 +38,7 @@
 #define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
 
 #include "cairo-types-private.h"
+#include "cairo-error-private.h"
 
 CAIRO_BEGIN_DECLS
 
diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h
index 647eb0b..46a8ad6 100644
--- a/src/cairo-pattern-private.h
+++ b/src/cairo-pattern-private.h
@@ -36,6 +36,7 @@
 #ifndef CAIRO_PATTERN_PRIVATE_H
 #define CAIRO_PATTERN_PRIVATE_H
 
+#include "cairo-error-private.h"
 #include "cairo-types-private.h"
 #include "cairo-list-private.h"
 
diff --git a/src/cairo-pdf-operators-private.h b/src/cairo-pdf-operators-private.h
index 48ae6b5..6e1ae18 100644
--- a/src/cairo-pdf-operators-private.h
+++ b/src/cairo-pdf-operators-private.h
@@ -43,6 +43,7 @@
 #define CAIRO_PDF_OPERATORS_H
 
 #include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
 #include "cairo-types-private.h"
 
 /* The glyph buffer size is based on the expected maximum glyphs in a
diff --git a/src/cairo-rtree-private.h b/src/cairo-rtree-private.h
index 191c858..11079e7 100644
--- a/src/cairo-rtree-private.h
+++ b/src/cairo-rtree-private.h
@@ -38,6 +38,7 @@
 #define CAIRO_RTREE_PRIVATE_H
 
 #include "cairo-compiler-private.h"
+#include "cairo-error-private.h"
 #include "cairo-types-private.h"
 
 #include "cairo-freelist-private.h"
diff --git a/src/cairo-surface-observer-private.h b/src/cairo-surface-observer-private.h
index 522be6a..7383a73 100644
--- a/src/cairo-surface-observer-private.h
+++ b/src/cairo-surface-observer-private.h
@@ -36,6 +36,8 @@
 #ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
 #define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
 
+#include "cairoint.h" /* cairo_surface_backend_t */
+
 #include "cairo-device-private.h"
 #include "cairo-surface-private.h"
 
diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h
index 7e0ace1..0b1c8d3 100644
--- a/src/cairo-surface-snapshot-private.h
+++ b/src/cairo-surface-snapshot-private.h
@@ -36,6 +36,8 @@
 #ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
 #define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
 
+#include "cairoint.h" /* for cairo_surface_backend_t */
+
 #include "cairo-surface-private.h"
 
 struct _cairo_surface_snapshot {
diff --git a/src/cairo-surface-subsurface-private.h b/src/cairo-surface-subsurface-private.h
index 2109a13..ada09a1 100644
--- a/src/cairo-surface-subsurface-private.h
+++ b/src/cairo-surface-subsurface-private.h
@@ -36,6 +36,8 @@
 #ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
 #define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
 
+#include "cairoint.h" /* for cairo_surface_backend_t */
+
 #include "cairo-surface-private.h"
 
 struct _cairo_surface_subsurface {
commit 2220693a40a4f8d13603b3fb29273ec59fd433bc
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Aug 2 22:31:49 2011 +0100

    Introduce cairo_mime_surface_t
    
    The mime surface is a user-callback surface designed for interfacing
    cairo with an opaque data source. For instance, in a web browser, the
    incoming page may be laid out and rendered to a recording surface before
    all the image data has finished being downloaded. In this circumstance
    we need to pass a place holder to cairo and to supply the image data
    later upon demand.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 793d1f4..8b6a673 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -398,6 +398,16 @@ enabled_cairo_boilerplate_private += $(cairo_boilerplate_image_private)
 enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_image_cxx_sources)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_image_sources)
 
+supported_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_mime_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_mime_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_mime_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_mime_sources)
+
 supported_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_recording_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_recording_private)
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index e49ad6e..84d65f4 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -99,6 +99,7 @@ ifeq ($(CAIRO_HAS_TEST_SURFACES),1)
 	@echo "#define CAIRO_HAS_TEST_SURFACES 1" >> $(top_srcdir)/src/cairo-features.h
 endif
 	@echo "#define CAIRO_HAS_IMAGE_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+	@echo "#define CAIRO_HAS_MIME_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 	@echo "#define CAIRO_HAS_RECORDING_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 	@echo "#define CAIRO_HAS_OBSERVER_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 ifeq ($(CAIRO_HAS_TEE_SURFACE),1)
diff --git a/build/configure.ac.features b/build/configure.ac.features
index 4f774e3..faa762c 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -366,6 +366,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  Image:         yes (always builtin)"
 	echo "  Recording:     yes (always builtin)"
 	echo "  Observer:      yes (always builtin)"
+	echo "  Mime:          yes (always builtin)"
 	echo "  Tee:           $use_tee"
 	echo "  XML:           $use_xml"
 	echo "  Skia:          $use_skia"
diff --git a/configure.ac b/configure.ac
index fc14341..71f8bf3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -620,6 +620,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(image, image, always, [
 
 dnl ===========================================================================
 
+CAIRO_ENABLE_SURFACE_BACKEND(mime, mime, always)
 CAIRO_ENABLE_SURFACE_BACKEND(recording, recording, always)
 CAIRO_ENABLE_SURFACE_BACKEND(observer, observer, always)
 CAIRO_ENABLE_SURFACE_BACKEND(tee, tee, no)
diff --git a/src/Makefile.sources b/src/Makefile.sources
index a47a010..2b563f2 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -151,6 +151,7 @@ cairo_sources = \
 	cairo-lzw.c \
 	cairo-matrix.c \
 	cairo-mesh-pattern-rasterizer.c \
+	cairo-mime-surface.c \
 	cairo-misc.c \
 	cairo-mutex.c \
 	cairo-observer.c \
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index db769ef..4b90d64 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -520,6 +520,16 @@ enabled_cairo_private += $(cairo_image_private)
 enabled_cairo_cxx_sources += $(cairo_image_cxx_sources)
 enabled_cairo_sources += $(cairo_image_sources)
 
+supported_cairo_headers += $(cairo_mime_headers)
+all_cairo_headers += $(cairo_mime_headers)
+all_cairo_private += $(cairo_mime_private)
+all_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+all_cairo_sources += $(cairo_mime_sources)
+enabled_cairo_headers += $(cairo_mime_headers)
+enabled_cairo_private += $(cairo_mime_private)
+enabled_cairo_cxx_sources += $(cairo_mime_cxx_sources)
+enabled_cairo_sources += $(cairo_mime_sources)
+
 supported_cairo_headers += $(cairo_recording_headers)
 all_cairo_headers += $(cairo_recording_headers)
 all_cairo_private += $(cairo_recording_private)
diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c
new file mode 100644
index 0000000..f23c451
--- /dev/null
+++ b/src/cairo-mime-surface.c
@@ -0,0 +1,411 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/**
+ * SECTION:cairo-mime-surface
+ * @Title: Callback Surfaces
+ * @Short_Description: Allows the user to provide a callback to supply image
+ * data upon demand
+ * @See_Also: #cairo_surface_t
+ *
+ * The mime surfaces provide the ability to render from arbitrary sources
+ * not necessarily resident nor immediately usable by Cairo. The user is
+ * given the ability to insert a placeholder surface which can be used
+ * with a pattern and then later supply the actual pixel data upon demand.
+ * This deferred source is given both the sample region for the operation
+ * along with the destination surface such that they may be taken into
+ * account when creating the actual surface to use as the source of pixel
+ * data.
+ *
+ * The reason why it is called the mime surface is two-fold. First it came
+ * about as an extension of the mime-data property to handle deferred
+ * image decoding when rendering to pixel buffers (as opposed to the pass-
+ * through support in PDF and friends.) And then to further rationalise
+ * the name, it is a surface that mimics a real source without ever
+ * holding onto to any pixels of its own - a mime surface.
+ *
+ * The mime-surface callback interface consists of 4 functions. The principal
+ * pair are the acquire/release callbacks which are called when pixel data
+ * is required for an operation (along with the target surface and the sample
+ * extents). The callee must supply a surface that covers the sample area and
+ * set the actual extents of the returned surface in the output rectangle
+ * parameter. The surface does not necessarily have to be an image surface,
+ * but it is expected that an image surface is likely to be the most
+ * convenient for uploading pixel data. (Use
+ * cairo_surface_create_similar_image() to create an image surface that is
+ * optimised for uploading to the target.) The release callback is
+ * subsequently called when the returned surface is no longer needed (before
+ * the operation completes, within the lifetime of the source).
+ *
+ * The other pair of functions are to aide with lifetime management of the
+ * surface with respect to patterns and other users. The destroy callback
+ * allows for the caller to cleanup the associated data when the last
+ * reference to surface is destroyed. The snapshot callback is triggered
+ * when there is an immutable surface pattern referencing the mime-surface
+ * and the mime-surface will be be invalidated. (Since the mime-surface is
+ * read-only and a reference will be held by the pattern, this can only be
+ * triggered through an explicit cairo_surface_finish().) In this
+ * circumstance, we need to clone the source in order to preserve the pixel
+ * data for later use (i.e. we have recorded the pattern). The snapshot
+ * callback provides an interface for the caller to clone the mime-surface
+ * in an efficient manner.  The returned surface may be of any type so long
+ * as it holds all pixel data and remains accessible.
+ */
+
+/**
+ * CAIRO_HAS_MIME_SURFACE:
+ *
+ * Defined if the mime surface backend is available.
+ * The mime surface backend is always built in.
+ *
+ * @Since: 1.12
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-image-surface-private.h"
+
+typedef struct _cairo_mime_surface {
+    cairo_surface_t base;
+
+    cairo_rectangle_int_t extents;
+
+    cairo_mime_surface_acquire_t acquire;
+    cairo_mime_surface_release_t release;
+    cairo_mime_surface_snapshot_t snapshot;
+    cairo_mime_surface_destroy_t destroy;
+
+    /* an explicit pre-allocated member in preference to the general user-data */
+    void *user_data;
+} cairo_mime_surface_t;
+
+static cairo_status_t
+_cairo_mime_surface_finish (void *abstract_surface)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    if (surface->destroy)
+	surface->destroy (&surface->base, surface->user_data);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_mime_surface_get_extents (void			  *abstract_surface,
+				 cairo_rectangle_int_t   *rectangle)
+{
+    cairo_mime_surface_t *surface = abstract_surface;
+
+    *rectangle = surface->extents;
+    return TRUE;
+}
+
+static cairo_status_t
+_cairo_mime_surface_acquire_source_image (void                    *abstract_surface,
+					  //cairo_surface_t	  *target,
+					  cairo_image_surface_t  **image_out,
+					  void                   **image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+    cairo_surface_t *acquired;
+    cairo_surface_t *dummy_target;
+    cairo_rectangle_int_t extents;
+
+    if (mime->acquire == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* Force the callee to populate the extents rectangle */
+    memset (&extents, 0, sizeof (extents));
+
+    /* Masquerade for a flexible user-interface */
+    dummy_target = _cairo_image_surface_create_with_content (mime->base.content, 0, 0);
+    acquired = mime->acquire (&mime->base, mime->user_data,
+			      dummy_target, &mime->extents, &extents);
+    cairo_surface_destroy (dummy_target);
+
+    if (acquired == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* The callee must have supplied us with all the image data */
+    assert (extents.width == mime->extents.width && extents.height == mime->extents.height);
+
+    if (! _cairo_surface_is_image (acquired)) {
+	cairo_status_t status;
+	void *extra = NULL;
+
+	status = _cairo_surface_acquire_source_image (acquired, image_out, &extra);
+	if (unlikely (status)) {
+	    cairo_surface_destroy (acquired);
+	    return status;
+	}
+
+	assert (extra == NULL);
+	*image_extra = acquired;
+    } else {
+	*image_out = (cairo_image_surface_t *) acquired;
+	*image_extra = NULL;
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_mime_surface_release_source_image (void                   *abstract_surface,
+					  cairo_image_surface_t  *image,
+					  void                   *image_extra)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (image_extra) {
+	cairo_surface_destroy (&image->base);
+	image = image_extra;
+    }
+
+    if (mime->release)
+	mime->release (&mime->base, mime->user_data, &image->base);
+}
+
+static cairo_surface_t *
+_cairo_mime_surface_snapshot (void *abstract_surface)
+{
+    cairo_mime_surface_t *mime = abstract_surface;
+
+    if (mime->snapshot == NULL)
+	return NULL;
+
+    return mime->snapshot (&mime->base, mime->user_data);
+}
+
+static const cairo_surface_backend_t cairo_mime_surface_backend = {
+    CAIRO_SURFACE_TYPE_MIME,
+    _cairo_mime_surface_finish,
+
+    NULL, /* Read-only */
+
+    NULL, /* create similar */
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_mime_surface_acquire_source_image,
+    _cairo_mime_surface_release_source_image,
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    _cairo_mime_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+
+
+    NULL, /* paint */
+    NULL, /* mask */
+    NULL, /* stroke */
+    NULL, /* fill */
+    NULL, /* glyphs */
+
+    _cairo_mime_surface_snapshot,
+};
+
+cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+    cairo_mime_surface_t *surface;
+
+    if (width < 0 || height < 0)
+	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+    if (! CAIRO_CONTENT_VALID (content))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
+
+    surface = calloc (1, sizeof (*surface));
+    if (unlikely (surface == NULL))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_surface_init (&surface->base,
+			 &cairo_mime_surface_backend,
+			 NULL, /* device */
+			 content);
+
+    surface->extents.x = 0;
+    surface->extents.y = 0;
+    surface->extents.width  = width;
+    surface->extents.height = height;
+
+    surface->user_data = data;
+
+    return &surface->base;
+}
+
+void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+				      void *data)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->user_data = data;
+}
+
+void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->user_data;
+}
+
+void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t acquire,
+				cairo_mime_surface_release_t release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->acquire = acquire;
+    mime->release = release;
+}
+
+void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t *acquire,
+				cairo_mime_surface_release_t *release)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    if (acquire)
+	*acquire = mime->acquire;
+    if (release)
+	*release = mime->release;
+}
+
+void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+				 cairo_mime_surface_snapshot_t snapshot)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->snapshot = snapshot;
+}
+
+cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->snapshot;
+}
+
+void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+				cairo_mime_surface_destroy_t destroy)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return;
+
+    mime = (cairo_mime_surface_t *)surface;
+    mime->destroy = destroy;
+}
+
+cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface)
+{
+    cairo_mime_surface_t *mime;
+
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+	return NULL;
+
+    if (surface->backend != &cairo_mime_surface_backend)
+	return NULL;
+
+    mime = (cairo_mime_surface_t *)surface;
+    return mime->destroy;
+}
diff --git a/src/cairo.c b/src/cairo.c
index a9f8c6e..6f6d00e 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -212,9 +212,11 @@ _cairo_create_in_error (cairo_status_t status)
  *  with cairo_destroy() when you are done using the #cairo_t.
  *  This function never returns %NULL. If memory cannot be
  *  allocated, a special #cairo_t object will be returned on
- *  which cairo_status() returns %CAIRO_STATUS_NO_MEMORY.
- *  You can use this object normally, but no drawing will
- *  be done.
+ *  which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If
+ *  you attempt to target a surface which does not support
+ *  writing (such as #cairo_mime_surface_t) then a
+ *  %CAIRO_STATUS_WRITE_ERROR will be raised.  You can use this
+ *  object normally, but no drawing will be done.
  **/
 cairo_t *
 cairo_create (cairo_surface_t *target)
@@ -224,6 +226,9 @@ cairo_create (cairo_surface_t *target)
     if (unlikely (target->status))
 	return _cairo_create_in_error (target->status);
 
+    if (target->backend->create_context == NULL)
+	return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
+
     return target->backend->create_context (target);
 
 }
diff --git a/src/cairo.h b/src/cairo.h
index bf2e232..4d0afaf 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -433,6 +433,24 @@ typedef cairo_status_t (*cairo_read_func_t) (void		*closure,
 					     unsigned char	*data,
 					     unsigned int	length);
 
+/**
+ * cairo_rectangle_int_t:
+ * @x: X coordinate of the left side of the rectangle
+ * @y: Y coordinate of the the top side of the rectangle
+ * @width: width of the rectangle
+ * @height: height of the rectangle
+ *
+ * A data structure for holding a rectangle with integer coordinates.
+ *
+ * Since: 1.10
+ **/
+
+typedef struct _cairo_rectangle_int {
+    int x, y;
+    int width, height;
+} cairo_rectangle_int_t;
+
+
 /* Functions for manipulating state objects */
 cairo_public cairo_t *
 cairo_create (cairo_surface_t *target);
@@ -2153,6 +2171,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
  * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
  *   cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_MIME: The surface is a callback (mime) surface, since 1.12
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -2202,7 +2221,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_TEE,
     CAIRO_SURFACE_TYPE_XML,
     CAIRO_SURFACE_TYPE_SKIA,
-    CAIRO_SURFACE_TYPE_SUBSURFACE
+    CAIRO_SURFACE_TYPE_SUBSURFACE,
+    CAIRO_SURFACE_TYPE_MIME
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
@@ -2357,6 +2377,56 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface,
                                      double *width,
                                      double *height);
 
+/* Mime-surface (callback) functions */
+
+typedef cairo_surface_t *(*cairo_mime_surface_acquire_t) (cairo_surface_t *mime_surface,
+							  void *callback_data,
+							  cairo_surface_t *target,
+							  const cairo_rectangle_int_t *sample_extents,
+							  cairo_rectangle_int_t *surface_extents);
+
+typedef void (*cairo_mime_surface_release_t) (cairo_surface_t *mime_surface,
+					      void *callback_data,
+					      cairo_surface_t *image_surface);
+
+typedef cairo_surface_t *(*cairo_mime_surface_snapshot_t) (cairo_surface_t *mime_surface,
+							   void *callback_data);
+typedef void (*cairo_mime_surface_destroy_t) (cairo_surface_t *mime_surface,
+					      void *callback_data);
+
+cairo_public cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height);
+
+cairo_public void
+cairo_mime_surface_set_callback_data (cairo_surface_t *surface,
+				      void *data);
+
+cairo_public void *
+cairo_mime_surface_get_callback_data (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t acquire,
+				cairo_mime_surface_release_t release);
+
+cairo_public void
+cairo_mime_surface_get_acquire (cairo_surface_t *surface,
+				cairo_mime_surface_acquire_t *acquire,
+				cairo_mime_surface_release_t *release);
+cairo_public void
+cairo_mime_surface_set_snapshot (cairo_surface_t *surface,
+				 cairo_mime_surface_snapshot_t snapshot);
+
+cairo_public cairo_mime_surface_snapshot_t
+cairo_mime_surface_get_snapshot (cairo_surface_t *surface);
+
+cairo_public void
+cairo_mime_surface_set_destroy (cairo_surface_t *surface,
+				cairo_mime_surface_destroy_t destroy);
+
+cairo_public cairo_mime_surface_destroy_t
+cairo_mime_surface_get_destroy (cairo_surface_t *surface);
+
 /* Pattern creation functions */
 
 cairo_public cairo_pattern_t *
@@ -2684,23 +2754,6 @@ cairo_matrix_transform_point (const cairo_matrix_t *matrix,
  **/
 typedef struct _cairo_region cairo_region_t;
 
-/**
- * cairo_rectangle_int_t:
- * @x: X coordinate of the left side of the rectangle
- * @y: Y coordinate of the the top side of the rectangle
- * @width: width of the rectangle
- * @height: height of the rectangle
- *
- * A data structure for holding a rectangle with integer coordinates.
- *
- * Since: 1.10
- **/
-
-typedef struct _cairo_rectangle_int {
-    int x, y;
-    int width, height;
-} cairo_rectangle_int_t;
-
 typedef enum _cairo_region_overlap {
     CAIRO_REGION_OVERLAP_IN,		/* completely inside region */
     CAIRO_REGION_OVERLAP_OUT,		/* completely outside region */
diff --git a/test/Makefile.refs b/test/Makefile.refs
index c435bf6..2d291f0 100644
--- a/test/Makefile.refs
+++ b/test/Makefile.refs
@@ -789,6 +789,7 @@ REFERENCE_IMAGES = \
 	mime-data.ref.png \
 	mime-data.script.ref.png \
 	mime-data.svg.ref.png \
+	mime-surface.ref.png \
 	miter-precision.ps2.ref.png \
 	miter-precision.ps3.ref.png \
 	miter-precision.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 21e58e8..a39a201 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -190,6 +190,7 @@ test_sources = \
 	mesh-pattern-overlap.c		        	\
 	mesh-pattern-transformed.c		        \
 	mime-data.c					\
+	mime-surface.c					\
 	miter-precision.c				\
 	move-to-show-surface.c				\
 	new-sub-path.c					\
diff --git a/test/mime-surface.c b/test/mime-surface.c
new file mode 100644
index 0000000..d59112f
--- /dev/null
+++ b/test/mime-surface.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairo-test.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+/* Basic test to exercise the new mime-surface callback. */
+
+#define WIDTH 200
+#define HEIGHT 80
+
+/* Lazy way of determining PNG dimensions... */
+static void
+png_dimensions (const char *filename,
+		cairo_content_t *content, int *width, int *height)
+{
+    cairo_surface_t *surface;
+
+    surface = cairo_image_surface_create_from_png (filename);
+    *content = cairo_surface_get_content (surface);
+    *width = cairo_image_surface_get_width (surface);
+    *height = cairo_image_surface_get_height (surface);
+    cairo_surface_destroy (surface);
+}
+
+static cairo_surface_t *
+png_acquire (cairo_surface_t *mime_surface, void *closure,
+	     cairo_surface_t *target, const cairo_rectangle_int_t *roi,
+	     cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+
+    image = cairo_image_surface_create_from_png (closure);
+    extents->x = extents->y = 0;
+    extents->width = cairo_image_surface_get_width (image);
+    extents->height = cairo_image_surface_get_height (image);
+    return image;
+}
+
+static cairo_surface_t *
+red_acquire (cairo_surface_t *mime_surface, void *closure,
+	     cairo_surface_t *target, const cairo_rectangle_int_t *roi,
+	     cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_t *cr;
+
+    image = cairo_surface_create_similar_image (target,
+						CAIRO_FORMAT_RGB24,
+						roi->width, roi->height);
+    cr = cairo_create (image);
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+
+    *extents = *roi;
+    return image;
+}
+
+static void
+release (cairo_surface_t *mime_surface, void *closure, cairo_surface_t *image)
+{
+    cairo_surface_destroy (image);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *png_filename = "png.png";
+    cairo_surface_t *png, *red;
+    cairo_content_t content;
+    int png_width, png_height;
+    int i, j;
+
+    png_dimensions (png_filename, &content, &png_width, &png_height);
+
+    png = cairo_mime_surface_create ((void*)png_filename, content, png_width, png_height);
+    cairo_mime_surface_set_acquire (png, png_acquire, release);
+
+    red = cairo_mime_surface_create (NULL, CAIRO_CONTENT_COLOR, WIDTH, HEIGHT);
+    cairo_mime_surface_set_acquire (red, red_acquire, release);
+
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    cairo_paint (cr);
+
+    cairo_translate (cr, 0, (HEIGHT-png_height)/2);
+    for (i = 0; i < 4; i++) {
+	for (j = 0; j < 4; j++) {
+	    cairo_surface_t *source;
+	    if ((i ^ j) & 1)
+		source = red;
+	    else
+		source = png;
+	    cairo_set_source_surface (cr, source, 0, 0);
+	    cairo_rectangle (cr, i * WIDTH/4, j * png_height/4, WIDTH/4, png_height/4);
+	    cairo_fill (cr);
+	}
+    }
+
+    cairo_surface_destroy (red);
+    cairo_surface_destroy (png);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+static cairo_test_status_t
+check_status (const cairo_test_context_t *ctx,
+	      cairo_status_t status,
+	      cairo_status_t expected)
+{
+    if (status == expected)
+	return CAIRO_TEST_SUCCESS;
+
+    cairo_test_log (ctx,
+		    "Error: Expected status value %d (%s), received %d (%s)\n",
+		    expected,
+		    cairo_status_to_string (expected),
+		    status,
+		    cairo_status_to_string (status));
+    return CAIRO_TEST_FAILURE;
+}
+
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+    cairo_surface_t *mime;
+    cairo_status_t status;
+    cairo_t *cr;
+
+    /* drawing to a mime-surface is verboten */
+
+    mime = cairo_mime_surface_create (NULL, CAIRO_CONTENT_COLOR, 0, 0);
+    cr = cairo_create (mime);
+    cairo_surface_destroy (mime);
+    status = cairo_status (cr);
+    cairo_destroy (cr);
+    status = check_status (ctx, status, CAIRO_STATUS_WRITE_ERROR);
+    if (status)
+	return status;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (mime_surface,
+	    "Check that the mime-surface embedding works",
+	    "api", /* keywords */
+	    NULL, /* requirements */
+	    WIDTH, HEIGHT,
+	    preamble, draw)
diff --git a/test/mime-surface.ref.png b/test/mime-surface.ref.png
new file mode 100644
index 0000000..ac5e560
Binary files /dev/null and b/test/mime-surface.ref.png differ
diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 614783c..1b42bed 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -3496,6 +3496,55 @@ cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format,
 }
 
 cairo_surface_t *
+cairo_mime_surface_create (void *data, cairo_content_t content, int width, int height)
+{
+    cairo_surface_t *ret;
+
+    _enter_trace ();
+
+    ret = DLCALL (cairo_mime_surface_create, data, content, width, height);
+
+    _emit_line_info ();
+    if (_write_lock ()) {
+	Object *obj = _create_surface (ret);
+	cairo_format_t format;
+	cairo_surface_t *image;
+	cairo_t *cr;
+
+	/* Impossible to accurately record the interaction with a mime-surface
+	 * so just suck all the data into an image upfront */
+	switch (content) {
+	case CAIRO_CONTENT_ALPHA: format = CAIRO_FORMAT_A8; break;
+	case CAIRO_CONTENT_COLOR: format = CAIRO_FORMAT_RGB24; break;
+	default:
+	case CAIRO_CONTENT_COLOR_ALPHA: format = CAIRO_FORMAT_ARGB32; break;
+	}
+
+	_trace_printf ("%% mime-surface\n");
+
+	image = DLCALL (cairo_image_surface_create, format, width, height);
+	cr = DLCALL (cairo_create, image);
+	DLCALL (cairo_set_source_surface, cr, ret, 0, 0);
+	DLCALL (cairo_paint, cr);
+	DLCALL (cairo_destroy, cr);
+
+	_emit_image (image, NULL);
+	DLCALL (cairo_surface_destroy, image);
+	_trace_printf (" dup /s%ld exch def\n",
+		       obj->token);
+
+	obj->width = width;
+	obj->height = height;
+	obj->defined = TRUE;
+	_push_object (obj);
+	_write_unlock ();
+    }
+
+    _exit_trace ();
+    return ret;
+}
+
+cairo_surface_t *
 cairo_surface_create_similar (cairo_surface_t *other,
 			      cairo_content_t content,
 			      int width, int height)


More information about the cairo-commit mailing list