[gthumb] cairo-io: save png images without converting to a GdkPixbuf



commit 8b7cfe69e182e9dbe46779146e4d6d082be55a35
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Aug 18 23:33:35 2012 +0200

    cairo-io: save png images without converting to a GdkPixbuf

 extensions/cairo_io/cairo-image-surface-png.c |    2 +-
 extensions/cairo_io/gth-image-saver-jpeg.c    |    2 +-
 extensions/cairo_io/gth-image-saver-png.c     |  240 +++++++++++++++++++++++--
 extensions/cairo_io/gth-image-saver-tga.c     |    2 +-
 extensions/cairo_io/gth-image-saver-tiff.c    |    2 +-
 gthumb/cairo-utils.c                          |    2 +-
 gthumb/cairo-utils.h                          |    2 +-
 7 files changed, 230 insertions(+), 22 deletions(-)
---
diff --git a/extensions/cairo_io/cairo-image-surface-png.c b/extensions/cairo_io/cairo-image-surface-png.c
index 6333654..d13ca63 100644
--- a/extensions/cairo_io/cairo-image-surface-png.c
+++ b/extensions/cairo_io/cairo-image-surface-png.c
@@ -176,7 +176,7 @@ _cairo_image_surface_create_from_png (GInputStream  *istream,
 	        return image;
 	}
 
-	if (PNG_SETJMP(cairo_png_data->png_ptr)) {
+	if (PNG_SETJMP (cairo_png_data->png_ptr)) {
 		_cairo_png_data_destroy (cairo_png_data);
 	        return image;
 	}
diff --git a/extensions/cairo_io/gth-image-saver-jpeg.c b/extensions/cairo_io/gth-image-saver-jpeg.c
index 29b1acf..ecca7e3 100644
--- a/extensions/cairo_io/gth-image-saver-jpeg.c
+++ b/extensions/cairo_io/gth-image-saver-jpeg.c
@@ -396,7 +396,7 @@ _cairo_surface_write_as_jpeg (cairo_surface_t  *image,
 
 		/* convert scanline from RGBA to RGB packed */
 
-		_cairo_copy_line_as_rgb (buf, pixels, w, FALSE);
+		_cairo_copy_line_as_rgba (buf, pixels, w, FALSE);
 
 		/* write scanline */
 		jbuf = (JSAMPROW *)(&buf);
diff --git a/extensions/cairo_io/gth-image-saver-png.c b/extensions/cairo_io/gth-image-saver-png.c
index 6b02a73..f4e567b 100644
--- a/extensions/cairo_io/gth-image-saver-png.c
+++ b/extensions/cairo_io/gth-image-saver-png.c
@@ -20,12 +20,28 @@
  */
 
 #include <config.h>
+#include <png.h>
 #include <glib/gi18n.h>
 #include <gthumb.h>
 #include "gth-image-saver-png.h"
 #include "preferences.h"
 
 
+/* starting from libpng version 1.5 it is not possible
+ * to access inside the PNG struct directly
+ */
+#define PNG_SETJMP(ptr) setjmp(png_jmpbuf(ptr))
+
+#ifdef PNG_LIBPNG_VER
+#if PNG_LIBPNG_VER < 10400
+#ifdef PNG_SETJMP
+#undef PNG_SETJMP
+#endif
+#define PNG_SETJMP(ptr) setjmp(ptr->jmpbuf)
+#endif
+#endif
+
+
 G_DEFINE_TYPE (GthImageSaverPng, gth_image_saver_png, GTH_TYPE_IMAGE_SAVER)
 
 
@@ -78,6 +94,204 @@ gth_image_saver_png_can_save (GthImageSaver *self,
 }
 
 
+typedef struct {
+	GError        **error;
+	png_struct     *png_ptr;
+	png_info       *png_info_ptr;
+	GthBufferData  *buffer_data;
+} CairoPngData;
+
+
+static void
+_cairo_png_data_destroy (CairoPngData *cairo_png_data)
+{
+	png_destroy_write_struct (&cairo_png_data->png_ptr, &cairo_png_data->png_info_ptr);
+	gth_buffer_data_free (cairo_png_data->buffer_data, FALSE);
+	g_free (cairo_png_data);
+}
+
+
+static void
+gerror_error_func (png_structp     png_ptr,
+		   png_const_charp message)
+{
+	GError ***error_p = png_get_error_ptr (png_ptr);
+	GError  **error = *error_p;
+
+	if (error != NULL)
+		*error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "%s", message);
+}
+
+
+static void
+gerror_warning_func (png_structp     png_ptr,
+		     png_const_charp message)
+{
+	/* void: we don't care about warnings */
+}
+
+
+static void
+cairo_png_write_data_func (png_structp png_ptr,
+		  	   png_bytep   buffer,
+		  	   png_size_t  size)
+{
+	CairoPngData *cairo_png_data;
+	GError       *error;
+
+	cairo_png_data = png_get_io_ptr (png_ptr);
+	if (! gth_buffer_data_write (cairo_png_data->buffer_data, buffer, size, &error)) {
+		png_error (png_ptr, error->message);
+		g_error_free (error);
+	}
+}
+
+
+static void
+cairo_png_flush_data_func (png_structp png_ptr)
+{
+	/* we are saving in a buffer, no need to flush */
+}
+
+
+static gboolean
+_cairo_surface_write_as_png (cairo_surface_t  *image,
+			     char            **buffer,
+			     gsize            *buffer_size,
+			     char            **keys,
+			     char            **values,
+			     GError          **error)
+{
+	int            compression_level;
+	int            width, height;
+	gboolean       alpha;
+	guchar        *pixels, *ptr, *buf;
+	int            rowstride;
+	CairoPngData  *cairo_png_data;
+	png_color_8    sig_bit;
+	int            bpp;
+	int            row;
+
+	compression_level = 6;
+
+	if (keys && *keys) {
+		char **kiter = keys;
+		char **viter = values;
+
+		while (*kiter) {
+			if (strcmp (*kiter, "compression") == 0) {
+				if (*viter == NULL) {
+					g_set_error (error,
+						     G_IO_ERROR,
+						     G_IO_ERROR_INVALID_DATA,
+						     "Must specify a compression level");
+					return FALSE;
+				}
+
+				compression_level = atoi (*viter);
+
+				if (compression_level < 0 || compression_level > 9) {
+					g_set_error (error,
+						     G_IO_ERROR,
+						     G_IO_ERROR_INVALID_DATA,
+						     "Unsupported compression level passed to the PNG saver");
+					return FALSE;
+				}
+			}
+			else {
+				g_warning ("Bad option name '%s' passed to the PNG saver", *kiter);
+				return FALSE;
+			}
+
+			++kiter;
+			++viter;
+		}
+	}
+
+	width     = cairo_image_surface_get_width (image);
+	height    = cairo_image_surface_get_height (image);
+	alpha     = _cairo_image_surface_get_has_alpha (image);
+	pixels    = cairo_image_surface_get_data (image);
+	rowstride = cairo_image_surface_get_stride (image);
+
+	cairo_png_data = g_new0 (CairoPngData, 1);
+	cairo_png_data->error = error;
+	cairo_png_data->buffer_data = gth_buffer_data_new ();
+
+	cairo_png_data->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
+							   &cairo_png_data->error,
+							   gerror_error_func,
+							   gerror_warning_func);
+	if (cairo_png_data->png_ptr == NULL) {
+		_cairo_png_data_destroy (cairo_png_data);
+	        return FALSE;
+	}
+
+	cairo_png_data->png_info_ptr = png_create_info_struct (cairo_png_data->png_ptr);
+	if (cairo_png_data->png_info_ptr == NULL) {
+		_cairo_png_data_destroy (cairo_png_data);
+	        return FALSE;
+	}
+
+	if (PNG_SETJMP (cairo_png_data->png_ptr)) {
+		_cairo_png_data_destroy (cairo_png_data);
+	        return FALSE;
+	}
+
+	png_set_write_fn (cairo_png_data->png_ptr,
+			  cairo_png_data,
+			  cairo_png_write_data_func,
+			  cairo_png_flush_data_func);
+
+	/* Set the image information here */
+
+	png_set_IHDR (cairo_png_data->png_ptr,
+		      cairo_png_data->png_info_ptr,
+		      width,
+		      height,
+		      8,
+		      (alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB),
+		      PNG_INTERLACE_NONE,
+		      PNG_COMPRESSION_TYPE_BASE,
+		      PNG_FILTER_TYPE_BASE);
+
+	/* Options */
+
+	sig_bit.red = 8;
+	sig_bit.green = 8;
+	sig_bit.blue = 8;
+	if (alpha)
+		sig_bit.alpha = 8;
+	png_set_sBIT (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr, &sig_bit);
+
+	png_set_compression_level (cairo_png_data->png_ptr, compression_level);
+
+	/* Write the file header information. */
+
+	png_write_info (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr);
+
+	/* Write the image */
+
+	bpp = alpha ? 4 : 3;
+	buf = g_new (guchar, width * bpp);
+	ptr = pixels;
+	for (row = 0; row < height; ++row) {
+		_cairo_copy_line_as_rgba (buf, ptr, width, alpha);
+		png_write_rows (cairo_png_data->png_ptr, &buf, 1);
+
+		ptr += rowstride;
+	}
+	g_free (buf);
+
+	png_write_end (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr);
+	gth_buffer_data_get (cairo_png_data->buffer_data, buffer, buffer_size);
+
+	_cairo_png_data_destroy (cairo_png_data);
+
+	return TRUE;
+}
+
+
 static gboolean
 gth_image_saver_png_save_image (GthImageSaver  *base,
 				GthImage       *image,
@@ -87,16 +301,13 @@ gth_image_saver_png_save_image (GthImageSaver  *base,
 				GError        **error)
 {
 	GthImageSaverPng  *self = GTH_IMAGE_SAVER_PNG (base);
-	GdkPixbuf         *pixbuf;
-	char              *pixbuf_type;
+	cairo_surface_t   *surface;
 	char             **option_keys;
 	char             **option_values;
 	int                i = -1;
 	int                i_value;
 	gboolean           result;
 
-	pixbuf_type = get_pixbuf_type_from_mime_type (mime_type);
-
 	option_keys = g_malloc (sizeof (char *) * 2);
 	option_values = g_malloc (sizeof (char *) * 2);
 
@@ -109,20 +320,17 @@ gth_image_saver_png_save_image (GthImageSaver  *base,
 	option_keys[i] = NULL;
 	option_values[i] = NULL;
 
-	/* FIXME: use libpng directly */
-	pixbuf = gth_image_get_pixbuf (image);
-	result = gdk_pixbuf_save_to_bufferv (pixbuf,
-					     buffer,
-					     buffer_size,
-					     pixbuf_type,
-					     option_keys,
-					     option_values,
-					     error);
-
-	g_object_unref (pixbuf);
+	surface = gth_image_get_cairo_surface (image);
+	result = _cairo_surface_write_as_png (surface,
+					      buffer,
+					      buffer_size,
+					      option_keys,
+					      option_values,
+					      error);
+
+	cairo_surface_destroy (surface);
 	g_strfreev (option_keys);
 	g_strfreev (option_values);
-	g_free (pixbuf_type);
 
 	return result;
 }
diff --git a/extensions/cairo_io/gth-image-saver-tga.c b/extensions/cairo_io/gth-image-saver-tga.c
index afb9dd3..22002e6 100644
--- a/extensions/cairo_io/gth-image-saver-tga.c
+++ b/extensions/cairo_io/gth-image-saver-tga.c
@@ -260,7 +260,7 @@ _cairo_surface_write_as_tga (cairo_surface_t  *image,
 
 	ptr = pixels;
 	for (row = 0; row < height; ++row) {
-		_cairo_copy_line_as_rgb (buf, ptr, width, alpha);
+		_cairo_copy_line_as_rgba (buf, ptr, width, alpha);
 
 		if (rle_compression)
 			rle_write (buffer_data, buf, width, out_bpp, error);
diff --git a/extensions/cairo_io/gth-image-saver-tiff.c b/extensions/cairo_io/gth-image-saver-tiff.c
index 79f77ab..d2f192e 100644
--- a/extensions/cairo_io/gth-image-saver-tiff.c
+++ b/extensions/cairo_io/gth-image-saver-tiff.c
@@ -439,7 +439,7 @@ _cairo_surface_write_as_tiff (cairo_surface_t  *image,
 	/* Now write the TIFF data. */
 	ptr = pixels;
 	for (row = 0; row < rows; row++) {
-		_cairo_copy_line_as_rgb (buf, ptr, cols, alpha);
+		_cairo_copy_line_as_rgba (buf, ptr, cols, alpha);
 		if (TIFFWriteScanline (tif, buf, row, 0) < 0) {
 			g_set_error (error,
 				     GDK_PIXBUF_ERROR,
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index 9023e43..02a07ba 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -507,7 +507,7 @@ _cairo_image_surface_transform (cairo_surface_t *source,
 
 
 void
-_cairo_copy_line_as_rgb (guchar *dest,
+_cairo_copy_line_as_rgba (guchar *dest,
 			 guchar *src,
 			 guint   width,
 			 guint   alpha)
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index 8f4be69..eba5161 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -171,7 +171,7 @@ void               _cairo_image_surface_transform_get_steps (cairo_format_t
 							     int                   *pixel_step_p);
 cairo_surface_t *  _cairo_image_surface_transform           (cairo_surface_t       *surface,
 							     GthTransform           transform);
-void               _cairo_copy_line_as_rgb                  (guchar                *dest,
+void               _cairo_copy_line_as_rgba                  (guchar                *dest,
 							     guchar                *src,
 							     guint                  width,
 							     guint                  alpha);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]