[cairo-commit] 5 commits - src/cairo.h src/cairo-output-stream.c src/cairo-output-stream-private.h src/cairo-png.c src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-svg-surface.c test/Makefile.am test/mime-data.c test/mime-data.pdf.ref.png test/mime-data.ps.ref.png test/mime-data.ref.png test/mime-data.svg.ref.png test/png.png util/cairo-trace

Chris Wilson ickle at kemper.freedesktop.org
Wed Nov 5 10:13:27 PST 2008


 src/cairo-output-stream-private.h |    5 
 src/cairo-output-stream.c         |   28 +++
 src/cairo-png.c                   |  127 +++++++++------
 src/cairo-surface-fallback.c      |   20 +-
 src/cairo-surface.c               |   17 +-
 src/cairo-svg-surface.c           |   38 ++++
 src/cairo.h                       |    1 
 test/Makefile.am                  |    1 
 test/mime-data.c                  |   67 +++++---
 test/mime-data.pdf.ref.png        |binary
 test/mime-data.ps.ref.png         |binary
 test/mime-data.ref.png            |binary
 test/mime-data.svg.ref.png        |binary
 test/png.png                      |binary
 util/cairo-trace/trace.c          |  303 ++++++++++++++++++++++++++------------
 15 files changed, 429 insertions(+), 178 deletions(-)

New commits:
commit ec559822cfe6df0006ca2db2aa3a11699326865c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Nov 5 18:11:36 2008 +0000

    [trace] Use the mime-type image representation
    
    When emitting image data, first check to see if we have a pre-compressed
    alternate representation.

diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 2ca184f..df2ec5c 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -989,8 +989,62 @@ _emit_data (const void *data, unsigned int length)
     _write_data_end (&stream);
 }
 
-static void
-_emit_image (cairo_surface_t *image)
+static const char *
+_format_to_string (cairo_format_t format)
+{
+    const char *names[] = {
+	"ARGB32",	/* CAIRO_FORMAT_ARGB32 */
+	"RGB24",	/* CAIRO_FORMAT_RGB24 */
+	"A8",		/* CAIRO_FORMAT_A8 */
+	"A1"		/* CAIRO_FORMAT_A1 */
+    };
+    return names[format];
+}
+
+static const char *
+_status_to_string (cairo_status_t status)
+{
+    const char *names[] = {
+	"STATUS_SUCCESS",
+	"STATUS_NO_MEMORY",
+	"STATUS_INVALID_RESTORE",
+	"STATUS_INVALID_POP_GROUP",
+	"STATUS_NO_CURRENT_POINT",
+	"STATUS_INVALID_MATRIX",
+	"STATUS_INVALID_STATUS",
+	"STATUS_NULL_POINTER",
+	"STATUS_INVALID_STRING",
+	"STATUS_INVALID_PATH_DATA",
+	"STATUS_READ_ERROR",
+	"STATUS_WRITE_ERROR",
+	"STATUS_SURFACE_FINISHED",
+	"STATUS_SURFACE_TYPE_MISMATCH",
+	"STATUS_PATTERN_TYPE_MISMATCH",
+	"STATUS_INVALID_CONTENT",
+	"STATUS_INVALID_FORMAT",
+	"STATUS_INVALID_VISUAL",
+	"STATUS_FILE_NOT_FOUND",
+	"STATUS_INVALID_DASH",
+	"STATUS_INVALID_DSC_COMMENT",
+	"STATUS_INVALID_INDEX",
+	"STATUS_CLIP_NOT_REPRESENTABLE",
+	"STATUS_TEMP_FILE_ERROR",
+	"STATUS_INVALID_STRIDE",
+	"STATUS_FONT_TYPE_MISMATCH",
+	"STATUS_USER_FONT_IMMUTABLE",
+	"STATUS_USER_FONT_ERROR",
+	"STATUS_NEGATIVE_COUNT",
+	"STATUS_INVALID_CLUSTERS",
+	"STATUS_INVALID_SLANT",
+	"STATUS_INVALID_WEIGHT"
+    };
+    return names[status];
+}
+
+static void CAIRO_PRINTF_FORMAT(2, 3)
+_emit_image (cairo_surface_t *image,
+	     const char *info,
+	     ...)
 {
     int stride, row, width, height;
     cairo_format_t format;
@@ -998,6 +1052,20 @@ _emit_image (cairo_surface_t *image)
     uint8_t *rowdata;
     uint8_t *data;
     struct _data_stream stream;
+    const char *mime_types[] = {
+	CAIRO_MIME_TYPE_JPEG,
+	CAIRO_MIME_TYPE_PNG,
+	NULL
+    }, **mime_type;
+
+    if (cairo_surface_status (image)) {
+	fprintf (logfile,
+		 "dict\n"
+		 "  /status //%s set\n"
+		 "  image",
+		 _status_to_string (cairo_surface_status (image)));
+	return;
+    }
 
     width = cairo_image_surface_get_width (image);
     height = cairo_image_surface_get_height (image);
@@ -1005,6 +1073,43 @@ _emit_image (cairo_surface_t *image)
     format = cairo_image_surface_get_format (image);
     data = cairo_image_surface_get_data (image);
 
+    fprintf (logfile,
+	     "dict\n"
+	     "  /width %d set\n"
+	     "  /height %d set\n"
+	     "  /format //%s set\n",
+	     width, height,
+	     _format_to_string (format));
+    if (info != NULL) {
+	va_list ap;
+
+	va_start (ap, info);
+	vfprintf (logfile, info, ap);
+	va_end (ap);
+    }
+
+    for (mime_type = mime_types; *mime_type; mime_type++) {
+	const unsigned char *mime_data;
+	unsigned int mime_length;
+
+	cairo_surface_get_mime_data (image, *mime_type,
+				     &mime_data, &mime_length);
+	if (mime_data != NULL) {
+	    fprintf (logfile,
+		     "  /mime-type (%s) set\n"
+		     "  /source <~",
+		     *mime_type);
+	    _write_base85_data_start (&stream);
+	    _write_base85_data (&stream, mime_data, mime_length);
+	    _write_base85_data_end (&stream);
+	    fprintf (logfile,
+		     "~> set\n"
+		     "  image");
+	    return;
+	}
+    }
+
+    fprintf (logfile, "  /source ");
     _write_data_start (&stream);
 
 #ifdef WORDS_BIGENDIAN
@@ -1039,16 +1144,13 @@ _emit_image (cairo_surface_t *image)
 	}
 	break;
     default:
-	_write_data_end (&stream);
-	return;
+	break;
     }
 #else
     if (stride > ARRAY_LENGTH (row_stack)) {
 	rowdata = malloc (stride);
-	if (rowdata == NULL) {
-	    _write_data_end (&stream);
-	    return;
-	}
+	if (rowdata == NULL)
+	    goto BAIL;
     } else
 	rowdata = row_stack;
 
@@ -1099,8 +1201,87 @@ _emit_image (cairo_surface_t *image)
     if (rowdata != row_stack)
 	free (rowdata);
 
+BAIL:
     _write_data_end (&stream);
 #endif
+    fprintf (logfile,
+	     " /deflate filter set\n"
+	     "  image");
+}
+
+static void
+_encode_string_literal (char *out, int max,
+			const char *utf8, int len)
+{
+    char c;
+    const char *end;
+
+    *out++ = '(';
+    max--;
+
+    if (utf8 == NULL)
+	goto DONE;
+
+    if (len < 0)
+	len = strlen (utf8);
+    end = utf8 + len;
+
+    while (utf8 < end) {
+	if (max < 5)
+	    break;
+
+	switch ((c = *utf8++)) {
+	case '\n':
+	    *out++ = '\\';
+	    *out++ = 'n';
+	    max -= 2;
+	    break;
+	case '\r':
+	    *out++ = '\\';
+	    *out++ = 'n';
+	    max -= 2;
+	case '\t':
+	    *out++ = '\\';
+	    *out++ = 't';
+	    max -= 2;
+	    break;
+	case '\b':
+	    *out++ = '\\';
+	    *out++ = 'b';
+	    max -= 2;
+	    break;
+	case '\f':
+	    *out++ = '\\';
+	    *out++ = 'r';
+	    max -= 2;
+	    break;
+	case '\\':
+	case '(':
+	case ')':
+	    *out++ = '\\';
+	    *out++ = c;
+	    max -= 2;
+	    break;
+	default:
+	    if (isprint (c) || isspace (c)) {
+		*out++ = c;
+	    } else {
+		int octal = 0;
+		while (c) {
+		    octal *= 10;
+		    octal += c&7;
+		    c /= 8;
+		}
+		octal = snprintf (out, max, "\\%03d", octal);
+		out += octal;
+		max -= octal;
+	    }
+	    break;
+	}
+    }
+DONE:
+    *out++ = ')';
+    *out = '\0';
 }
 
 static void
@@ -1357,17 +1538,9 @@ _emit_source_image (cairo_surface_t *surface)
     DLCALL (cairo_paint, cr);
     DLCALL (cairo_destroy, cr);
 
+    _emit_image (image, NULL);
     fprintf (logfile,
-	     "dict\n"
-	     "  /width %d set\n"
-	     "  /height %d set\n"
-	     "  /format //ARGB32 set\n"
-	     "  /source ",
-	     obj->width, obj->height);
-    _emit_image (image);
-    fprintf (logfile,
-	     " /deflate filter set\n"
-	     "  image set_source_image ");
+	     " set_source_image ");
     DLCALL (cairo_surface_destroy, image);
 
     _get_object (SURFACE, surface)->foreign = false;
@@ -2238,7 +2411,7 @@ _emit_glyphs (cairo_scaled_font_t *font,
 	struct _data_stream stream;
 
 	if (num_glyphs == 1) {
-	    fprintf (logfile, "[%g %g <%x>]", x, y,  glyphs->index);
+	    fprintf (logfile, "[%g %g <%lx>]", x, y,  glyphs->index);
 	} else {
 	    fprintf (logfile, "[%g %g <~", x, y);
 	    _write_base85_data_start (&stream);
@@ -2407,18 +2580,6 @@ cairo_append_path (cairo_t *cr, const cairo_path_t *path)
     }
 }
 
-static const char *
-_format_to_string (cairo_format_t format)
-{
-    const char *names[] = {
-	"ARGB32",	/* CAIRO_FORMAT_ARGB32 */
-	"RGB24",	/* CAIRO_FORMAT_RGB24 */
-	"A8",		/* CAIRO_FORMAT_A8 */
-	"A1"		/* CAIRO_FORMAT_A1 */
-    };
-    return names[format];
-}
-
 cairo_surface_t *
 cairo_image_surface_create (cairo_format_t format, int width, int height)
 {
@@ -2452,22 +2613,11 @@ cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format,
 {
     cairo_surface_t *ret;
     long surface_id;
-    const char *format_str;
 
     ret = DLCALL (cairo_image_surface_create_for_data, data, format, width, height, stride);
     surface_id = _create_surface_id (ret);
 
     if (data != NULL && _write_lock ()) {
-	format_str = _format_to_string (format);
-
-	fprintf (logfile,
-		 "dict\n"
-		 "  /width %d set\n"
-		 "  /height %d set\n"
-		 "  /format //%s set\n",
-		 width, height,
-		 format_str);
-
 	/* cairo_image_surface_create_for_data() is both used to supply
 	 * foreign pixel data to cairo and in order to read pixels back.
 	 * Defer grabbing the pixel contents until we have to, but only for
@@ -2476,17 +2626,26 @@ cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format,
 	 */
 	if (width * height < 128) {
 	    fprintf (logfile, "  /source ");
-	    _emit_image (ret);
-	    fprintf (logfile, " /deflate filter set\n");
+	    _emit_image (ret, NULL);
+	    fprintf (logfile,
+		     " dup /s%ld exch def\n",
+		     surface_id);
 	} else {
+	    fprintf (logfile,
+		     "dict\n"
+		     "  /width %d set\n"
+		     "  /height %d set\n"
+		     "  /format //%s set\n"
+		     "  image dup /s%ld exch def\n",
+		     width, height,
+		     _format_to_string (format),
+		     surface_id);
+
 	    _get_object (SURFACE, ret)->width  = width;
 	    _get_object (SURFACE, ret)->height = height;
 	    _get_object (SURFACE, ret)->foreign = true;
 	}
 
-	fprintf (logfile,
-		 "  image dup /s%ld exch def\n",
-		 surface_id);
 	_get_object (SURFACE, ret)->defined = true;
 	_push_operand (SURFACE, ret);
 	_write_unlock ();
@@ -3243,37 +3402,20 @@ cairo_surface_t *
 cairo_image_surface_create_from_png (const char *filename)
 {
     cairo_surface_t *ret;
-    cairo_format_t format;
-    int width;
-    int height;
     long surface_id;
-    const char *format_str;
 
     ret = DLCALL (cairo_image_surface_create_from_png, filename);
 
-    width = cairo_image_surface_get_width (ret);
-    height = cairo_image_surface_get_height (ret);
-    format = cairo_image_surface_get_format (ret);
-
     surface_id = _create_surface_id (ret);
-    format_str = _format_to_string (format);
 
     if (_write_lock ()) {
+	char filename_string[4096];
+
+	_encode_string_literal (filename_string, sizeof (filename_string),
+				filename, -1);
+	_emit_image (ret, "  /filename %s set\n", filename_string);
 	fprintf (logfile,
-		 "dict\n"
-		 "  /width %d set\n"
-		 "  /height %d set\n"
-		 "  /format //%s set\n"
-		 "  /filename ",
-		 width, height, format_str);
-	_emit_string_literal (filename, -1);
-	fprintf (logfile,
-		 " set\n"
-		 "  /source ");
-	_emit_image (ret);
-	fprintf (logfile,
-		 " /deflate filter set\n"
-		 "  image dup /s%ld exch def\n",
+		 " dup /s%ld exch def\n",
 		 surface_id);
 	_get_object (SURFACE, ret)->defined = true;
 	_push_operand (SURFACE, ret);
@@ -3287,32 +3429,15 @@ cairo_surface_t *
 cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, void *closure)
 {
     cairo_surface_t *ret;
-    cairo_format_t format;
-    int width;
-    int height;
-    const char *format_str;
     long surface_id;
 
     ret = DLCALL (cairo_image_surface_create_from_png_stream, read_func, closure);
-    width = cairo_image_surface_get_width (ret);
-    height = cairo_image_surface_get_height (ret);
-    format = cairo_image_surface_get_format (ret);
-
     surface_id = _create_surface_id (ret);
-    format_str = _format_to_string (format);
 
     if (_write_lock ()) {
+	_emit_image (ret, NULL);
 	fprintf (logfile,
-		 "dict\n"
-		 "  /width %d set\n"
-		 "  /height %d set\n"
-		 "  /format //%s set\n"
-		 "  /source ",
-		 width, height, format_str);
-	_emit_image (ret);
-	fprintf (logfile,
-		 " /deflate filter set\n"
-		 "  image dup /s%ld exch def\n",
+		 " dup /s%ld exch def\n",
 		 surface_id);
 	_get_object (SURFACE, ret)->defined = true;
 	_push_operand (SURFACE, ret);
commit d63267e4e7e148836dcfc4c8e2a8396ddaab70d8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Nov 5 17:18:51 2008 +0000

    [test] Update mime-data to check image/png
    
    Add a "image/png" mime-type test.

diff --git a/test/Makefile.am b/test/Makefile.am
index d9d4d39..131ed60 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1012,6 +1012,7 @@ EXTRA_DIST +=		\
 6x13.pcf		\
 make-html.pl		\
 jpeg.jpg		\
+png.png			\
 romedalen.jpg		\
 romedalen.png		\
 scarab.jpg		\
diff --git a/test/mime-data.c b/test/mime-data.c
index a387d67..87fe74c 100644
--- a/test/mime-data.c
+++ b/test/mime-data.c
@@ -31,27 +31,23 @@
 /* Basic test to exercise the new mime-data embedding. */
 
 static cairo_status_t
-read_jpg_file (const cairo_test_context_t *ctx,
-	       unsigned char **data_out,
-	       unsigned int *length_out)
+read_file (const cairo_test_context_t *ctx,
+	   const char *filename,
+	   unsigned char **data_out,
+	   unsigned int *length_out)
 {
-    /* Deliberately use a non-matching jpeg image, so that we can identify
-     * when the JPEG representation is used in preference to the image
-     * surface.
-     */
-    const char jpg_filename[] = "jpeg.jpg";
     FILE *file;
     unsigned char *buf;
     unsigned int len;
 
-    file = fopen (jpg_filename, "rb");
+    file = fopen (filename, "rb");
     if (file == NULL) {
-	char filename[4096];
+	char path[4096];
 
 	/* try again with srcdir */
-	snprintf (filename, sizeof (filename),
-		  "%s/%s", ctx->srcdir, jpg_filename);
-	file = fopen (filename, "rb");
+	snprintf (path, sizeof (path),
+		  "%s/%s", ctx->srcdir, filename);
+	file = fopen (path, "rb");
     }
     if (file == NULL) {
 	switch (errno) {
@@ -79,37 +75,62 @@ read_jpg_file (const cairo_test_context_t *ctx,
 }
 
 static cairo_test_status_t
-draw (cairo_t *cr, int width, int height)
+paint_file (cairo_t *cr,
+	    const char *filename, const char *mime_type,
+	    int x, int y)
 {
     const cairo_test_context_t *ctx = cairo_test_get_context (cr);
     cairo_surface_t *image;
-    unsigned char *jpg_data;
-    unsigned int jpg_len;
+    unsigned char *mime_data;
+    unsigned int mime_length;
     cairo_status_t status;
 
-    status = read_jpg_file (ctx, &jpg_data, &jpg_len);
-    if (status) {
+    /* Deliberately use a non-matching MIME images, so that we can identify
+     * when the MIME representation is used in preference to the plain image
+     * surface.
+     */
+    status = read_file (ctx, filename, &mime_data, &mime_length);
+    if (status)
 	return cairo_test_status_from_status (ctx, status);
-    }
 
     image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 50);
-    status = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JPEG,
-					  jpg_data, jpg_len, free);
+
+    status = cairo_surface_set_mime_data (image, mime_type,
+					  mime_data, mime_length, free);
     if (status) {
 	cairo_surface_destroy (image);
 	return cairo_test_status_from_status (ctx, status);
     }
 
-    cairo_set_source_surface (cr, image, 0, 0);
+    cairo_set_source_surface (cr, image, x, y);
     cairo_surface_destroy (image);
+
     cairo_paint (cr);
 
     return CAIRO_TEST_SUCCESS;
 }
 
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char jpg_filename[] = "jpeg.jpg";
+    const char png_filename[] = "png.png";
+    cairo_test_status_t status;
+
+    status = paint_file (cr, jpg_filename, CAIRO_MIME_TYPE_JPEG, 0, 0);
+    if (status)
+	return status;
+
+    status = paint_file (cr, png_filename, CAIRO_MIME_TYPE_PNG, 0, 50);
+    if (status)
+	return status;
+
+    return CAIRO_TEST_SUCCESS;
+}
+
 CAIRO_TEST (mime_data,
 	    "Check that the mime-data embedding works",
 	    "jpeg, api", /* keywords */
 	    NULL, /* requirements */
-	    200, 50,
+	    200, 100,
 	    NULL, draw)
diff --git a/test/mime-data.pdf.ref.png b/test/mime-data.pdf.ref.png
index cf53a61..ebfcb79 100644
Binary files a/test/mime-data.pdf.ref.png and b/test/mime-data.pdf.ref.png differ
diff --git a/test/mime-data.ps.ref.png b/test/mime-data.ps.ref.png
index cf53a61..ebfcb79 100644
Binary files a/test/mime-data.ps.ref.png and b/test/mime-data.ps.ref.png differ
diff --git a/test/mime-data.ref.png b/test/mime-data.ref.png
index 782a2ee..ccbf1b6 100644
Binary files a/test/mime-data.ref.png and b/test/mime-data.ref.png differ
diff --git a/test/mime-data.svg.ref.png b/test/mime-data.svg.ref.png
new file mode 100644
index 0000000..96a7bec
Binary files /dev/null and b/test/mime-data.svg.ref.png differ
diff --git a/test/png.png b/test/png.png
new file mode 100644
index 0000000..56c428e
Binary files /dev/null and b/test/png.png differ
commit e4ec5c762f6d01cc5af28dc0a256e268a04101aa
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Nov 5 16:44:49 2008 +0000

    [svg] Embed original PNG data.
    
    Embed the attached PNG representation of a surface in preference to
    re-encoding the surface.

diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 8c23242..cfc56c4 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1016,6 +1016,40 @@ _cairo_surface_base64_encode_jpeg (cairo_surface_t       *surface,
 }
 
 static cairo_int_status_t
+_cairo_surface_base64_encode_png (cairo_surface_t       *surface,
+				  cairo_output_stream_t *output)
+{
+    const unsigned char *mime_data;
+    unsigned int mime_data_length;
+    base64_write_closure_t info;
+    cairo_status_t status;
+
+    cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
+				 &mime_data, &mime_data_length);
+    if (mime_data == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_output_stream_printf (output, "data:image/png;base64,");
+
+    info.output = output;
+    info.in_mem = 0;
+    info.trailing = 0;
+
+    status = base64_write_func (&info, mime_data, mime_data_length);
+    if (status)
+	return status;
+
+    if (info.in_mem > 0) {
+	memset (info.src + info.in_mem, 0, 3 - info.in_mem);
+	info.trailing = 3 - info.in_mem;
+	info.in_mem = 3;
+	status = base64_write_func (&info, NULL, 0);
+    }
+
+    return status;
+}
+
+static cairo_int_status_t
 _cairo_surface_base64_encode (cairo_surface_t       *surface,
 			      cairo_output_stream_t *output)
 {
@@ -1026,6 +1060,10 @@ _cairo_surface_base64_encode (cairo_surface_t       *surface,
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	return status;
 
+    status = _cairo_surface_base64_encode_png (surface, output);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return status;
+
     info.output = output;
     info.in_mem = 0;
     info.trailing = 0;
commit e40d62a0fe96b8b937017a3bc2f18766c411ec41
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Nov 5 16:38:34 2008 +0000

    [png] Attach the png representation to cairo_surface_create_from_png().
    
    Attach the original png data as an alternate representation for image
    surfaces created by cairo_surface_create_from_png().

diff --git a/src/cairo-output-stream-private.h b/src/cairo-output-stream-private.h
index 787d061..6f10483 100644
--- a/src/cairo-output-stream-private.h
+++ b/src/cairo-output-stream-private.h
@@ -161,6 +161,11 @@ _cairo_memory_stream_copy (cairo_output_stream_t *base,
 cairo_private int
 _cairo_memory_stream_length (cairo_output_stream_t *stream);
 
+cairo_private cairo_status_t
+_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
+			      unsigned char **data_out,
+			      unsigned int *length_out);
+
 cairo_private cairo_output_stream_t *
 _cairo_null_stream_create (void);
 
diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
index bae6ac4..c31c2da 100644
--- a/src/cairo-output-stream.c
+++ b/src/cairo-output-stream.c
@@ -656,6 +656,32 @@ _cairo_memory_stream_create (void)
     return &stream->base;
 }
 
+cairo_status_t
+_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream,
+			      unsigned char **data_out,
+			      unsigned int *length_out)
+{
+    memory_stream_t *stream;
+    cairo_status_t status;
+
+    status = abstract_stream->status;
+    if (status)
+	return _cairo_output_stream_destroy (abstract_stream);
+
+    stream = (memory_stream_t *) abstract_stream;
+
+    *length_out = _cairo_array_num_elements (&stream->array);
+    *data_out = malloc (*length_out);
+    if (*data_out == NULL) {
+	status = _cairo_output_stream_destroy (abstract_stream);
+	assert (status == CAIRO_STATUS_SUCCESS);
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
+    memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out);
+
+    return _cairo_output_stream_destroy (abstract_stream);
+}
+
 void
 _cairo_memory_stream_copy (cairo_output_stream_t *base,
 			   cairo_output_stream_t *dest)
@@ -670,7 +696,7 @@ _cairo_memory_stream_copy (cairo_output_stream_t *base,
 	return;
     }
 
-    _cairo_output_stream_write (dest, 
+    _cairo_output_stream_write (dest,
 				_cairo_array_index (&stream->array, 0),
 				_cairo_array_num_elements (&stream->array));
 }
diff --git a/src/cairo-png.c b/src/cairo-png.c
index 4721825..b4aeb08 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -37,10 +37,19 @@
  */
 
 #include "cairoint.h"
+#include "cairo-output-stream-private.h"
 
+#include <stdio.h>
 #include <errno.h>
 #include <png.h>
 
+struct png_read_closure_t {
+    cairo_read_func_t		 read_func;
+    void			*closure;
+    cairo_output_stream_t	*png_data;
+};
+
+
 /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
 static void
 unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
@@ -436,9 +445,43 @@ convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
     }
 }
 
+static cairo_status_t
+stdio_read_func (void *closure, unsigned char *data, unsigned int size)
+{
+    while (size) {
+	size_t ret;
+
+	ret = fread (data, 1, size, closure);
+	size -= ret;
+	data += ret;
+
+	if (size && (feof (closure) || ferror (closure)))
+	    return _cairo_error (CAIRO_STATUS_READ_ERROR);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+stream_read_func (png_structp png, png_bytep data, png_size_t size)
+{
+    cairo_status_t status;
+    struct png_read_closure_t *png_closure;
+
+    png_closure = png_get_io_ptr (png);
+    status = png_closure->read_func (png_closure->closure, data, size);
+    if (status) {
+	cairo_status_t *error = png_get_error_ptr (png);
+	if (*error == CAIRO_STATUS_SUCCESS)
+	    *error = status;
+	png_error (png, NULL);
+    }
+
+    _cairo_output_stream_write (png_closure->png_data, data, size);
+}
+
 static cairo_surface_t *
-read_png (png_rw_ptr	read_func,
-	  void		*closure)
+read_png (struct png_read_closure_t *png_closure)
 {
     cairo_surface_t *surface;
     png_struct *png = NULL;
@@ -450,6 +493,8 @@ read_png (png_rw_ptr	read_func,
     unsigned int i;
     cairo_format_t format;
     cairo_status_t status;
+    unsigned char *mime_data;
+    unsigned int mime_data_length;
 
     /* XXX: Perhaps we'll want some other error handlers? */
     png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
@@ -467,7 +512,8 @@ read_png (png_rw_ptr	read_func,
 	goto BAIL;
     }
 
-    png_set_read_fn (png, closure, read_func);
+    png_closure->png_data = _cairo_memory_stream_create ();
+    png_set_read_fn (png, png_closure, stream_read_func);
 
     status = CAIRO_STATUS_SUCCESS;
 #ifdef PNG_SETJMP_SUPPORTED
@@ -589,6 +635,27 @@ read_png (png_rw_ptr	read_func,
     _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
     data = NULL;
 
+    status = _cairo_memory_stream_destroy (png_closure->png_data,
+					   &mime_data,
+					   &mime_data_length);
+    if (status) {
+	cairo_surface_destroy (surface);
+	surface = _cairo_surface_create_in_error (status);
+	goto BAIL;
+    }
+
+    status = cairo_surface_set_mime_data (surface,
+					  CAIRO_MIME_TYPE_PNG,
+					  mime_data,
+					  mime_data_length,
+					  free);
+    if (status) {
+	free (mime_data);
+	cairo_surface_destroy (surface);
+	surface = _cairo_surface_create_in_error (status);
+	goto BAIL;
+    }
+
  BAIL:
     if (row_pointers)
 	free (row_pointers);
@@ -600,25 +667,6 @@ read_png (png_rw_ptr	read_func,
     return surface;
 }
 
-static void
-stdio_read_func (png_structp png, png_bytep data, png_size_t size)
-{
-    FILE *fp;
-
-    fp = png_get_io_ptr (png);
-    while (size) {
-	size_t ret = fread (data, 1, size, fp);
-	size -= ret;
-	data += ret;
-	if (size && (feof (fp) || ferror (fp))) {
-	    cairo_status_t *error = png_get_error_ptr (png);
-	    if (*error == CAIRO_STATUS_SUCCESS)
-		*error = _cairo_error (CAIRO_STATUS_READ_ERROR);
-	    png_error (png, NULL);
-	}
-    }
-}
-
 /**
  * cairo_image_surface_create_from_png:
  * @filename: name of PNG file to load
@@ -638,11 +686,11 @@ stdio_read_func (png_structp png, png_bytep data, png_size_t size)
 cairo_surface_t *
 cairo_image_surface_create_from_png (const char *filename)
 {
-    FILE *fp;
+    struct png_read_closure_t png_closure;
     cairo_surface_t *surface;
 
-    fp = fopen (filename, "rb");
-    if (fp == NULL) {
+    png_closure.closure = fopen (filename, "rb");
+    if (png_closure.closure == NULL) {
 	cairo_status_t status;
 	switch (errno) {
 	case ENOMEM:
@@ -658,32 +706,13 @@ cairo_image_surface_create_from_png (const char *filename)
 	return _cairo_surface_create_in_error (status);
     }
 
-    surface = read_png (stdio_read_func, fp);
-
-    fclose (fp);
+    png_closure.read_func = stdio_read_func;
 
-    return surface;
-}
+    surface = read_png (&png_closure);
 
-struct png_read_closure_t {
-    cairo_read_func_t		 read_func;
-    void			*closure;
-};
+    fclose (png_closure.closure);
 
-static void
-stream_read_func (png_structp png, png_bytep data, png_size_t size)
-{
-    cairo_status_t status;
-    struct png_read_closure_t *png_closure;
-
-    png_closure = png_get_io_ptr (png);
-    status = png_closure->read_func (png_closure->closure, data, size);
-    if (status) {
-	cairo_status_t *error = png_get_error_ptr (png);
-	if (*error == CAIRO_STATUS_SUCCESS)
-	    *error = status;
-	png_error (png, NULL);
-    }
+    return surface;
 }
 
 /**
@@ -707,5 +736,5 @@ cairo_image_surface_create_from_png_stream (cairo_read_func_t	read_func,
     png_closure.read_func = read_func;
     png_closure.closure = closure;
 
-    return read_png (stream_read_func, &png_closure);
+    return read_png (&png_closure);
 }
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index f8f8902..bb29a9f 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1036,6 +1036,11 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface)
     cairo_surface_pattern_t pattern;
     cairo_image_surface_t *image;
     void *image_extra;
+    const char *mime_types[] = {
+	CAIRO_MIME_TYPE_JPEG,
+	CAIRO_MIME_TYPE_PNG,
+	NULL
+    }, **mime_type;
 
     status = _cairo_surface_acquire_source_image (surface,
 						  &image, &image_extra);
@@ -1075,15 +1080,12 @@ _cairo_surface_fallback_snapshot (cairo_surface_t *surface)
     snapshot->device_transform = surface->device_transform;
     snapshot->device_transform_inverse = surface->device_transform_inverse;
 
-    /* XXX Need to copy all known image representations...
-     * For now, just copy "image/jpeg", but in future we should construct
-     * an array of known types and iterate.
-     */
-    status = _cairo_surface_copy_mime_data (snapshot, surface,
-	                                    CAIRO_MIME_TYPE_JPEG);
-    if (status) {
-	cairo_surface_destroy (snapshot);
-	return _cairo_surface_create_in_error (status);
+    for (mime_type = mime_types; *mime_type; mime_type++) {
+	status = _cairo_surface_copy_mime_data (snapshot, surface, *mime_type);
+	if (status) {
+	    cairo_surface_destroy (snapshot);
+	    return _cairo_surface_create_in_error (status);
+	}
     }
 
     snapshot->is_snapshot = TRUE;
diff --git a/src/cairo.h b/src/cairo.h
index a1cd0c5..cbc109f 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1948,6 +1948,7 @@ cairo_surface_set_user_data (cairo_surface_t		 *surface,
 			     cairo_destroy_func_t	 destroy);
 
 #define CAIRO_MIME_TYPE_JPEG "image/jpeg"
+#define CAIRO_MIME_TYPE_PNG "image/png"
 
 cairo_public void
 cairo_surface_get_mime_data (cairo_surface_t		*surface,
commit 89616dee8f11c6a7de3fa476b13661420648786f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Nov 5 16:41:13 2008 +0000

    [surface] Don't allocate a structure for mime_data == NULL.
    
    If the user attempts to clear the attached mime data representation, just
    clear the user data slot and do not allocate an empty structure.

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 72997b0..9df75ca 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -682,15 +682,18 @@ cairo_surface_set_mime_data (cairo_surface_t		*surface,
     if (status)
 	return _cairo_surface_set_error (surface, status);
 
-    mime_data = malloc (sizeof (cairo_mime_data_t));
-    if (mime_data == NULL)
-	return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+    if (data != NULL) {
+	mime_data = malloc (sizeof (cairo_mime_data_t));
+	if (mime_data == NULL)
+	    return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
 
-    CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
+	CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
 
-    mime_data->data = (unsigned char *) data;
-    mime_data->length = length;
-    mime_data->destroy = destroy;
+	mime_data->data = (unsigned char *) data;
+	mime_data->length = length;
+	mime_data->destroy = destroy;
+    } else
+	mime_data = NULL;
 
     status = _cairo_user_data_array_set_data (&surface->user_data,
 					      (cairo_user_data_key_t *) mime_type,


More information about the cairo-commit mailing list