[gthumb] cairo-io: use GthImage or cairo_surface_t instead of GdkPixbuf



commit 75676d4a9ff3d509c3f7e7a59ad6832af49ac68c
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Aug 18 19:40:10 2012 +0200

    cairo-io: use GthImage or cairo_surface_t instead of GdkPixbuf
    
    this reduces the convertions from cairo_surface_t to GdkPixbuf abd vice
    versa.

 extensions/cairo_io/gth-image-saver-jpeg.c         |  130 ++++----
 extensions/cairo_io/gth-image-saver-jpeg.h         |    4 +-
 extensions/cairo_io/gth-image-saver-png.c          |   52 ++--
 extensions/cairo_io/gth-image-saver-png.h          |    4 +-
 extensions/cairo_io/gth-image-saver-tga.c          |  116 +++----
 extensions/cairo_io/gth-image-saver-tga.h          |    4 +-
 extensions/cairo_io/gth-image-saver-tiff.c         |  119 ++++---
 extensions/cairo_io/gth-image-saver-tiff.h         |    4 +-
 extensions/cairo_io/main.c                         |    8 +-
 extensions/cairo_io/preferences.c                  |   36 +-
 extensions/cairo_io/preferences.h                  |   10 +-
 extensions/contact_sheet/dlg-contact-sheet.c       |   10 +-
 extensions/contact_sheet/dlg-image-wall.c          |   10 +-
 .../contact_sheet/gth-contact-sheet-creator.c      |   65 ++--
 extensions/convert_format/dlg-convert-format.c     |   31 +-
 extensions/desktop_background/actions.c            |   10 +-
 extensions/exiv2_tools/exiv2-utils.cpp             |   42 ++-
 extensions/exiv2_tools/exiv2-utils.h               |   48 ++--
 extensions/exiv2_tools/main.c                      |    2 +-
 extensions/facebook/facebook-service.c             |   55 ++--
 extensions/file_tools/gth-file-tool-enhance.c      |    6 +-
 extensions/file_tools/gth-file-tool-equalize.c     |    8 +-
 extensions/gstreamer_tools/actions.c               |   12 +-
 extensions/image_rotation/rotation-utils.c         |   29 +-
 extensions/image_viewer/gth-image-viewer-page.c    |   14 +-
 extensions/importer/gth-import-task.c              |   10 +-
 extensions/resize_images/dlg-resize-images.c       |   67 ++--
 extensions/webalbums/gth-web-exporter.c            |  112 ++++--
 gthumb/Makefile.am                                 |   12 +-
 gthumb/cairo-scale.c                               |   45 +++
 gthumb/cairo-scale.h                               |   16 +-
 gthumb/cairo-utils.c                               |   20 +
 gthumb/cairo-utils.h                               |    4 +
 gthumb/gth-file-chooser-dialog.c                   |   10 +-
 ...th-pixbuf-list-task.c => gth-image-list-task.c} |  202 ++++++------
 gthumb/gth-image-list-task.h                       |   65 ++++
 gthumb/gth-image-loader.c                          |  114 +++----
 gthumb/gth-image-loader.h                          |   12 +-
 gthumb/gth-image-saver.c                           |  367 ++++++++++++++++++++
 gthumb/gth-image-saver.h                           |  124 +++++++
 gthumb/gth-image-task.c                            |  150 ++++++++
 gthumb/gth-image-task.h                            |   70 ++++
 gthumb/gth-image.c                                 |   43 +++
 gthumb/gth-image.h                                 |    2 +
 gthumb/gth-main-default-hooks.c                    |    6 +-
 gthumb/gth-main.c                                  |   10 +-
 gthumb/gth-main.h                                  |    4 +-
 gthumb/gth-overwrite-dialog.c                      |   12 +-
 gthumb/gth-overwrite-dialog.h                      |    3 +-
 gthumb/gth-pixbuf-list-task.h                      |   64 ----
 gthumb/gth-pixbuf-saver.c                          |  158 ---------
 gthumb/gth-pixbuf-saver.h                          |   91 -----
 gthumb/gth-pixbuf-task.c                           |  273 ---------------
 gthumb/gth-pixbuf-task.h                           |   94 -----
 gthumb/pixbuf-io.c                                 |  174 +---------
 gthumb/pixbuf-io.h                                 |   23 --
 gthumb/typedefs.h                                  |    8 +
 57 files changed, 1620 insertions(+), 1574 deletions(-)
---
diff --git a/extensions/cairo_io/gth-image-saver-jpeg.c b/extensions/cairo_io/gth-image-saver-jpeg.c
index 5a73df5..29b1acf 100644
--- a/extensions/cairo_io/gth-image-saver-jpeg.c
+++ b/extensions/cairo_io/gth-image-saver-jpeg.c
@@ -35,7 +35,7 @@
 #include "preferences.h"
 
 
-G_DEFINE_TYPE (GthImageSaverJpeg, gth_image_saver_jpeg, GTH_TYPE_PIXBUF_SAVER)
+G_DEFINE_TYPE (GthImageSaverJpeg, gth_image_saver_jpeg, GTH_TYPE_IMAGE_SAVER)
 
 
 struct _GthImageSaverJpegPrivate {
@@ -59,7 +59,7 @@ gth_image_saver_jpeg_finalize (GObject *object)
 
 
 static const char *
-gth_image_saver_jpeg_get_default_ext (GthPixbufSaver *base)
+gth_image_saver_jpeg_get_default_ext (GthImageSaver *base)
 {
 	GthImageSaverJpeg *self = GTH_IMAGE_SAVER_JPEG (base);
 
@@ -71,7 +71,7 @@ gth_image_saver_jpeg_get_default_ext (GthPixbufSaver *base)
 
 
 static GtkWidget *
-gth_image_saver_jpeg_get_control (GthPixbufSaver *base)
+gth_image_saver_jpeg_get_control (GthImageSaver *base)
 {
 	GthImageSaverJpeg  *self = GTH_IMAGE_SAVER_JPEG (base);
 	char         **extensions;
@@ -82,7 +82,7 @@ gth_image_saver_jpeg_get_control (GthPixbufSaver *base)
 		self->priv->builder = _gtk_builder_new_from_file ("jpeg-options.ui", "cairo_io");
 
 	active_idx = 0;
-	extensions = g_strsplit (gth_pixbuf_saver_get_extensions (base), " ", -1);
+	extensions = g_strsplit (gth_image_saver_get_extensions (base), " ", -1);
 	for (i = 0; extensions[i] != NULL; i++) {
 		GtkTreeIter iter;
 
@@ -91,7 +91,7 @@ gth_image_saver_jpeg_get_control (GthPixbufSaver *base)
 				    &iter,
 				    0, extensions[i],
 				    -1);
-		if (g_str_equal (extensions[i], gth_pixbuf_saver_get_default_ext (base)))
+		if (g_str_equal (extensions[i], gth_image_saver_get_default_ext (base)))
 			active_idx = i;
 	}
 	gtk_combo_box_set_active (GTK_COMBO_BOX (_gtk_builder_get_widget (self->priv->builder, "jpeg_default_extension_combobox")), active_idx);
@@ -111,7 +111,7 @@ gth_image_saver_jpeg_get_control (GthPixbufSaver *base)
 
 
 static void
-gth_image_saver_jpeg_save_options (GthPixbufSaver *base)
+gth_image_saver_jpeg_save_options (GthImageSaver *base)
 {
 	GthImageSaverJpeg *self = GTH_IMAGE_SAVER_JPEG (base);
 	GtkTreeIter   iter;
@@ -132,8 +132,8 @@ gth_image_saver_jpeg_save_options (GthPixbufSaver *base)
 
 
 static gboolean
-gth_image_saver_jpeg_can_save (GthPixbufSaver *self,
-			       const char     *mime_type)
+gth_image_saver_jpeg_can_save (GthImageSaver *self,
+			       const char    *mime_type)
 {
 #ifdef HAVE_LIBJPEG
 
@@ -224,17 +224,16 @@ output_message_handler (j_common_ptr cinfo)
 
 
 static gboolean
-_gdk_pixbuf_save_as_jpeg (GdkPixbuf   *pixbuf,
-			  char       **buffer,
-			  gsize       *buffer_size,
-			  char       **keys,
-			  char       **values,
-			  GError     **error)
+_cairo_surface_write_as_jpeg (cairo_surface_t  *image,
+			      char            **buffer,
+			      gsize            *buffer_size,
+			      char            **keys,
+			      char            **values,
+			      GError          **error)
 {
 	struct jpeg_compress_struct cinfo;
 	struct error_handler_data jerr;
 	guchar            *buf = NULL;
-	guchar            *ptr;
 	guchar            *pixels = NULL;
 	volatile int       quality = 85; /* default; must be between 0 and 100 */
 	volatile int       smoothing = 0;
@@ -242,10 +241,8 @@ _gdk_pixbuf_save_as_jpeg (GdkPixbuf   *pixbuf,
 #ifdef HAVE_PROGRESSIVE_JPEG
 	volatile gboolean  progressive = FALSE;
 #endif
-	int                i, j;
 	int                w, h = 0;
 	int                rowstride = 0;
-	volatile int       bpp;
 
 	if (keys && *keys) {
 		char **kiter = keys;
@@ -340,19 +337,15 @@ _gdk_pixbuf_save_as_jpeg (GdkPixbuf   *pixbuf,
 		}
 	}
 
-	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-	w = gdk_pixbuf_get_width (pixbuf);
-	h = gdk_pixbuf_get_height (pixbuf);
-	if (gdk_pixbuf_get_has_alpha (pixbuf))
-		bpp = 4;
-	else
-		bpp = 3;
-	pixels = gdk_pixbuf_get_pixels (pixbuf);
+	rowstride = cairo_image_surface_get_stride (image);
+	w = cairo_image_surface_get_width (image);
+	h = cairo_image_surface_get_height (image);
+	pixels = cairo_image_surface_get_data (image);
 	g_return_val_if_fail (pixels != NULL, FALSE);
 
 	/* allocate a small buffer to convert image data */
 
-	buf = g_try_malloc (w * bpp * sizeof (guchar));
+	buf = g_try_malloc (w * 3 * sizeof (guchar));
 	if (! buf) {
 		g_set_error (error,
 			     GDK_PIXBUF_ERROR,
@@ -396,29 +389,26 @@ _gdk_pixbuf_save_as_jpeg (GdkPixbuf   *pixbuf,
 #endif /* HAVE_PROGRESSIVE_JPEG */
 
 	jpeg_start_compress (&cinfo, TRUE);
-	/* get the start pointer */
-	ptr = pixels;
+
 	/* go one scanline at a time... and save */
-	i = 0;
 	while (cinfo.next_scanline < cinfo.image_height) {
 		JSAMPROW *jbuf;
 
-		/* convert scanline from ARGB to RGB packed */
-		for (j = 0; j < w; j++)
-			memcpy (&(buf[j * 3]),
-				&(ptr[i * rowstride + j * bpp]),
-				3);
+		/* convert scanline from RGBA to RGB packed */
+
+		_cairo_copy_line_as_rgb (buf, pixels, w, FALSE);
 
 		/* write scanline */
 		jbuf = (JSAMPROW *)(&buf);
 		jpeg_write_scanlines (&cinfo, jbuf, 1);
-		i++;
+
+		pixels += rowstride;
 	}
 
 	/* finish off */
 
 	jpeg_finish_compress (&cinfo);
-	jpeg_destroy_compress(&cinfo);
+	jpeg_destroy_compress (&cinfo);
 	g_free (buf);
 
 	return TRUE;
@@ -429,20 +419,21 @@ _gdk_pixbuf_save_as_jpeg (GdkPixbuf   *pixbuf,
 
 
 static gboolean
-gth_image_saver_jpeg_save_pixbuf (GthPixbufSaver  *base,
-				  GdkPixbuf       *pixbuf,
-				  char           **buffer,
-				  gsize           *buffer_size,
-				  const char      *mime_type,
-				  GError         **error)
+gth_image_saver_jpeg_save_image (GthImageSaver  *base,
+				 GthImage       *image,
+				 char          **buffer,
+				 gsize          *buffer_size,
+				 const char     *mime_type,
+				 GError        **error)
 {
 #ifdef HAVE_LIBJPEG
 	GthImageSaverJpeg  *self = GTH_IMAGE_SAVER_JPEG (base);
-	char         **option_keys;
-	char         **option_values;
-	int            i = -1;
-	int            i_value;
-	gboolean       result;
+	char              **option_keys;
+	char              **option_values;
+	int                 i = -1;
+	int                 i_value;
+	cairo_surface_t    *surface;
+	gboolean            result;
 
 	option_keys = g_malloc (sizeof (char *) * 5);
 	option_values = g_malloc (sizeof (char *) * 5);
@@ -471,21 +462,25 @@ gth_image_saver_jpeg_save_pixbuf (GthPixbufSaver  *base,
 	option_keys[i] = NULL;
 	option_values[i] = NULL;
 
-	result = _gdk_pixbuf_save_as_jpeg (pixbuf,
-					   buffer,
-					   buffer_size,
-					   option_keys,
-					   option_values,
-					   error);
+	surface = gth_image_get_cairo_surface (image);
+	result = _cairo_surface_write_as_jpeg (surface,
+					       buffer,
+					       buffer_size,
+					       option_keys,
+					       option_values,
+					       error);
 
+	cairo_surface_destroy (surface);
 	g_strfreev (option_keys);
 	g_strfreev (option_values);
 
 #else /* ! HAVE_LIBJPEG */
 
-	char     *pixbuf_type;
-	gboolean  result;
+	GdkPixbuf *pixbuf;
+	char      *pixbuf_type;
+	gboolean   result;
 
+	pixbuf = gth_image_get_pixbuf (image);
 	pixbuf_type = get_pixbuf_type_from_mime_type (mime_type);
 	result = gdk_pixbuf_save_to_bufferv (pixbuf,
 					     buffer,
@@ -496,6 +491,7 @@ gth_image_saver_jpeg_save_pixbuf (GthPixbufSaver  *base,
 					     error);
 
 	g_free (pixbuf_type);
+	g_object_unref (pixbuf);
 
 #endif /* HAVE_LIBJPEG */
 
@@ -507,23 +503,23 @@ static void
 gth_image_saver_jpeg_class_init (GthImageSaverJpegClass *klass)
 {
 	GObjectClass        *object_class;
-	GthPixbufSaverClass *pixbuf_saver_class;
+	GthImageSaverClass *image_saver_class;
 
 	g_type_class_add_private (klass, sizeof (GthImageSaverJpegPrivate));
 
 	object_class = G_OBJECT_CLASS (klass);
 	object_class->finalize = gth_image_saver_jpeg_finalize;
 
-	pixbuf_saver_class = GTH_PIXBUF_SAVER_CLASS (klass);
-	pixbuf_saver_class->id = "jpeg";
-	pixbuf_saver_class->display_name = _("JPEG");
-	pixbuf_saver_class->mime_type = "image/jpeg";
-	pixbuf_saver_class->extensions = "jpeg jpg";
-	pixbuf_saver_class->get_default_ext = gth_image_saver_jpeg_get_default_ext;
-	pixbuf_saver_class->get_control = gth_image_saver_jpeg_get_control;
-	pixbuf_saver_class->save_options = gth_image_saver_jpeg_save_options;
-	pixbuf_saver_class->can_save = gth_image_saver_jpeg_can_save;
-	pixbuf_saver_class->save_pixbuf = gth_image_saver_jpeg_save_pixbuf;
+	image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
+	image_saver_class->id = "jpeg";
+	image_saver_class->display_name = _("JPEG");
+	image_saver_class->mime_type = "image/jpeg";
+	image_saver_class->extensions = "jpeg jpg";
+	image_saver_class->get_default_ext = gth_image_saver_jpeg_get_default_ext;
+	image_saver_class->get_control = gth_image_saver_jpeg_get_control;
+	image_saver_class->save_options = gth_image_saver_jpeg_save_options;
+	image_saver_class->can_save = gth_image_saver_jpeg_can_save;
+	image_saver_class->save_image = gth_image_saver_jpeg_save_image;
 }
 
 
@@ -531,7 +527,7 @@ static void
 gth_image_saver_jpeg_init (GthImageSaverJpeg *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_SAVER_JPEG, GthImageSaverJpegPrivate);
-	self->priv->settings = g_settings_new (GTHUMB_PIXBUF_SAVERS_JPEG_SCHEMA);
+	self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_JPEG_SCHEMA);
 	self->priv->builder = NULL;
 	self->priv->default_ext = NULL;
 }
diff --git a/extensions/cairo_io/gth-image-saver-jpeg.h b/extensions/cairo_io/gth-image-saver-jpeg.h
index 36c9042..0180d4a 100644
--- a/extensions/cairo_io/gth-image-saver-jpeg.h
+++ b/extensions/cairo_io/gth-image-saver-jpeg.h
@@ -40,13 +40,13 @@ typedef struct _GthImageSaverJpegPrivate  GthImageSaverJpegPrivate;
 
 struct _GthImageSaverJpeg
 {
-	GthPixbufSaver __parent;
+	GthImageSaver __parent;
 	GthImageSaverJpegPrivate *priv;
 };
 
 struct _GthImageSaverJpegClass
 {
-	GthPixbufSaverClass __parent_class;
+	GthImageSaverClass __parent_class;
 };
 
 GType  gth_image_saver_jpeg_get_type (void);
diff --git a/extensions/cairo_io/gth-image-saver-png.c b/extensions/cairo_io/gth-image-saver-png.c
index 381d079..6b02a73 100644
--- a/extensions/cairo_io/gth-image-saver-png.c
+++ b/extensions/cairo_io/gth-image-saver-png.c
@@ -26,7 +26,7 @@
 #include "preferences.h"
 
 
-G_DEFINE_TYPE (GthImageSaverPng, gth_image_saver_png, GTH_TYPE_PIXBUF_SAVER)
+G_DEFINE_TYPE (GthImageSaverPng, gth_image_saver_png, GTH_TYPE_IMAGE_SAVER)
 
 
 struct _GthImageSaverPngPrivate {
@@ -47,7 +47,7 @@ gth_image_saver_png_finalize (GObject *object)
 
 
 static GtkWidget *
-gth_image_saver_png_get_control (GthPixbufSaver *base)
+gth_image_saver_png_get_control (GthImageSaver *base)
 {
 	GthImageSaverPng *self = GTH_IMAGE_SAVER_PNG (base);
 
@@ -62,7 +62,7 @@ gth_image_saver_png_get_control (GthPixbufSaver *base)
 
 
 static void
-gth_image_saver_png_save_options (GthPixbufSaver *base)
+gth_image_saver_png_save_options (GthImageSaver *base)
 {
 	GthImageSaverPng *self = GTH_IMAGE_SAVER_PNG (base);
 
@@ -71,22 +71,23 @@ gth_image_saver_png_save_options (GthPixbufSaver *base)
 
 
 static gboolean
-gth_image_saver_png_can_save (GthPixbufSaver *self,
-			      const char     *mime_type)
+gth_image_saver_png_can_save (GthImageSaver *self,
+			      const char    *mime_type)
 {
 	return g_content_type_equals (mime_type, "image/png");
 }
 
 
 static gboolean
-gth_image_saver_png_save_pixbuf (GthPixbufSaver  *base,
-				 GdkPixbuf       *pixbuf,
-				 char           **buffer,
-				 gsize           *buffer_size,
-				 const char      *mime_type,
-				 GError         **error)
+gth_image_saver_png_save_image (GthImageSaver  *base,
+				GthImage       *image,
+				char          **buffer,
+				gsize          *buffer_size,
+				const char     *mime_type,
+				GError        **error)
 {
 	GthImageSaverPng  *self = GTH_IMAGE_SAVER_PNG (base);
+	GdkPixbuf         *pixbuf;
 	char              *pixbuf_type;
 	char             **option_keys;
 	char             **option_values;
@@ -108,6 +109,8 @@ gth_image_saver_png_save_pixbuf (GthPixbufSaver  *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,
@@ -116,6 +119,7 @@ gth_image_saver_png_save_pixbuf (GthPixbufSaver  *base,
 					     option_values,
 					     error);
 
+	g_object_unref (pixbuf);
 	g_strfreev (option_keys);
 	g_strfreev (option_values);
 	g_free (pixbuf_type);
@@ -127,24 +131,24 @@ gth_image_saver_png_save_pixbuf (GthPixbufSaver  *base,
 static void
 gth_image_saver_png_class_init (GthImageSaverPngClass *klass)
 {
-	GObjectClass        *object_class;
-	GthPixbufSaverClass *pixbuf_saver_class;
+	GObjectClass       *object_class;
+	GthImageSaverClass *image_saver_class;
 
 	g_type_class_add_private (klass, sizeof (GthImageSaverPngPrivate));
 
 	object_class = G_OBJECT_CLASS (klass);
 	object_class->finalize = gth_image_saver_png_finalize;
 
-	pixbuf_saver_class = GTH_PIXBUF_SAVER_CLASS (klass);
-	pixbuf_saver_class->id = "png";
-	pixbuf_saver_class->display_name = _("PNG");
-	pixbuf_saver_class->mime_type = "image/png";
-	pixbuf_saver_class->extensions = "png";
-	pixbuf_saver_class->get_default_ext = NULL;
-	pixbuf_saver_class->get_control = gth_image_saver_png_get_control;
-	pixbuf_saver_class->save_options = gth_image_saver_png_save_options;
-	pixbuf_saver_class->can_save = gth_image_saver_png_can_save;
-	pixbuf_saver_class->save_pixbuf = gth_image_saver_png_save_pixbuf;
+	image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
+	image_saver_class->id = "png";
+	image_saver_class->display_name = _("PNG");
+	image_saver_class->mime_type = "image/png";
+	image_saver_class->extensions = "png";
+	image_saver_class->get_default_ext = NULL;
+	image_saver_class->get_control = gth_image_saver_png_get_control;
+	image_saver_class->save_options = gth_image_saver_png_save_options;
+	image_saver_class->can_save = gth_image_saver_png_can_save;
+	image_saver_class->save_image = gth_image_saver_png_save_image;
 }
 
 
@@ -152,5 +156,5 @@ static void
 gth_image_saver_png_init (GthImageSaverPng *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_SAVER_PNG, GthImageSaverPngPrivate);
-	self->priv->settings = g_settings_new (GTHUMB_PIXBUF_SAVERS_PNG_SCHEMA);
+	self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_PNG_SCHEMA);
 }
diff --git a/extensions/cairo_io/gth-image-saver-png.h b/extensions/cairo_io/gth-image-saver-png.h
index 3a77353..95ee73e 100644
--- a/extensions/cairo_io/gth-image-saver-png.h
+++ b/extensions/cairo_io/gth-image-saver-png.h
@@ -40,13 +40,13 @@ typedef struct _GthImageSaverPngPrivate  GthImageSaverPngPrivate;
 
 struct _GthImageSaverPng
 {
-	GthPixbufSaver __parent;
+	GthImageSaver __parent;
 	GthImageSaverPngPrivate *priv;
 };
 
 struct _GthImageSaverPngClass
 {
-	GthPixbufSaverClass __parent_class;
+	GthImageSaverClass __parent_class;
 };
 
 GType  gth_image_saver_png_get_type  (void);
diff --git a/extensions/cairo_io/gth-image-saver-tga.c b/extensions/cairo_io/gth-image-saver-tga.c
index a2f31f4..afb9dd3 100644
--- a/extensions/cairo_io/gth-image-saver-tga.c
+++ b/extensions/cairo_io/gth-image-saver-tga.c
@@ -26,7 +26,7 @@
 #include "preferences.h"
 
 
-G_DEFINE_TYPE (GthImageSaverTga, gth_image_saver_tga, GTH_TYPE_PIXBUF_SAVER)
+G_DEFINE_TYPE (GthImageSaverTga, gth_image_saver_tga, GTH_TYPE_IMAGE_SAVER)
 
 
 struct _GthImageSaverTgaPrivate {
@@ -47,7 +47,7 @@ gth_image_saver_tga_finalize (GObject *object)
 
 
 static GtkWidget *
-gth_image_saver_tga_get_control (GthPixbufSaver *base)
+gth_image_saver_tga_get_control (GthImageSaver *base)
 {
 	GthImageSaverTga *self = GTH_IMAGE_SAVER_TGA (base);
 
@@ -62,7 +62,7 @@ gth_image_saver_tga_get_control (GthPixbufSaver *base)
 
 
 static void
-gth_image_saver_tga_save_options (GthPixbufSaver *base)
+gth_image_saver_tga_save_options (GthImageSaver *base)
 {
 	GthImageSaverTga *self = GTH_IMAGE_SAVER_TGA (base);
 
@@ -71,8 +71,8 @@ gth_image_saver_tga_save_options (GthPixbufSaver *base)
 
 
 static gboolean
-gth_image_saver_tga_can_save (GthPixbufSaver *self,
-			      const char     *mime_type)
+gth_image_saver_tga_can_save (GthImageSaver *self,
+			      const char    *mime_type)
 {
 	return g_content_type_equals (mime_type, "image/tga") || g_content_type_equals (mime_type, "image/x-tga");
 }
@@ -157,42 +157,13 @@ rle_write (GthBufferData  *buffer_data,
 }
 
 
-static void
-bgr2rgb (guchar *dest,
-	 guchar *src,
-	 guint   width,
-	 guint   bytes,
-	 guint   alpha)
-{
-	guint x;
-
-	if (alpha)
-		for (x = 0; x < width; x++) {
-			*(dest++) = src[2];
-			*(dest++) = src[1];
-			*(dest++) = src[0];
-			*(dest++) = src[3];
-
-			src += bytes;
-		}
-	else
-		for (x = 0; x < width; x++) {
-			*(dest++) = src[2];
-			*(dest++) = src[1];
-			*(dest++) = src[0];
-
-			src += bytes;
-		}
-}
-
-
 static gboolean
-_gdk_pixbuf_save_as_tga (GdkPixbuf   *pixbuf,
-			 char       **buffer,
-			 gsize       *buffer_size,
-			 char       **keys,
-			 char       **values,
-			 GError     **error)
+_cairo_surface_write_as_tga (cairo_surface_t  *image,
+			     char            **buffer,
+			     gsize            *buffer_size,
+			     char            **keys,
+			     char            **values,
+			     GError          **error)
 {
 	GthBufferData *buffer_data;
 	int            out_bpp = 0;
@@ -245,11 +216,11 @@ _gdk_pixbuf_save_as_tga (GdkPixbuf   *pixbuf,
 		}
 	}
 
-	width     = gdk_pixbuf_get_width (pixbuf);
-	height    = gdk_pixbuf_get_height (pixbuf);
-	alpha     = gdk_pixbuf_get_has_alpha (pixbuf);
-	pixels    = gdk_pixbuf_get_pixels (pixbuf);
-	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	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);
 
 	buffer_data = gth_buffer_data_new ();
 
@@ -289,7 +260,7 @@ _gdk_pixbuf_save_as_tga (GdkPixbuf   *pixbuf,
 
 	ptr = pixels;
 	for (row = 0; row < height; ++row) {
-		bgr2rgb (buf, ptr, width, out_bpp, alpha);
+		_cairo_copy_line_as_rgb (buf, ptr, width, alpha);
 
 		if (rle_compression)
 			rle_write (buffer_data, buf, width, out_bpp, error);
@@ -315,18 +286,19 @@ _gdk_pixbuf_save_as_tga (GdkPixbuf   *pixbuf,
 
 
 static gboolean
-gth_image_saver_tga_save_pixbuf (GthPixbufSaver  *base,
-				 GdkPixbuf       *pixbuf,
-				 char           **buffer,
-				 gsize           *buffer_size,
-				 const char      *mime_type,
-				 GError         **error)
+gth_image_saver_tga_save_image (GthImageSaver  *base,
+				GthImage       *image,
+				char          **buffer,
+				gsize          *buffer_size,
+				const char     *mime_type,
+				GError        **error)
 {
 	GthImageSaverTga  *self = GTH_IMAGE_SAVER_TGA (base);
 	char              *pixbuf_type;
 	char             **option_keys;
 	char             **option_values;
 	int                i = -1;
+	cairo_surface_t   *surface;
 	gboolean           result;
 
 	pixbuf_type = get_pixbuf_type_from_mime_type (mime_type);
@@ -342,13 +314,15 @@ gth_image_saver_tga_save_pixbuf (GthPixbufSaver  *base,
 	option_keys[i] = NULL;
 	option_values[i] = NULL;
 
-	result = _gdk_pixbuf_save_as_tga (pixbuf,
-					  buffer,
-					  buffer_size,
-					  option_keys,
-					  option_values,
-					  error);
+	surface = gth_image_get_cairo_surface (image);
+	result = _cairo_surface_write_as_tga (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);
@@ -360,24 +334,24 @@ gth_image_saver_tga_save_pixbuf (GthPixbufSaver  *base,
 static void
 gth_image_saver_tga_class_init (GthImageSaverTgaClass *klass)
 {
-	GObjectClass        *object_class;
-	GthPixbufSaverClass *pixbuf_saver_class;
+	GObjectClass       *object_class;
+	GthImageSaverClass *image_saver_class;
 
 	g_type_class_add_private (klass, sizeof (GthImageSaverTgaPrivate));
 
 	object_class = G_OBJECT_CLASS (klass);
 	object_class->finalize = gth_image_saver_tga_finalize;
 
-	pixbuf_saver_class = GTH_PIXBUF_SAVER_CLASS (klass);
-	pixbuf_saver_class->id = "tga";
-	pixbuf_saver_class->display_name = _("TGA");
-	pixbuf_saver_class->mime_type = "image/x-tga";
-	pixbuf_saver_class->extensions = "tga";
-	pixbuf_saver_class->get_default_ext = NULL;
-	pixbuf_saver_class->get_control = gth_image_saver_tga_get_control;
-	pixbuf_saver_class->save_options = gth_image_saver_tga_save_options;
-	pixbuf_saver_class->can_save = gth_image_saver_tga_can_save;
-	pixbuf_saver_class->save_pixbuf = gth_image_saver_tga_save_pixbuf;
+	image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
+	image_saver_class->id = "tga";
+	image_saver_class->display_name = _("TGA");
+	image_saver_class->mime_type = "image/x-tga";
+	image_saver_class->extensions = "tga";
+	image_saver_class->get_default_ext = NULL;
+	image_saver_class->get_control = gth_image_saver_tga_get_control;
+	image_saver_class->save_options = gth_image_saver_tga_save_options;
+	image_saver_class->can_save = gth_image_saver_tga_can_save;
+	image_saver_class->save_image = gth_image_saver_tga_save_image;
 }
 
 
@@ -385,5 +359,5 @@ static void
 gth_image_saver_tga_init (GthImageSaverTga *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_SAVER_TGA, GthImageSaverTgaPrivate);
-	self->priv->settings = g_settings_new (GTHUMB_PIXBUF_SAVERS_TGA_SCHEMA);
+	self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_TGA_SCHEMA);
 }
diff --git a/extensions/cairo_io/gth-image-saver-tga.h b/extensions/cairo_io/gth-image-saver-tga.h
index 55d50c3..5f20083 100644
--- a/extensions/cairo_io/gth-image-saver-tga.h
+++ b/extensions/cairo_io/gth-image-saver-tga.h
@@ -40,13 +40,13 @@ typedef struct _GthImageSaverTgaPrivate  GthImageSaverTgaPrivate;
 
 struct _GthImageSaverTga
 {
-	GthPixbufSaver __parent;
+	GthImageSaver __parent;
 	GthImageSaverTgaPrivate *priv;
 };
 
 struct _GthImageSaverTgaClass
 {
-	GthPixbufSaverClass __parent_class;
+	GthImageSaverClass __parent_class;
 };
 
 GType  gth_image_saver_tga_get_type (void);
diff --git a/extensions/cairo_io/gth-image-saver-tiff.c b/extensions/cairo_io/gth-image-saver-tiff.c
index cd24140..79f77ab 100644
--- a/extensions/cairo_io/gth-image-saver-tiff.c
+++ b/extensions/cairo_io/gth-image-saver-tiff.c
@@ -30,7 +30,7 @@
 #include "preferences.h"
 
 
-G_DEFINE_TYPE (GthImageSaverTiff, gth_image_saver_tiff, GTH_TYPE_PIXBUF_SAVER)
+G_DEFINE_TYPE (GthImageSaverTiff, gth_image_saver_tiff, GTH_TYPE_IMAGE_SAVER)
 
 
 struct _GthImageSaverTiffPrivate {
@@ -54,7 +54,7 @@ gth_image_saver_tiff_finalize (GObject *object)
 
 
 static const char *
-gth_image_saver_tiff_get_default_ext (GthPixbufSaver *base)
+gth_image_saver_tiff_get_default_ext (GthImageSaver *base)
 {
 	GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (base);
 
@@ -66,7 +66,7 @@ gth_image_saver_tiff_get_default_ext (GthPixbufSaver *base)
 
 
 static GtkWidget *
-gth_image_saver_tiff_get_control (GthPixbufSaver *base)
+gth_image_saver_tiff_get_control (GthImageSaver *base)
 {
 #ifdef HAVE_LIBTIFF
 
@@ -80,7 +80,7 @@ gth_image_saver_tiff_get_control (GthPixbufSaver *base)
 		self->priv->builder = _gtk_builder_new_from_file ("tiff-options.ui", "cairo_io");
 
 	active_idx = 0;
-	extensions = g_strsplit (gth_pixbuf_saver_get_extensions (base), " ", -1);
+	extensions = g_strsplit (gth_image_saver_get_extensions (base), " ", -1);
 	for (i = 0; extensions[i] != NULL; i++) {
 		GtkTreeIter iter;
 
@@ -89,7 +89,7 @@ gth_image_saver_tiff_get_control (GthPixbufSaver *base)
 				    &iter,
 				    0, extensions[i],
 				    -1);
-		if (g_str_equal (extensions[i], gth_pixbuf_saver_get_default_ext (base)))
+		if (g_str_equal (extensions[i], gth_image_saver_get_default_ext (base)))
 			active_idx = i;
 	}
 	gtk_combo_box_set_active (GTK_COMBO_BOX (_gtk_builder_get_widget (self->priv->builder, "tiff_default_extension_combobox")), active_idx);
@@ -117,14 +117,14 @@ gth_image_saver_tiff_get_control (GthPixbufSaver *base)
 
 #else /* ! HAVE_LIBTIFF */
 
-	return GTH_PIXBUF_SAVER_CLASS (gth_image_saver_tiff_parent_class)->get_control (base);
+	return GTH_IMAGE_SAVER_CLASS (gth_image_saver_tiff_parent_class)->get_control (base);
 
 #endif /* HAVE_LIBTIFF */
 }
 
 
 static void
-gth_image_saver_tiff_save_options (GthPixbufSaver *base)
+gth_image_saver_tiff_save_options (GthImageSaver *base)
 {
 #ifdef HAVE_LIBTIFF
 
@@ -157,8 +157,8 @@ gth_image_saver_tiff_save_options (GthPixbufSaver *base)
 
 
 static gboolean
-gth_image_saver_tiff_can_save (GthPixbufSaver *self,
-			       const char     *mime_type)
+gth_image_saver_tiff_can_save (GthImageSaver *self,
+			       const char    *mime_type)
 {
 #ifdef HAVE_LIBTIFF
 
@@ -250,12 +250,12 @@ tiff_save_size (thandle_t handle)
 
 
 static gboolean
-_gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
-			  char       **buffer,
-			  gsize       *buffer_size,
-			  char       **keys,
-			  char       **values,
-			  GError     **error)
+_cairo_surface_write_as_tiff (cairo_surface_t  *image,
+			      char            **buffer,
+			      gsize            *buffer_size,
+			      char            **keys,
+			      char            **values,
+			      GError          **error)
 {
 	GthBufferData *buffer_data;
 	TIFF          *tif;
@@ -269,7 +269,7 @@ _gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
 	gshort         bitspersample;
 	gushort        extra_samples[1];
 	int            rowstride;
-	guchar        *pixels;
+	guchar        *pixels, *ptr, *buf;
 	int            horizontal_dpi = 72, vertical_dpi = 72;
 	gboolean       save_resolution = FALSE;
 
@@ -380,11 +380,11 @@ _gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
 		return FALSE;
 	}
 
-	cols      = gdk_pixbuf_get_width (pixbuf);
-	rows      = gdk_pixbuf_get_height (pixbuf);
-	alpha     = gdk_pixbuf_get_has_alpha (pixbuf);
-	pixels    = gdk_pixbuf_get_pixels (pixbuf);
-	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	cols      = cairo_image_surface_get_width (image);
+	rows      = 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);
 
 	predictor       = 2;
 	bitspersample   = 8;
@@ -427,9 +427,20 @@ _gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
 		TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
 	}
 
+	buf = g_try_malloc (cols * samplesperpixel * sizeof (guchar));
+	if (! buf) {
+		g_set_error_literal (error,
+				     GDK_PIXBUF_ERROR,
+				     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+				     _("Insufficient memory"));
+		return FALSE;
+	}
+
 	/* Now write the TIFF data. */
+	ptr = pixels;
 	for (row = 0; row < rows; row++) {
-		if (TIFFWriteScanline (tif, pixels + row * rowstride, row, 0) < 0) {
+		_cairo_copy_line_as_rgb (buf, ptr, cols, alpha);
+		if (TIFFWriteScanline (tif, buf, row, 0) < 0) {
 			g_set_error (error,
 				     GDK_PIXBUF_ERROR,
 				     GDK_PIXBUF_ERROR_FAILED,
@@ -437,8 +448,12 @@ _gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
 				     row);
 			return FALSE;
 		}
+
+		ptr += rowstride;
 	}
 
+	g_free (buf);
+
 	TIFFFlushData (tif);
 	TIFFClose (tif);
 
@@ -453,12 +468,12 @@ _gdk_pixbuf_save_as_tiff (GdkPixbuf   *pixbuf,
 
 
 static gboolean
-gth_image_saver_tiff_save_pixbuf (GthPixbufSaver  *base,
-				  GdkPixbuf       *pixbuf,
-				  char           **buffer,
-				  gsize           *buffer_size,
-				  const char      *mime_type,
-				  GError         **error)
+gth_image_saver_tiff_save_image (GthImageSaver  *base,
+				 GthImage       *image,
+				 char          **buffer,
+				 gsize          *buffer_size,
+				 const char     *mime_type,
+				 GError        **error)
 {
 #ifdef HAVE_LIBTIFF
 	GthImageSaverTiff  *self = GTH_IMAGE_SAVER_TIFF (base);
@@ -466,6 +481,7 @@ gth_image_saver_tiff_save_pixbuf (GthPixbufSaver  *base,
 	char              **option_values;
 	int                 i = -1;
 	int                 i_value;
+	cairo_surface_t    *surface;
 	gboolean            result;
 
 	option_keys = g_malloc (sizeof (char *) * 4);
@@ -489,21 +505,25 @@ gth_image_saver_tiff_save_pixbuf (GthPixbufSaver  *base,
 	option_keys[i] = NULL;
 	option_values[i] = NULL;
 
-	result = _gdk_pixbuf_save_as_tiff (pixbuf,
-					   buffer,
-					   buffer_size,
-					   option_keys,
-					   option_values,
-					   error);
+	surface = gth_image_get_cairo_surface (image);
+	result = _cairo_surface_write_as_tiff (surface,
+					       buffer,
+					       buffer_size,
+					       option_keys,
+					       option_values,
+					       error);
 
+	cairo_surface_destroy (surface);
 	g_strfreev (option_keys);
 	g_strfreev (option_values);
 
 #else /* ! HAVE_LIBTIFF */
 
-	char     *pixbuf_type;
-	gboolean  result;
+	GdkPixbuf *pixbuf;
+	char      *pixbuf_type;
+	gboolean   result;
 
+	pixbuf = gth_image_get_pixbuf (image);
 	pixbuf_type = get_pixbuf_type_from_mime_type (mime_type);
 	result = gdk_pixbuf_save_to_bufferv (pixbuf,
 					     buffer,
@@ -514,6 +534,7 @@ gth_image_saver_tiff_save_pixbuf (GthPixbufSaver  *base,
 					     error);
 
 	g_free (pixbuf_type);
+	g_object_unref (pixbuf);
 
 #endif /* HAVE_LIBTIFF */
 
@@ -524,24 +545,24 @@ gth_image_saver_tiff_save_pixbuf (GthPixbufSaver  *base,
 static void
 gth_image_saver_tiff_class_init (GthImageSaverTiffClass *klass)
 {
-	GObjectClass        *object_class;
-	GthPixbufSaverClass *pixbuf_saver_class;
+	GObjectClass       *object_class;
+	GthImageSaverClass *image_saver_class;
 
 	g_type_class_add_private (klass, sizeof (GthImageSaverTiffPrivate));
 
 	object_class = G_OBJECT_CLASS (klass);
 	object_class->finalize = gth_image_saver_tiff_finalize;
 
-	pixbuf_saver_class = GTH_PIXBUF_SAVER_CLASS (klass);
-	pixbuf_saver_class->id = "tiff";
-	pixbuf_saver_class->display_name = _("TIFF");
-	pixbuf_saver_class->mime_type = "image/tiff";
-	pixbuf_saver_class->extensions = "tiff tif";
-	pixbuf_saver_class->get_default_ext = gth_image_saver_tiff_get_default_ext;
-	pixbuf_saver_class->get_control = gth_image_saver_tiff_get_control;
-	pixbuf_saver_class->save_options = gth_image_saver_tiff_save_options;
-	pixbuf_saver_class->can_save = gth_image_saver_tiff_can_save;
-	pixbuf_saver_class->save_pixbuf = gth_image_saver_tiff_save_pixbuf;
+	image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
+	image_saver_class->id = "tiff";
+	image_saver_class->display_name = _("TIFF");
+	image_saver_class->mime_type = "image/tiff";
+	image_saver_class->extensions = "tiff tif";
+	image_saver_class->get_default_ext = gth_image_saver_tiff_get_default_ext;
+	image_saver_class->get_control = gth_image_saver_tiff_get_control;
+	image_saver_class->save_options = gth_image_saver_tiff_save_options;
+	image_saver_class->can_save = gth_image_saver_tiff_can_save;
+	image_saver_class->save_image = gth_image_saver_tiff_save_image;
 }
 
 
@@ -549,7 +570,7 @@ static void
 gth_image_saver_tiff_init (GthImageSaverTiff *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_SAVER_TIFF, GthImageSaverTiffPrivate);
-	self->priv->settings = g_settings_new (GTHUMB_PIXBUF_SAVERS_TIFF_SCHEMA);
+	self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_TIFF_SCHEMA);
 	self->priv->builder = NULL;
 	self->priv->default_ext = NULL;
 }
diff --git a/extensions/cairo_io/gth-image-saver-tiff.h b/extensions/cairo_io/gth-image-saver-tiff.h
index 41e6722..92ed47f 100644
--- a/extensions/cairo_io/gth-image-saver-tiff.h
+++ b/extensions/cairo_io/gth-image-saver-tiff.h
@@ -40,13 +40,13 @@ typedef struct _GthImageSaverTiffPrivate  GthImageSaverTiffPrivate;
 
 struct _GthImageSaverTiff
 {
-	GthPixbufSaver __parent;
+	GthImageSaver __parent;
 	GthImageSaverTiffPrivate *priv;
 };
 
 struct _GthImageSaverTiffClass
 {
-	GthPixbufSaverClass __parent_class;
+	GthImageSaverClass __parent_class;
 };
 
 GType  gth_image_saver_tiff_get_type  (void);
diff --git a/extensions/cairo_io/main.c b/extensions/cairo_io/main.c
index 86685a8..249fb22 100644
--- a/extensions/cairo_io/main.c
+++ b/extensions/cairo_io/main.c
@@ -41,7 +41,7 @@ gthumb_extension_activate (void)
 					     GTH_IMAGE_FORMAT_CAIRO_SURFACE,
 					     "image/jpeg",
 					     NULL);
-	gth_main_register_type ("pixbuf-saver", GTH_TYPE_IMAGE_SAVER_JPEG);
+	gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_JPEG);
 
 #endif
 
@@ -59,9 +59,9 @@ gthumb_extension_activate (void)
 
 #endif
 
-	gth_main_register_type ("pixbuf-saver", GTH_TYPE_IMAGE_SAVER_PNG);
-	gth_main_register_type ("pixbuf-saver", GTH_TYPE_IMAGE_SAVER_TGA);
-	gth_main_register_type ("pixbuf-saver", GTH_TYPE_IMAGE_SAVER_TIFF);
+	gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_PNG);
+	gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_TGA);
+	gth_main_register_type ("image-saver", GTH_TYPE_IMAGE_SAVER_TIFF);
 	gth_hook_add_callback ("dlg-preferences-construct", 30, G_CALLBACK (ci__dlg_preferences_construct_cb), NULL);
 	gth_hook_add_callback ("dlg-preferences-apply", 10, G_CALLBACK (ci__dlg_preferences_apply_cb), NULL);
 }
diff --git a/extensions/cairo_io/preferences.c b/extensions/cairo_io/preferences.c
index 310ce0b..113b54d 100644
--- a/extensions/cairo_io/preferences.c
+++ b/extensions/cairo_io/preferences.c
@@ -38,14 +38,14 @@ enum {
 
 typedef struct {
 	GtkBuilder *builder;
-	GList      *pixbuf_saver;
+	GList      *image_saver;
 } BrowserData;
 
 
 static void
 browser_data_free (BrowserData *data)
 {
-	_g_object_list_unref (data->pixbuf_saver);
+	_g_object_list_unref (data->image_saver);
 	g_object_unref (data->builder);
 	g_free (data);
 }
@@ -59,7 +59,7 @@ treeselection_changed_cb (GtkTreeSelection *treeselection,
 	BrowserData    *data;
 	GtkTreeIter     iter;
 	int             page_n;
-	GthPixbufSaver *pixbuf_saver;
+	GthImageSaver *image_saver;
 
 	data = g_object_get_data (G_OBJECT (dialog), BROWSER_DATA_KEY);
 	g_return_if_fail (data != NULL);
@@ -69,12 +69,12 @@ treeselection_changed_cb (GtkTreeSelection *treeselection,
 
 	gtk_tree_model_get (GTK_TREE_MODEL (gtk_builder_get_object (data->builder, "file_type_liststore")), &iter,
 			    FILE_TYPE_COLUMN_N, &page_n,
-			    FILE_TYPE_COLUMN_OBJ, &pixbuf_saver,
+			    FILE_TYPE_COLUMN_OBJ, &image_saver,
 			    -1);
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (_gtk_builder_get_widget (data->builder, "options_notebook")), page_n);
-	gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (data->builder, "file_type_label")), gth_pixbuf_saver_get_display_name (pixbuf_saver));
+	gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (data->builder, "file_type_label")), gth_image_saver_get_display_name (image_saver));
 
-	g_object_unref (pixbuf_saver);
+	g_object_unref (image_saver);
 }
 
 
@@ -87,7 +87,7 @@ ci__dlg_preferences_construct_cb (GtkWidget  *dialog,
 	GtkWidget        *notebook;
 	GtkWidget        *page;
 	GtkListStore     *model;
-	GArray           *pixbuf_saver_types;
+	GArray           *image_saver_types;
 	int               i;
 	GtkTreeSelection *treeselection;
 	GtkTreePath      *path;
@@ -102,26 +102,26 @@ ci__dlg_preferences_construct_cb (GtkWidget  *dialog,
 	gtk_widget_show (page);
 
 	model = (GtkListStore *) gtk_builder_get_object (data->builder, "file_type_liststore");
-	pixbuf_saver_types = gth_main_get_type_set ("pixbuf-saver");
-	for (i = 0; (pixbuf_saver_types != NULL) && (i < pixbuf_saver_types->len); i++) {
-		GthPixbufSaver *pixbuf_saver;
+	image_saver_types = gth_main_get_type_set ("image-saver");
+	for (i = 0; (image_saver_types != NULL) && (i < image_saver_types->len); i++) {
+		GthImageSaver *image_saver;
 		GtkTreeIter     iter;
 		GtkWidget      *options;
 
-		pixbuf_saver = g_object_new (g_array_index (pixbuf_saver_types, GType, i), NULL);
+		image_saver = g_object_new (g_array_index (image_saver_types, GType, i), NULL);
 
 		gtk_list_store_append (model, &iter);
 		gtk_list_store_set (model, &iter,
 				    FILE_TYPE_COLUMN_N, i,
-				    FILE_TYPE_COLUMN_OBJ, pixbuf_saver,
-				    FILE_TYPE_COLUMN_DISPLAY_NAME, gth_pixbuf_saver_get_display_name (pixbuf_saver),
+				    FILE_TYPE_COLUMN_OBJ, image_saver,
+				    FILE_TYPE_COLUMN_DISPLAY_NAME, gth_image_saver_get_display_name (image_saver),
 				    -1);
 
-		options = gth_pixbuf_saver_get_control (pixbuf_saver);
+		options = gth_image_saver_get_control (image_saver);
 		gtk_widget_show (options);
 		gtk_notebook_append_page (GTK_NOTEBOOK (_gtk_builder_get_widget (data->builder, "options_notebook")), options, NULL);
 
-		data->pixbuf_saver = g_list_prepend (data->pixbuf_saver, pixbuf_saver);
+		data->image_saver = g_list_prepend (data->image_saver, image_saver);
 	}
 
 	treeselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (_gtk_builder_get_widget (data->builder, "file_type_treeview")));
@@ -154,8 +154,8 @@ ci__dlg_preferences_apply_cb (GtkWidget  *dialog,
 	data = g_object_get_data (G_OBJECT (dialog), BROWSER_DATA_KEY);
 	g_return_if_fail (data != NULL);
 
-	for (scan = data->pixbuf_saver; scan; scan = scan->next) {
-		GthPixbufSaver *pixbuf_saver = scan->data;
-		gth_pixbuf_saver_save_options (pixbuf_saver);
+	for (scan = data->image_saver; scan; scan = scan->next) {
+		GthImageSaver *image_saver = scan->data;
+		gth_image_saver_save_options (image_saver);
 	}
 }
diff --git a/extensions/cairo_io/preferences.h b/extensions/cairo_io/preferences.h
index f4dddd2..1d48bcc 100644
--- a/extensions/cairo_io/preferences.h
+++ b/extensions/cairo_io/preferences.h
@@ -34,11 +34,11 @@ typedef enum {
 
 /* schemas */
 
-#define GTHUMB_PIXBUF_SAVERS              GTHUMB_SCHEMA ".pixbuf-savers"
-#define GTHUMB_PIXBUF_SAVERS_JPEG_SCHEMA  GTHUMB_PIXBUF_SAVERS ".jpeg"
-#define GTHUMB_PIXBUF_SAVERS_PNG_SCHEMA   GTHUMB_PIXBUF_SAVERS ".png"
-#define GTHUMB_PIXBUF_SAVERS_TGA_SCHEMA   GTHUMB_PIXBUF_SAVERS ".tga"
-#define GTHUMB_PIXBUF_SAVERS_TIFF_SCHEMA  GTHUMB_PIXBUF_SAVERS ".tiff"
+#define GTHUMB_IMAGE_SAVERS               GTHUMB_SCHEMA ".pixbuf-savers"
+#define GTHUMB_IMAGE_SAVERS_JPEG_SCHEMA   GTHUMB_IMAGE_SAVERS ".jpeg"
+#define GTHUMB_IMAGE_SAVERS_PNG_SCHEMA    GTHUMB_IMAGE_SAVERS ".png"
+#define GTHUMB_IMAGE_SAVERS_TGA_SCHEMA    GTHUMB_IMAGE_SAVERS ".tga"
+#define GTHUMB_IMAGE_SAVERS_TIFF_SCHEMA   GTHUMB_IMAGE_SAVERS ".tiff"
 
 /* keys: jpeg */
 
diff --git a/extensions/contact_sheet/dlg-contact-sheet.c b/extensions/contact_sheet/dlg-contact-sheet.c
index eb50973..49d06e5 100644
--- a/extensions/contact_sheet/dlg-contact-sheet.c
+++ b/extensions/contact_sheet/dlg-contact-sheet.c
@@ -729,20 +729,20 @@ dlg_contact_sheet (GthBrowser *browser,
 
 	default_mime_type = g_settings_get_string (data->settings, PREF_CONTACT_SHEET_MIME_TYPE);
 	active_index = 0;
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	for (i = 0; (savers != NULL) && (i < savers->len); i++) {
-		GthPixbufSaver *saver;
+		GthImageSaver *saver;
 		GtkTreeIter     iter;
 
 		saver = g_object_new (g_array_index (savers, GType, i), NULL);
 
-		if (g_str_equal (default_mime_type, gth_pixbuf_saver_get_mime_type (saver)))
+		if (g_str_equal (default_mime_type, gth_image_saver_get_mime_type (saver)))
 			active_index = i;
 
 		gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("filetype_liststore")), &iter);
 		gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("filetype_liststore")), &iter,
-				    FILE_TYPE_COLUMN_MIME_TYPE, gth_pixbuf_saver_get_mime_type (saver),
-				    FILE_TYPE_COLUMN_DEFAULT_EXTENSION, gth_pixbuf_saver_get_default_ext (saver),
+				    FILE_TYPE_COLUMN_MIME_TYPE, gth_image_saver_get_mime_type (saver),
+				    FILE_TYPE_COLUMN_DEFAULT_EXTENSION, gth_image_saver_get_default_ext (saver),
 				    -1);
 
 		g_object_unref (saver);
diff --git a/extensions/contact_sheet/dlg-image-wall.c b/extensions/contact_sheet/dlg-image-wall.c
index 4d76095..da03d7d 100644
--- a/extensions/contact_sheet/dlg-image-wall.c
+++ b/extensions/contact_sheet/dlg-image-wall.c
@@ -268,20 +268,20 @@ dlg_image_wall (GthBrowser *browser,
 
 	default_mime_type = g_settings_get_string (data->settings, PREF_IMAGE_WALL_MIME_TYPE);
 	active_index = 0;
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	for (i = 0; (savers != NULL) && (i < savers->len); i++) {
-		GthPixbufSaver *saver;
+		GthImageSaver *saver;
 		GtkTreeIter     iter;
 
 		saver = g_object_new (g_array_index (savers, GType, i), NULL);
 
-		if (g_str_equal (default_mime_type, gth_pixbuf_saver_get_mime_type (saver)))
+		if (g_str_equal (default_mime_type, gth_image_saver_get_mime_type (saver)))
 			active_index = i;
 
 		gtk_list_store_append (GTK_LIST_STORE (GET_WIDGET ("filetype_liststore")), &iter);
 		gtk_list_store_set (GTK_LIST_STORE (GET_WIDGET ("filetype_liststore")), &iter,
-				    FILE_TYPE_COLUMN_MIME_TYPE, gth_pixbuf_saver_get_mime_type (saver),
-				    FILE_TYPE_COLUMN_DEFAULT_EXTENSION, gth_pixbuf_saver_get_default_ext (saver),
+				    FILE_TYPE_COLUMN_MIME_TYPE, gth_image_saver_get_mime_type (saver),
+				    FILE_TYPE_COLUMN_DEFAULT_EXTENSION, gth_image_saver_get_default_ext (saver),
 				    -1);
 
 		g_object_unref (saver);
diff --git a/extensions/contact_sheet/gth-contact-sheet-creator.c b/extensions/contact_sheet/gth-contact-sheet-creator.c
index 45d8f70..be6fae8 100644
--- a/extensions/contact_sheet/gth-contact-sheet-creator.c
+++ b/extensions/contact_sheet/gth-contact-sheet-creator.c
@@ -35,10 +35,10 @@ G_DEFINE_TYPE (GthContactSheetCreator, gth_contact_sheet_creator, GTH_TYPE_TASK)
 
 
 typedef struct {
-	GthFileData *file_data;
-	GdkPixbuf   *thumbnail;
-	int          original_width;
-	int          original_height;
+	GthFileData     *file_data;
+	cairo_surface_t *thumbnail;
+	int              original_width;
+	int              original_height;
 } ItemData;
 
 
@@ -60,7 +60,7 @@ item_data_new (GthFileData *file_data)
 static void
 item_data_free (ItemData *item_data)
 {
-	_g_object_unref (item_data->thumbnail);
+	cairo_surface_destroy (item_data->thumbnail);
 	_g_object_unref (item_data->file_data);
 	g_free (item_data);
 }
@@ -100,7 +100,6 @@ struct _GthContactSheetCreatorPrivate {
 	PangoLayout          *pango_layout;
 
 	GthImageLoader       *image_loader;
-	GthPixbufSaver       *pixbuf_saver;
 	GList                *files;                /* ItemData list */
 	GList                *current_file;         /* Next file to be loaded. */
 	gint                  n_files;              /* Used for the progress signal. */
@@ -349,19 +348,19 @@ end_page (GthContactSheetCreator  *self,
 	  int                      page_n,
 	  GError                 **error)
 {
-	GdkPixbuf *pixbuf;
-	char      *buffer;
-	gsize      size;
-
-	pixbuf = _gdk_pixbuf_new_from_cairo_context (self->priv->cr);
-	if (! gth_pixbuf_saver_save_pixbuf (self->priv->pixbuf_saver,
-					    pixbuf,
-					    &buffer,
-					    &size,
-					    self->priv->mime_type,
-					    error))
+	GthImage *image;
+	char     *buffer;
+	gsize     size;
+
+	image = gth_image_new ();
+	gth_image_set_cairo_surface (image, cairo_get_target (self->priv->cr));
+	if (! gth_image_save_to_buffer (image,
+					self->priv->mime_type,
+					&buffer,
+					&size,
+					error))
 	{
-		g_object_unref (pixbuf);
+		g_object_unref (image);
 		return FALSE;
 	}
 
@@ -373,13 +372,14 @@ end_page (GthContactSheetCreator  *self,
 			     gth_task_get_cancellable (GTH_TASK (self)),
 			     error))
 	{
-		g_object_unref (pixbuf);
+		g_object_unref (image);
 		return FALSE;
 	}
 
-	self->priv->created_files = g_list_prepend (self->priv->created_files, g_object_ref (self->priv->destination_file));
+	self->priv->created_files = g_list_prepend (self->priv->created_files,
+						    g_object_ref (self->priv->destination_file));
 
-	g_object_unref (pixbuf);
+	g_object_unref (image);
 
 	/* image map file. */
 
@@ -591,10 +591,10 @@ paint_frame (GthContactSheetCreator *self,
 static void
 paint_image (GthContactSheetCreator *self,
 	     cairo_rectangle_int_t  *image_rect,
-	     GdkPixbuf              *image)
+	     cairo_surface_t        *image)
 {
 	cairo_save (self->priv->cr);
-	gdk_cairo_set_source_pixbuf (self->priv->cr, image, image_rect->x, image_rect->y);
+	cairo_set_source_surface (self->priv->cr, image, image_rect->x, image_rect->y);
   	cairo_rectangle (self->priv->cr, image_rect->x, image_rect->y, image_rect->width, image_rect->height);
   	cairo_fill (self->priv->cr);
   	cairo_restore (self->priv->cr);
@@ -613,9 +613,6 @@ export (GthContactSheetCreator *self)
 	GList     *scan;
 	GError    *error = NULL;
 
-	if (self->priv->pixbuf_saver == NULL)
-		self->priv->pixbuf_saver = gth_main_get_pixbuf_saver (self->priv->mime_type);
-
 	columns = ((self->priv->page_width - self->priv->theme->col_spacing) / (self->priv->thumb_width + (self->priv->theme->frame_hpadding * 2) + self->priv->theme->col_spacing));
 	first_row = TRUE;
 	begin_page (self, ++page_n);
@@ -724,8 +721,8 @@ export (GthContactSheetCreator *self)
 				int                   thumbnail_height;
 				cairo_rectangle_int_t image_rect;
 
-				thumbnail_width = gdk_pixbuf_get_width (row_item->thumbnail);
-				thumbnail_height = gdk_pixbuf_get_height (row_item->thumbnail);
+				thumbnail_width = cairo_image_surface_get_width (row_item->thumbnail);
+				thumbnail_height = cairo_image_surface_get_height (row_item->thumbnail);
 
 				image_rect.x = x + (frame_width - thumbnail_width) / 2;
 				image_rect.y = y + (frame_height - thumbnail_height) / 2;
@@ -923,7 +920,7 @@ image_loader_ready_cb (GObject      *source_object,
 {
 	GthContactSheetCreator *self = user_data;
 	GthImage               *image = NULL;
-	GdkPixbuf              *pixbuf;
+	cairo_surface_t        *image_surface;
 	int                     original_width;
 	int                     original_height;
 	GError                 *error = NULL;
@@ -940,17 +937,17 @@ image_loader_ready_cb (GObject      *source_object,
 		return;
 	}
 
-	pixbuf = gth_image_get_pixbuf (image);
+	image_surface = gth_image_get_cairo_surface (image);
 
 	item_data = self->priv->current_file->data;
 	if (self->priv->squared_thumbnails)
-		item_data->thumbnail = _gdk_pixbuf_scale_squared (pixbuf, MIN (self->priv->thumb_height, self->priv->thumb_width), GDK_INTERP_BILINEAR);
+		item_data->thumbnail = _cairo_image_surface_scale_squared (image_surface, MIN (self->priv->thumb_height, self->priv->thumb_width), SCALE_FILTER_BEST, NULL);
 	else
-		item_data->thumbnail = g_object_ref (pixbuf);
+		item_data->thumbnail = cairo_surface_reference (image_surface);
 	item_data->original_width = original_width;
 	item_data->original_height = original_height;
 
-	g_object_unref (pixbuf);
+	cairo_surface_destroy (image_surface);
 	g_object_unref (image);
 
 	self->priv->current_file = self->priv->current_file->next;
@@ -1032,7 +1029,6 @@ gth_contact_sheet_creator_finalize (GObject *object)
 	_g_object_list_unref (self->priv->created_files);
 	g_list_foreach (self->priv->files, (GFunc) item_data_free, NULL);
 	g_list_free (self->priv->files);
-	_g_object_unref (self->priv->pixbuf_saver);
 	_g_object_unref (self->priv->image_loader);
 	_g_object_unref (self->priv->pango_layout);
 	_g_object_unref (self->priv->pango_context);
@@ -1084,7 +1080,6 @@ gth_contact_sheet_creator_init (GthContactSheetCreator *self)
 	self->priv->pango_context = NULL;
 	self->priv->pango_layout = NULL;
 	self->priv->image_loader = NULL;
-	self->priv->pixbuf_saver = NULL;
 	self->priv->files = NULL;
 	self->priv->created_files = NULL;
 	self->priv->imagemap_file = NULL;
diff --git a/extensions/convert_format/dlg-convert-format.c b/extensions/convert_format/dlg-convert-format.c
index ab203f9..c8660de 100644
--- a/extensions/convert_format/dlg-convert-format.c
+++ b/extensions/convert_format/dlg-convert-format.c
@@ -68,10 +68,12 @@ help_button_clicked_cb (GtkWidget  *widget,
 }
 
 
-static void
-convert_step (GthPixbufTask *pixbuf_task)
+static gpointer
+exec_convert (GthAsyncTask *task,
+	      gpointer      user_data)
 {
-	pixbuf_task->dest = gdk_pixbuf_copy (pixbuf_task->src);
+	gth_image_task_copy_source_to_destination (GTH_IMAGE_TASK (task));
+	return NULL;
 }
 
 
@@ -90,23 +92,22 @@ ok_button_clicked_cb (GtkWidget  *widget,
 			    -1);
 	g_settings_set_string (data->settings, PREF_CONVERT_FORMAT_MIME_TYPE, mime_type);
 
-	convert_task = gth_pixbuf_task_new (_("Converting images"),
-					    TRUE,
+	convert_task = gth_image_task_new (_("Converting images"),
 					    NULL,
-					    convert_step,
+					    exec_convert,
 					    NULL,
 					    NULL,
 					    NULL);
-	list_task = gth_pixbuf_list_task_new (data->browser,
-					      data->file_list,
-					      GTH_PIXBUF_TASK (convert_task));
-	gth_pixbuf_list_task_set_overwrite_mode (GTH_PIXBUF_LIST_TASK (list_task), GTH_OVERWRITE_ASK);
-	gth_pixbuf_list_task_set_output_mime_type (GTH_PIXBUF_LIST_TASK (list_task), mime_type);
+	list_task = gth_image_list_task_new (data->browser,
+					     data->file_list,
+					     GTH_IMAGE_TASK (convert_task));
+	gth_image_list_task_set_overwrite_mode (GTH_IMAGE_LIST_TASK (list_task), GTH_OVERWRITE_ASK);
+	gth_image_list_task_set_output_mime_type (GTH_IMAGE_LIST_TASK (list_task), mime_type);
 	if (data->use_destination) {
 		GFile *destination;
 
 		destination = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")));
-		gth_pixbuf_list_task_set_destination (GTH_PIXBUF_LIST_TASK (list_task), destination);
+		gth_image_list_task_set_destination (GTH_IMAGE_LIST_TASK (list_task), destination);
 
 		g_object_unref (destination);
 	}
@@ -158,7 +159,7 @@ dlg_convert_format (GthBrowser *browser,
 
 	/* Set widgets data. */
 
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	if (savers != NULL) {
 		char         *default_mime_type;
 		GthIconCache *icon_cache;
@@ -170,14 +171,14 @@ dlg_convert_format (GthBrowser *browser,
 		list_store = (GtkListStore *) GET_WIDGET ("mime_type_liststore");
 		for (i = 0; i < savers->len; i++) {
 			GType           saver_type;
-			GthPixbufSaver *saver;
+			GthImageSaver *saver;
 			const char     *mime_type;
 			GdkPixbuf      *pixbuf;
 			GtkTreeIter     iter;
 
 			saver_type = g_array_index (savers, GType, i);
 			saver = g_object_new (saver_type, NULL);
-			mime_type = gth_pixbuf_saver_get_mime_type (saver);
+			mime_type = gth_image_saver_get_mime_type (saver);
 			pixbuf = gth_icon_cache_get_pixbuf (icon_cache, g_content_type_get_icon (mime_type));
 			gtk_list_store_append (list_store, &iter);
 			gtk_list_store_set (list_store, &iter,
diff --git a/extensions/desktop_background/actions.c b/extensions/desktop_background/actions.c
index 0f5c3b7..a729e67 100644
--- a/extensions/desktop_background/actions.c
+++ b/extensions/desktop_background/actions.c
@@ -266,19 +266,19 @@ gth_browser_activate_action_tool_desktop_background (GtkAction  *action,
 
 		viewer_page = gth_browser_get_viewer_page (browser);
 		if (viewer_page != NULL) {
-			GdkPixbuf *pixbuf;
+			GthImage *image;
 
-			pixbuf = gth_image_viewer_page_get_pixbuf (GTH_IMAGE_VIEWER_PAGE (viewer_page));
+			image = gth_image_new_for_surface (gth_image_viewer_page_get_image (GTH_IMAGE_VIEWER_PAGE (viewer_page)));
 			file_data = gth_file_data_new (wdata->new_file, NULL);
-			_gdk_pixbuf_save_async (pixbuf,
-						file_data,
+			gth_image_save_to_file (image,
 						"image/jpeg",
+						file_data,
 						TRUE,
 						wallpaper_save_ready_cb,
 						wdata);
 			saving_wallpaper = TRUE;
 
-			g_object_unref (pixbuf);
+			g_object_unref (image);
 		}
 	}
 
diff --git a/extensions/exiv2_tools/exiv2-utils.cpp b/extensions/exiv2_tools/exiv2-utils.cpp
index a38ab08..473537f 100644
--- a/extensions/exiv2_tools/exiv2-utils.cpp
+++ b/extensions/exiv2_tools/exiv2-utils.cpp
@@ -957,7 +957,7 @@ dump_exif_data (Exiv2::ExifData &exifData,
 static Exiv2::DataBuf
 exiv2_write_metadata_private (Exiv2::Image::AutoPtr  image,
 			      GFileInfo             *info,
-			      GdkPixbuf             *pixbuf)
+			      GthImage              *image_data)
 {
 	static char  *software_name = NULL;
 	char        **attributes;
@@ -1028,15 +1028,19 @@ exiv2_write_metadata_private (Exiv2::Image::AutoPtr  image,
 
 	// Update the dimension tags with actual image values
 
+	cairo_surface_t *surface = NULL;
 	int width = 0;
 	int height = 0;
 
-	if (pixbuf != NULL) {
-		width = gdk_pixbuf_get_width (pixbuf);
+	if (image_data != NULL)
+		surface = gth_image_get_cairo_surface (image_data);
+
+	if (surface != NULL) {
+		width = cairo_image_surface_get_width (surface);
 		if (width > 0)
 			ed["Exif.Photo.PixelXDimension"] = width;
 
-		height = gdk_pixbuf_get_height (pixbuf);
+		height = cairo_image_surface_get_height (surface);
 		if (height > 0)
 			ed["Exif.Photo.PixelYDimension"] = height;
 
@@ -1046,15 +1050,17 @@ exiv2_write_metadata_private (Exiv2::Image::AutoPtr  image,
 	// Update the thumbnail
 
 	Exiv2::ExifThumb thumb(ed);
-	if ((pixbuf != NULL) && (width > 0) && (height > 0)) {
-		GdkPixbuf *thumb_pixbuf;
-		char      *buffer;
-		gsize      buffer_size;
+	if ((surface != NULL) && (width > 0) && (height > 0)) {
+		cairo_surface_t *thumbnail;
+		GthImage        *thumbnail_data;
+		char            *buffer;
+		gsize            buffer_size;
 
 		scale_keeping_ratio (&width, &height, 128, 128, FALSE);
-		thumb_pixbuf = _gdk_pixbuf_scale_simple_safe (pixbuf, width, height, GDK_INTERP_BILINEAR);
-		if (gdk_pixbuf_save_to_buffer (thumb_pixbuf, &buffer, &buffer_size, "jpeg", NULL, NULL)) {
-			thumb.setJpegThumbnail ((Exiv2::byte*) buffer, buffer_size);
+		thumbnail = _cairo_image_surface_scale (surface, width, height, SCALE_FILTER_BEST, NULL);
+		thumbnail_data = gth_image_new_for_surface (thumbnail);
+		if (gth_image_save_to_buffer (thumbnail_data, "image/jpeg", &buffer, &buffer_size, NULL)) {
+			thumb.setJpegThumbnail ((Exiv2::byte *) buffer, buffer_size);
 			ed["Exif.Thumbnail.XResolution"] = 72;
 			ed["Exif.Thumbnail.YResolution"] = 72;
 			ed["Exif.Thumbnail.ResolutionUnit"] =  2;
@@ -1063,11 +1069,15 @@ exiv2_write_metadata_private (Exiv2::Image::AutoPtr  image,
 		else
 			thumb.erase();
 
-		g_object_unref (thumb_pixbuf);
+		g_object_unref (thumbnail_data);
+		cairo_surface_destroy (thumbnail);
 	}
 	else
 		thumb.erase();
 
+	if (surface != NULL)
+		cairo_surface_destroy (surface);
+
 	// Update the DateTime tag
 
 	if (g_file_info_get_attribute_object (info, "Exif::Image::DateTime") == NULL) {
@@ -1214,14 +1224,14 @@ exiv2_supports_writes (const char *mime_type)
 
 extern "C"
 gboolean
-exiv2_write_metadata (SavePixbufData *data)
+exiv2_write_metadata (GthImageSaveData *data)
 {
 	if (exiv2_supports_writes (data->mime_type)) {
 		try {
 			Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) data->buffer, data->buffer_size);
 			g_assert (image.get() != 0);
 
-			Exiv2::DataBuf buf = exiv2_write_metadata_private (image, data->file_data->info, data->pixbuf);
+			Exiv2::DataBuf buf = exiv2_write_metadata_private (image, data->file_data->info, data->image);
 
 			g_free (data->buffer);
 			data->buffer = g_memdup (buf.pData_, buf.size_);
@@ -1244,14 +1254,14 @@ gboolean
 exiv2_write_metadata_to_buffer (void      **buffer,
 				gsize      *buffer_size,
 				GFileInfo  *info,
-				GdkPixbuf  *pixbuf,
+				GthImage   *image_data,
 				GError    **error)
 {
 	try {
 		Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open ((Exiv2::byte*) *buffer, *buffer_size);
 		g_assert (image.get() != 0);
 
-		Exiv2::DataBuf buf = exiv2_write_metadata_private (image, info, pixbuf);
+		Exiv2::DataBuf buf = exiv2_write_metadata_private (image, info, image_data);
 
 		g_free (*buffer);
 		*buffer = g_memdup (buf.pData_, buf.size_);
diff --git a/extensions/exiv2_tools/exiv2-utils.h b/extensions/exiv2_tools/exiv2-utils.h
index 22ead69..d3529ac 100644
--- a/extensions/exiv2_tools/exiv2-utils.h
+++ b/extensions/exiv2_tools/exiv2-utils.h
@@ -36,30 +36,30 @@ extern const char *_LOCATION_TAG_NAMES[];
 extern const char *_KEYWORDS_TAG_NAMES[];
 extern const char *_RATING_TAG_NAMES[];
 
-gboolean   exiv2_read_metadata_from_file    (GFile           *file,
-					     GFileInfo       *info,
-					     GCancellable    *cancellable,
-					     GError         **error);
-gboolean   exiv2_read_metadata_from_buffer  (void            *buffer,
-					     gsize            buffer_size,
-					     GFileInfo       *info,
-					     GError         **error);
-GFile *    exiv2_get_sidecar                (GFile           *file);
-gboolean   exiv2_read_sidecar               (GFile           *file,
-					     GFileInfo       *info);
-gboolean   exiv2_supports_writes            (const char      *mime_type);
-gboolean   exiv2_write_metadata  	    (SavePixbufData  *data);
-gboolean   exiv2_write_metadata_to_buffer   (void           **buffer,
-					     gsize           *buffer_size,
-					     GFileInfo       *info,
-					     GdkPixbuf       *pixbuf, /* optional */
-					     GError         **error);
-gboolean   exiv2_clear_metadata             (void           **buffer,
-					     gsize           *buffer_size,
-					     GError         **error);
-GdkPixbuf *exiv2_generate_thumbnail         (const char      *uri,
-					     const char      *mime_type,
-					     int              size);
+gboolean   exiv2_read_metadata_from_file    (GFile             *file,
+					     GFileInfo         *info,
+					     GCancellable      *cancellable,
+					     GError           **error);
+gboolean   exiv2_read_metadata_from_buffer  (void              *buffer,
+					     gsize              buffer_size,
+					     GFileInfo         *info,
+					     GError           **error);
+GFile *    exiv2_get_sidecar                (GFile             *file);
+gboolean   exiv2_read_sidecar               (GFile             *file,
+					     GFileInfo         *info);
+gboolean   exiv2_supports_writes            (const char        *mime_type);
+gboolean   exiv2_write_metadata  	    (GthImageSaveData  *data);
+gboolean   exiv2_write_metadata_to_buffer   (void              **buffer,
+					     gsize              *buffer_size,
+					     GFileInfo          *info,
+					     GthImage           *image_data, /* optional */
+					     GError           **error);
+gboolean   exiv2_clear_metadata             (void             **buffer,
+					     gsize             *buffer_size,
+					     GError           **error);
+GdkPixbuf *exiv2_generate_thumbnail         (const char        *uri,
+					     const char        *mime_type,
+					     int                size);
 
 G_END_DECLS
 
diff --git a/extensions/exiv2_tools/main.c b/extensions/exiv2_tools/main.c
index 1519404..94e02ad 100644
--- a/extensions/exiv2_tools/main.c
+++ b/extensions/exiv2_tools/main.c
@@ -290,7 +290,7 @@ gthumb_extension_activate (void)
 		gth_main_register_type ("edit-comment-dialog-page", GTH_TYPE_EDIT_IPTC_PAGE);
 		gth_hook_add_callback ("delete-metadata", 10, G_CALLBACK (exiv2_delete_metadata_cb), NULL);
 	}
-	gth_hook_add_callback ("save-pixbuf", 10, G_CALLBACK (exiv2_write_metadata), NULL);
+	gth_hook_add_callback ("save-image", 10, G_CALLBACK (exiv2_write_metadata), NULL);
 	if (gth_hook_present ("jpegtran-after"))
 		gth_hook_add_callback ("jpegtran-after", 10, G_CALLBACK (exiv2_jpeg_tran_cb), NULL);
 	gth_hook_add_callback ("generate-thumbnail", 10, G_CALLBACK (exiv2_generate_thumbnail), NULL);
diff --git a/extensions/facebook/facebook-service.c b/extensions/facebook/facebook-service.c
index 06753fc..1093c8a 100644
--- a/extensions/facebook/facebook-service.c
+++ b/extensions/facebook/facebook-service.c
@@ -688,7 +688,6 @@ upload_photo_file_buffer_ready_cb (void     **buffer,
 	FacebookService *self = user_data;
 	GthFileData     *file_data;
 	SoupMultipart   *multipart;
-	GthPixbufSaver  *saver;
 	char            *uri;
 	SoupBuffer      *body;
 	SoupMessage     *msg;
@@ -740,17 +739,16 @@ upload_photo_file_buffer_ready_cb (void     **buffer,
 
 	/* the file part: rotate and scale the image if required */
 
-	saver = gth_main_get_pixbuf_saver (gth_file_data_get_mime_type (file_data));
-	if (saver != NULL) {
-		GInputStream *stream;
-		GdkPixbuf    *pixbuf;
-		GdkPixbuf    *tmp_pixbuf;
-		int           width;
-		int           height;
+	{
+		GInputStream    *stream;
+		GthImage        *image;
+		cairo_surface_t *surface;
+		int              width;
+		int              height;
 
 		stream = g_memory_input_stream_new_from_data (*buffer, count, NULL);
-		pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, &error);
-		if (pixbuf == NULL) {
+		image = gth_image_new_from_stream (stream, -1, NULL, NULL, NULL, &error);
+		if (image == NULL) {
 			g_object_unref (stream);
 			soup_multipart_free (multipart);
 			upload_photos_done (self, error);
@@ -759,42 +757,41 @@ upload_photo_file_buffer_ready_cb (void     **buffer,
 
 		g_object_unref (stream);
 
-		tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
-		g_object_unref (pixbuf);
-		pixbuf = tmp_pixbuf;
-
-		width = gdk_pixbuf_get_width (pixbuf);
-		height = gdk_pixbuf_get_height (pixbuf);
+		surface = gth_image_get_cairo_surface (image);
+		width = cairo_image_surface_get_width (surface);
+		height = cairo_image_surface_get_height (surface);
 		if (scale_keeping_ratio (&width,
 					 &height,
 					 self->priv->post_photos->max_resolution,
 					 self->priv->post_photos->max_resolution,
 					 FALSE))
 		{
-			tmp_pixbuf = _gdk_pixbuf_scale_simple_safe (pixbuf, width, height, GDK_INTERP_BILINEAR);
-			g_object_unref (pixbuf);
-			pixbuf = tmp_pixbuf;
+			cairo_surface_t *scaled;
+
+			scaled = _cairo_image_surface_scale (surface, width, height, SCALE_FILTER_BEST, NULL);
+			cairo_surface_destroy (surface);
+			surface = scaled;
 		}
 
 		g_free (*buffer);
 		*buffer = NULL;
 
-		if (! gth_pixbuf_saver_save_pixbuf (saver,
-						    pixbuf,
-						    (char **)buffer,
-						    &count,
-						    gth_file_data_get_mime_type (file_data),
-						    &error))
+		gth_image_set_cairo_surface (image, surface);
+		if (! gth_image_save_to_buffer (image,
+						gth_file_data_get_mime_type (file_data),
+					        (char **) buffer,
+					        &count,
+					        &error))
 		{
-			g_object_unref (pixbuf);
-			g_object_unref (saver);
+			cairo_surface_destroy (surface);
+			g_object_unref (image);
 			soup_multipart_free (multipart);
 			upload_photos_done (self, error);
 			return;
 		}
 
-		g_object_unref (pixbuf);
-		g_object_unref (saver);
+		cairo_surface_destroy (surface);
+		g_object_unref (image);
 	}
 
 	uri = g_file_get_uri (file_data->file);
diff --git a/extensions/file_tools/gth-file-tool-enhance.c b/extensions/file_tools/gth-file-tool-enhance.c
index 34a6dd2..2560f58 100644
--- a/extensions/file_tools/gth-file-tool-enhance.c
+++ b/extensions/file_tools/gth-file-tool-enhance.c
@@ -235,9 +235,9 @@ enhance_exec (GthAsyncTask *task,
 		p_destination = p_destination_line;
 		for (x = 0; x < width; x++) {
 			CAIRO_GET_RGBA (p_source, red, green, blue, alpha);
-			red   = levels_func (red, enhance_data->levels, RED_PIX);
-			green = levels_func (green, enhance_data->levels, GREEN_PIX);
-			blue  = levels_func (blue, enhance_data->levels, BLUE_PIX);
+			red   = levels_func (red, enhance_data->levels, GTH_CHANNEL_RED);
+			green = levels_func (green, enhance_data->levels, GTH_CHANNEL_GREEN);
+			blue  = levels_func (blue, enhance_data->levels, GTH_CHANNEL_BLUE);
 			CAIRO_SET_RGBA (p_destination, red, green, blue, alpha);
 
 			p_source += 4;
diff --git a/extensions/file_tools/gth-file-tool-equalize.c b/extensions/file_tools/gth-file-tool-equalize.c
index 22adda9..ffc64d5 100644
--- a/extensions/file_tools/gth-file-tool-equalize.c
+++ b/extensions/file_tools/gth-file-tool-equalize.c
@@ -154,11 +154,11 @@ equalize_exec (GthAsyncTask *task,
 		p_destination = p_destination_line;
 		for (x = 0; x < width; x++) {
 			CAIRO_GET_RGBA (p_source, red, green, blue, alpha);
-			red   = equalize_func (red, equalize_data->part, RED_PIX);
-			green = equalize_func (green, equalize_data->part, GREEN_PIX);
-			blue  = equalize_func (blue, equalize_data->part, BLUE_PIX);
+			red   = equalize_func (red, equalize_data->part, GTH_CHANNEL_RED);
+			green = equalize_func (green, equalize_data->part, GTH_CHANNEL_GREEN);
+			blue  = equalize_func (blue, equalize_data->part, GTH_CHANNEL_BLUE);
 			if (alpha != 0xff)
-				alpha = equalize_func (alpha, equalize_data->part, ALPHA_PIX);
+				alpha = equalize_func (alpha, equalize_data->part, GTH_CHANNEL_ALPHA);
 			CAIRO_SET_RGBA (p_destination, red, green, blue, alpha);
 
 			p_source += 4;
diff --git a/extensions/gstreamer_tools/actions.c b/extensions/gstreamer_tools/actions.c
index f44523e..b25b845 100644
--- a/extensions/gstreamer_tools/actions.c
+++ b/extensions/gstreamer_tools/actions.c
@@ -39,7 +39,7 @@ typedef struct {
 	GSettings          *settings;
 	GthMediaViewerPage *page;
 	gboolean            playing_before_screenshot;
-	GdkPixbuf          *pixbuf;
+	GthImage           *image;
 	GthFileData        *file_data;
 } SaveData;
 
@@ -48,7 +48,7 @@ static void
 save_date_free (SaveData *save_data)
 {
 	_g_object_unref (save_data->file_data);
-	_g_object_unref (save_data->pixbuf);
+	_g_object_unref (save_data->image);
 	_g_object_unref (save_data->settings);
 	g_free (save_data);
 }
@@ -100,9 +100,9 @@ save_as_response_cb (GtkDialog  *file_sel,
 
 	save_data->file_data = gth_file_data_new (file, NULL);
 	gth_file_data_set_mime_type (save_data->file_data, mime_type);
-	_gdk_pixbuf_save_async (save_data->pixbuf,
-				save_data->file_data,
+	gth_image_save_to_file (save_data->image,
 				mime_type,
+				save_data->file_data,
 				TRUE,
 				screenshot_saved_cb,
 				save_data);
@@ -128,8 +128,8 @@ screenshot_ready_cb (GdkPixbuf *pixbuf,
 		return;
 	}
 
-	save_data->pixbuf = pixbuf;
-	file_sel = gth_file_chooser_dialog_new (_("Save Image"), GTK_WINDOW (save_data->browser), "pixbuf-saver");
+	save_data->image = gth_image_new_for_pixbuf (pixbuf);
+	file_sel = gth_file_chooser_dialog_new (_("Save Image"), GTK_WINDOW (save_data->browser), "image-saver");
 
 	{
 		char        *last_uri;
diff --git a/extensions/image_rotation/rotation-utils.c b/extensions/image_rotation/rotation-utils.c
index 6121bfd..208d5c0 100644
--- a/extensions/image_rotation/rotation-utils.c
+++ b/extensions/image_rotation/rotation-utils.c
@@ -310,33 +310,32 @@ file_buffer_ready_cb (void     **buffer,
 	else
 #endif /* HAVE_LIBJPEG */
 	{
-		GInputStream *istream;
-		GdkPixbuf    *original_pixbuf;
-		GdkPixbuf    *tmp;
-		GdkPixbuf    *transformed_pixbuf;
+		GInputStream    *istream;
+		GthImage        *image;
+		cairo_surface_t *surface;
+		cairo_surface_t *transformed;
 
 		istream = g_memory_input_stream_new_from_data (*buffer, count, NULL);
-		original_pixbuf = gdk_pixbuf_new_from_stream (istream, tdata->cancellable, &error);
-		if (original_pixbuf == NULL) {
+		image = gth_image_new_from_stream (istream, -1, NULL, NULL, tdata->cancellable, &error);
+		if (image == NULL) {
 			tdata->ready_func (error, tdata->user_data);
 			transformation_data_free (tdata);
 			return;
 		}
 
-		tmp = gdk_pixbuf_apply_embedded_orientation (original_pixbuf);
-		g_object_unref (original_pixbuf);
-		original_pixbuf = tmp;
-
-		transformed_pixbuf = _gdk_pixbuf_transform (original_pixbuf, tdata->transform);
-		_gdk_pixbuf_save_async (transformed_pixbuf,
-					tdata->file_data,
+		surface = gth_image_get_cairo_surface (image);
+		transformed = _cairo_image_surface_transform (surface, tdata->transform);
+		gth_image_set_cairo_surface (image, transformed);
+		gth_image_save_to_file (image,
 					gth_file_data_get_mime_type (tdata->file_data),
+					tdata->file_data,
 					TRUE,
 					pixbuf_saved_cb,
 					tdata);
 
-		g_object_unref (transformed_pixbuf);
-		g_object_unref (original_pixbuf);
+		cairo_surface_destroy (transformed);
+		cairo_surface_destroy (surface);
+		g_object_unref (image);
 		g_object_unref (istream);
 	}
 }
diff --git a/extensions/image_viewer/gth-image-viewer-page.c b/extensions/image_viewer/gth-image-viewer-page.c
index 028bc13..fd52656 100644
--- a/extensions/image_viewer/gth-image-viewer-page.c
+++ b/extensions/image_viewer/gth-image-viewer-page.c
@@ -1108,7 +1108,7 @@ _gth_image_viewer_page_real_save (GthViewerPage *base,
 	GthImageViewerPage *self;
 	SaveData           *data;
 	GthFileData        *current_file;
-	GdkPixbuf          *pixbuf;
+	GthImage           *image;
 
 	self = (GthImageViewerPage *) base;
 
@@ -1147,15 +1147,15 @@ _gth_image_viewer_page_real_save (GthViewerPage *base,
 	 * wants to save (see load_file_delayed_cb in gth-browser.c). */
 	g_file_info_set_attribute_boolean (data->file_to_save->info, "gth::file::is-modified", FALSE);
 
-	pixbuf = gth_image_viewer_get_current_pixbuf (GTH_IMAGE_VIEWER (self->priv->viewer));
-	_gdk_pixbuf_save_async (pixbuf,
+	image = gth_image_new_for_surface (gth_image_viewer_get_current_image (GTH_IMAGE_VIEWER (self->priv->viewer)));
+	gth_image_save_to_file (image,
+				mime_type,
 				data->file_to_save,
-			        mime_type,
 			        TRUE,
 				image_saved_cb,
 				data);
 
-	_g_object_unref (pixbuf);
+	_g_object_unref (image);
 }
 
 
@@ -1164,7 +1164,7 @@ gth_image_viewer_page_real_can_save (GthViewerPage *base)
 {
 	GArray *savers;
 
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	return (savers != NULL) && (savers->len > 0);
 }
 
@@ -1247,7 +1247,7 @@ gth_image_viewer_page_real_save_as (GthViewerPage *base,
 	self = GTH_IMAGE_VIEWER_PAGE (base);
 	file_sel = gth_file_chooser_dialog_new (_("Save Image"),
 						GTK_WINDOW (self->priv->browser),
-						"pixbuf-saver");
+						"image-saver");
 
 	uri = g_file_get_uri (self->priv->file_data->file);
 	gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_sel), uri);
diff --git a/extensions/importer/gth-import-task.c b/extensions/importer/gth-import-task.c
index 6a21869..801e39e 100644
--- a/extensions/importer/gth-import-task.c
+++ b/extensions/importer/gth-import-task.c
@@ -334,7 +334,7 @@ after_saving_to_destination (GthImportTask  *self,
 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
 			if (self->priv->default_response != GTH_OVERWRITE_RESPONSE_ALWAYS_NO) {
 				GInputStream *stream;
-				GdkPixbuf    *pixbuf;
+				GthImage     *image;
 				GtkWidget    *dialog;
 
 				/* take ownership of the buffer */
@@ -353,15 +353,15 @@ after_saving_to_destination (GthImportTask  *self,
 
 				if (self->priv->buffer != NULL) {
 					stream = g_memory_input_stream_new_from_data (self->priv->buffer, self->priv->buffer_size, NULL);
-					pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, 128, 128, TRUE, NULL, NULL);
+					image = gth_image_new_from_stream (stream, 128, NULL, NULL, NULL, NULL);
 				}
 				else {
 					stream = NULL;
-					pixbuf = NULL;
+					image = NULL;
 				}
 
 				dialog = gth_overwrite_dialog_new (file_data->file,
-								   pixbuf,
+								   image,
 								   self->priv->destination_file->file,
 								   self->priv->default_response,
 								   self->priv->files->next == NULL);
@@ -372,7 +372,7 @@ after_saving_to_destination (GthImportTask  *self,
 				gtk_widget_show (dialog);
 				gth_task_dialog (GTH_TASK (self), TRUE, dialog);
 
-				_g_object_unref (pixbuf);
+				_g_object_unref (image);
 				_g_object_unref (stream);
 			}
 			else
diff --git a/extensions/resize_images/dlg-resize-images.c b/extensions/resize_images/dlg-resize-images.c
index 89de185..9885493 100644
--- a/extensions/resize_images/dlg-resize-images.c
+++ b/extensions/resize_images/dlg-resize-images.c
@@ -84,16 +84,21 @@ destroy_cb (GtkWidget  *widget,
 }
 
 
-static void
-resize_step (GthPixbufTask *pixbuf_task)
+static gpointer
+exec_resize (GthAsyncTask *task,
+	     gpointer      user_data)
 {
-	ResizeData *resize_data = pixbuf_task->data;
-	int         w, h;
-	int         new_w, new_h;
-	int         max_w, max_h;
-
-	w = gdk_pixbuf_get_width (pixbuf_task->src);
-	h = gdk_pixbuf_get_height (pixbuf_task->src);
+	ResizeData      *resize_data = user_data;
+	cairo_surface_t *source;
+	cairo_surface_t *destination;
+	int              w, h;
+	int              new_w, new_h;
+	int              max_w, max_h;
+	GthImage        *destination_image;
+
+	source = gth_image_get_cairo_surface (gth_image_task_get_source (GTH_IMAGE_TASK (task)));
+	w = cairo_image_surface_get_width (source);
+	h = cairo_image_surface_get_height (source);
 
 	if (resize_data->allow_swap
 	    && (((h > w) && (resize_data->width > resize_data->height))
@@ -121,11 +126,18 @@ resize_step (GthPixbufTask *pixbuf_task)
 		new_h = max_h;
 	}
 
-	_g_object_unref (pixbuf_task->dest);
 	if ((new_w > 1) && (new_h > 1))
-		pixbuf_task->dest = _gdk_pixbuf_scale_simple_safe (pixbuf_task->src, new_w, new_h, GDK_INTERP_BILINEAR);
+		destination = _cairo_image_surface_scale (source, new_w, new_h, SCALE_FILTER_BEST, task);
 	else
-		pixbuf_task->dest = NULL;
+		destination = NULL;
+	destination_image = gth_image_new_for_surface (destination);
+	gth_image_task_set_destination (GTH_IMAGE_TASK (task), destination_image);
+
+	_g_object_unref (destination_image);
+	_g_object_unref (destination);
+	_g_object_unref (source);
+
+	return NULL;
 }
 
 
@@ -165,23 +177,22 @@ ok_clicked_cb (GtkWidget  *widget,
 	g_settings_set_boolean (data->settings, PREF_RESIZE_IMAGES_KEEP_RATIO, resize_data->keep_aspect_ratio);
 	g_settings_set_string (data->settings, PREF_RESIZE_IMAGES_MIME_TYPE, mime_type ? mime_type : "");
 
-	resize_task = gth_pixbuf_task_new (_("Resizing images"),
-					   TRUE,
-					   NULL,
-					   resize_step,
-					   NULL,
-					   resize_data,
-					   g_free);
-	list_task = gth_pixbuf_list_task_new (data->browser,
-					      data->file_list,
-					      GTH_PIXBUF_TASK (resize_task));
-	gth_pixbuf_list_task_set_overwrite_mode (GTH_PIXBUF_LIST_TASK (list_task), GTH_OVERWRITE_ASK);
-	gth_pixbuf_list_task_set_output_mime_type (GTH_PIXBUF_LIST_TASK (list_task), mime_type);
+	resize_task = gth_image_task_new (_("Resizing images"),
+					  NULL,
+					  exec_resize,
+					  NULL,
+					  resize_data,
+					  g_free);
+	list_task = gth_image_list_task_new (data->browser,
+					     data->file_list,
+					     GTH_IMAGE_TASK (resize_task));
+	gth_image_list_task_set_overwrite_mode (GTH_IMAGE_LIST_TASK (list_task), GTH_OVERWRITE_ASK);
+	gth_image_list_task_set_output_mime_type (GTH_IMAGE_LIST_TASK (list_task), mime_type);
 	if (data->use_destination) {
 		GFile *destination;
 
 		destination = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")));
-		gth_pixbuf_list_task_set_destination (GTH_PIXBUF_LIST_TASK (list_task), destination);
+		gth_image_list_task_set_destination (GTH_IMAGE_LIST_TASK (list_task), destination);
 
 		g_object_unref (destination);
 	}
@@ -396,7 +407,7 @@ dlg_resize_images (GthBrowser *browser,
 		gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("height_spinbutton")), data->latest_height_in_pixel);
 	}
 
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	if (savers != NULL) {
 		GtkListStore *list_store;
 		GtkTreeIter   iter;
@@ -418,13 +429,13 @@ dlg_resize_images (GthBrowser *browser,
 
 		for (i = 0; i < savers->len; i++) {
 			GType           saver_type;
-			GthPixbufSaver *saver;
+			GthImageSaver *saver;
 			const char     *mime_type;
 			GdkPixbuf      *pixbuf;
 
 			saver_type = g_array_index (savers, GType, i);
 			saver = g_object_new (saver_type, NULL);
-			mime_type = gth_pixbuf_saver_get_mime_type (saver);
+			mime_type = gth_image_saver_get_mime_type (saver);
 			pixbuf = gth_icon_cache_get_pixbuf (icon_cache, g_content_type_get_icon (mime_type));
 			gtk_list_store_append (list_store, &iter);
 			gtk_list_store_set (list_store, &iter,
diff --git a/extensions/webalbums/gth-web-exporter.c b/extensions/webalbums/gth-web-exporter.c
index 586ff38..4a9a0e8 100644
--- a/extensions/webalbums/gth-web-exporter.c
+++ b/extensions/webalbums/gth-web-exporter.c
@@ -70,16 +70,17 @@ extern GFileInputStream *yy_istream;
 typedef struct {
 	GthFileData *file_data;
 	char        *dest_filename;
-	GdkPixbuf   *image;
+	GthImage    *image;
 	int          image_width, image_height;
-	GdkPixbuf   *thumb;
+	GthImage    *thumb;
 	int          thumb_width, thumb_height;
-	GdkPixbuf   *preview;
+	GthImage    *preview;
 	int          preview_width, preview_height;
 	gboolean     caption_set;
 	gboolean     no_preview;
 } ImageData;
 
+
 #define IMAGE_DATA(x) ((ImageData*)(x))
 
 
@@ -2107,9 +2108,9 @@ save_thumbnail (gpointer data)
 
 		destination = get_thumbnail_file (self, image_data, self->priv->tmp_dir);
 		file_data = gth_file_data_new (destination, NULL);
-		_gdk_pixbuf_save_async (image_data->thumb,
-					file_data,
+		gth_image_save_to_file (image_data->thumb,
 					"image/jpeg",
+					file_data,
 					TRUE,
 					save_thumbnail_ready_cb,
 					self);
@@ -2340,9 +2341,9 @@ save_image_preview (gpointer data)
 
 		destination = get_preview_file (self, image_data, self->priv->tmp_dir);
 		file_data = gth_file_data_new (destination, NULL);
-		_gdk_pixbuf_save_async (image_data->preview,
-					file_data,
+		gth_image_save_to_file (image_data->preview,
 					"image/jpeg",
+					file_data,
 					TRUE,
 					save_image_preview_ready_cb,
 					self);
@@ -2447,9 +2448,9 @@ save_resized_image (gpointer data)
 
 		destination = get_image_file (self, image_data, self->priv->tmp_dir);
 		file_data = gth_file_data_new (destination, NULL);
-		_gdk_pixbuf_save_async (image_data->image,
-					file_data,
+		gth_image_save_to_file (image_data->image,
 					"image/jpeg",
+					file_data,
 					TRUE,
 					save_resized_image_ready_cd,
 					self);
@@ -2555,10 +2556,10 @@ image_loader_ready_cb (GObject      *source_object,
                        GAsyncResult *result,
                        gpointer      user_data)
 {
-	GthWebExporter *self = user_data;
-	ImageData      *idata;
-	GthImage       *image = NULL;
-	GdkPixbuf      *pixbuf;
+	GthWebExporter  *self = user_data;
+	ImageData       *idata;
+	GthImage        *image = NULL;
+	cairo_surface_t *surface;
 
 	if (! gth_image_loader_load_finish (GTH_IMAGE_LOADER (source_object),
 					    result,
@@ -2572,33 +2573,45 @@ image_loader_ready_cb (GObject      *source_object,
 	}
 
 	idata = (ImageData *) self->priv->current_file->data;
-	pixbuf = gth_image_get_pixbuf (image);
+	surface = gth_image_get_cairo_surface (image);
 
 	/* image */
 
-	idata->image = g_object_ref (pixbuf);
+	idata->image = g_object_ref (image);
+	idata->image_width = cairo_image_surface_get_width (surface);
+	idata->image_height = cairo_image_surface_get_width (surface);
+
 	if (self->priv->copy_images && self->priv->resize_images) {
-		int w = gdk_pixbuf_get_width (pixbuf);
-		int h = gdk_pixbuf_get_height (pixbuf);
+		int w = cairo_image_surface_get_width (surface);
+		int h = cairo_image_surface_get_height (surface);
+
 		if (scale_keeping_ratio (&w, &h,
 				         self->priv->resize_max_width,
 				         self->priv->resize_max_height,
 				         FALSE))
 		{
-			g_object_unref (idata->image);
-			idata->image = _gdk_pixbuf_scale_composite (pixbuf, w, h, GDK_INTERP_BILINEAR);
+			cairo_surface_t *scaled;
+
+			g_object_ref (idata->image);
+
+			scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
+			idata->image = gth_image_new_for_surface (scaled);
+			idata->image_width = cairo_image_surface_get_width (scaled);
+			idata->image_height = cairo_image_surface_get_width (scaled);
+
+			cairo_surface_destroy (scaled);
 		}
 	}
 
-	idata->image_width = gdk_pixbuf_get_width (idata->image);
-	idata->image_height = gdk_pixbuf_get_height (idata->image);
-
 	/* preview */
 
-	idata->preview = g_object_ref (pixbuf);
+	idata->preview = g_object_ref (image);
+	idata->preview_width = cairo_image_surface_get_width (surface);
+	idata->preview_height = cairo_image_surface_get_width (surface);
+
 	if ((self->priv->preview_max_width > 0) && (self->priv->preview_max_height > 0)) {
-		int w = gdk_pixbuf_get_width (pixbuf);
-		int h = gdk_pixbuf_get_height (pixbuf);
+		int w = cairo_image_surface_get_width (surface);
+		int h = cairo_image_surface_get_height (surface);
 
 		if (scale_keeping_ratio_min (&w, &h,
 					     self->priv->preview_min_width,
@@ -2607,14 +2620,19 @@ image_loader_ready_cb (GObject      *source_object,
 					     self->priv->preview_max_height,
 					     FALSE))
 		{
-			g_object_unref (idata->preview);
-			idata->preview = _gdk_pixbuf_scale_composite (pixbuf, w, h, GDK_INTERP_BILINEAR);
+			cairo_surface_t *scaled;
+
+			g_object_ref (idata->preview);
+
+			scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
+			idata->preview = gth_image_new_for_surface (scaled);
+			idata->preview_width = cairo_image_surface_get_width (scaled);
+			idata->preview_height = cairo_image_surface_get_width (scaled);
+
+			cairo_surface_destroy (scaled);
 		}
 	}
 
-	idata->preview_width = gdk_pixbuf_get_width (idata->preview);
-	idata->preview_height = gdk_pixbuf_get_height (idata->preview);
-
 	idata->no_preview = ((idata->preview_width == idata->image_width)
 			     && (idata->preview_height == idata->image_height));
 
@@ -2625,28 +2643,44 @@ image_loader_ready_cb (GObject      *source_object,
 
 	/* thumbnail. */
 
-	idata->thumb = g_object_ref (pixbuf);
+	idata->thumb = g_object_ref (image);
+	idata->thumb_width = cairo_image_surface_get_width (surface);
+	idata->thumb_height = cairo_image_surface_get_width (surface);
+
 	if ((self->priv->thumb_width > 0) && (self->priv->thumb_height > 0)) {
-		int w = gdk_pixbuf_get_width (pixbuf);
-		int h = gdk_pixbuf_get_height (pixbuf);
+		int w = cairo_image_surface_get_width (surface);
+		int h = cairo_image_surface_get_height (surface);
 
 		if (self->priv->squared_thumbnails) {
+			cairo_surface_t *scaled;
+
 			g_object_unref (idata->thumb);
-			idata->thumb = _gdk_pixbuf_scale_squared (idata->thumb, self->priv->thumb_width, GDK_INTERP_BILINEAR);
+
+			scaled = _cairo_image_surface_scale_squared (surface, self->priv->thumb_width, SCALE_FILTER_BEST, NULL);
+			idata->thumb = gth_image_new_for_surface (scaled);
+			idata->thumb_width = cairo_image_surface_get_width (scaled);
+			idata->thumb_height = cairo_image_surface_get_width (scaled);
+
+			cairo_surface_destroy (scaled);
 		}
 		else if (scale_keeping_ratio (&w, &h,
 					      self->priv->thumb_width,
 					      self->priv->thumb_height,
 					      FALSE))
 		{
+			cairo_surface_t *scaled;
+
 			g_object_unref (idata->thumb);
-			idata->thumb = _gdk_pixbuf_scale_composite (pixbuf, w, h, GDK_INTERP_BILINEAR);
+
+			scaled = _cairo_image_surface_scale (surface, w, h, SCALE_FILTER_BEST, NULL);
+			idata->thumb = gth_image_new_for_surface (scaled);
+			idata->thumb_width = cairo_image_surface_get_width (scaled);
+			idata->thumb_height = cairo_image_surface_get_width (scaled);
+
+			cairo_surface_destroy (scaled);
 		}
 	}
 
-	idata->thumb_width = gdk_pixbuf_get_width (idata->thumb);
-	idata->thumb_height = gdk_pixbuf_get_height (idata->thumb);
-
 	/* save the image */
 
 	if (self->priv->copy_images) {
@@ -2658,7 +2692,7 @@ image_loader_ready_cb (GObject      *source_object,
 	else
 		self->priv->saving_timeout = g_idle_add (save_image_preview, self);
 
-	g_object_unref (pixbuf);
+	cairo_surface_destroy (surface);
 	g_object_unref (image);
 }
 
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 213ca02..f46bc30 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -67,10 +67,13 @@ PUBLIC_HEADER_FILES = 					\
 	gth-image.h					\
 	gth-image-dragger.h				\
 	gth-image-history.h				\
+	gth-image-list-task.h				\
 	gth-image-loader.h				\
 	gth-image-navigator.h				\
 	gth-image-preloader.h				\
+	gth-image-saver.h				\
 	gth-image-selector.h				\
+	gth-image-task.h				\
 	gth-image-viewer.h				\
 	gth-image-viewer-tool.h				\
 	gth-info-bar.h					\
@@ -85,9 +88,6 @@ PUBLIC_HEADER_FILES = 					\
 	gth-monitor.h					\
 	gth-multipage.h					\
 	gth-overwrite-dialog.h				\
-	gth-pixbuf-task.h				\
-	gth-pixbuf-list-task.h				\
-	gth-pixbuf-saver.h				\
 	gth-preferences.h				\
 	gth-progress-dialog.h				\
 	gth-request-dialog.h				\
@@ -192,10 +192,13 @@ gthumb_SOURCES = 					\
 	gth-image.c					\
 	gth-image-dragger.c				\
 	gth-image-history.c				\
+	gth-image-list-task.c				\
 	gth-image-loader.c				\
 	gth-image-navigator.c				\
 	gth-image-preloader.c				\
+	gth-image-saver.c				\
 	gth-image-selector.c				\
+	gth-image-task.c				\
 	gth-image-viewer.c				\
 	gth-image-viewer-tool.c				\
 	gth-info-bar.c					\
@@ -216,9 +219,6 @@ gthumb_SOURCES = 					\
 	gth-monitor.c					\
 	gth-multipage.c					\
 	gth-overwrite-dialog.c				\
-	gth-pixbuf-task.c				\
-	gth-pixbuf-list-task.c				\
-	gth-pixbuf-saver.c				\
 	gth-preferences.c				\
 	gth-progress-dialog.c				\
 	gth-request-dialog.c				\
diff --git a/gthumb/cairo-scale.c b/gthumb/cairo-scale.c
index 3ca59f0..1248fed 100644
--- a/gthumb/cairo-scale.c
+++ b/gthumb/cairo-scale.c
@@ -443,3 +443,48 @@ _cairo_image_surface_scale (cairo_surface_t  *image,
 						  filter,
 						  task);
 }
+
+
+cairo_surface_t *
+_cairo_image_surface_scale_squared (cairo_surface_t *image,
+				    int              size,
+				    scale_filter_t   quality,
+				    GthAsyncTask    *task)
+{
+	int              width, height;
+	int              scaled_width, scaled_height;
+	cairo_surface_t *scaled;
+	cairo_surface_t *squared;
+
+	width = cairo_image_surface_get_width (image);
+	height = cairo_image_surface_get_height (image);
+
+	if ((width < size) && (height < size))
+		return _cairo_image_surface_copy (image);
+
+	if (width > height) {
+		scaled_height = size;
+		scaled_width = (int) (((double) width / height) * scaled_height);
+	}
+	else {
+		scaled_width = size;
+		scaled_height = (int) (((double) height / width) * scaled_width);
+	}
+
+	if ((scaled_width != width) || (scaled_height != height))
+		scaled = _cairo_image_surface_scale (image, scaled_width, scaled_height, quality, task);
+	else
+		scaled = cairo_surface_reference (image);
+
+	if ((scaled_width == size) && (scaled_height == size))
+		return scaled;
+
+	squared = _cairo_image_surface_copy_subsurface (scaled,
+							(scaled_width - size) / 2,
+							(scaled_height - size) / 2,
+							size,
+							size);
+	cairo_surface_destroy (scaled);
+
+	return squared;
+}
diff --git a/gthumb/cairo-scale.h b/gthumb/cairo-scale.h
index e8fa062..90c9e2a 100644
--- a/gthumb/cairo-scale.h
+++ b/gthumb/cairo-scale.h
@@ -37,15 +37,19 @@ typedef enum /*< skip >*/ {
 
 	SCALE_FILTER_FAST = SCALE_FILTER_POINT,
 	SCALE_FILTER_GOOD = SCALE_FILTER_BOX,
-	SCALE_FILTER_BEST = SCALE_FILTER_LANCZOS
+	SCALE_FILTER_BEST = SCALE_FILTER_TRIANGLE
 } scale_filter_t;
 
 
-cairo_surface_t *  _cairo_image_surface_scale  (cairo_surface_t  *image,
-						int               width,
-						int               height,
-						scale_filter_t    quality,
-						GthAsyncTask     *task);
+cairo_surface_t *  _cairo_image_surface_scale		(cairo_surface_t *image,
+							 int              width,
+							 int              height,
+							 scale_filter_t   quality,
+							 GthAsyncTask    *task);
+cairo_surface_t *  _cairo_image_surface_scale_squared   (cairo_surface_t *image,
+							 int              size,
+							 scale_filter_t   quality,
+							 GthAsyncTask    *task);
 
 G_END_DECLS
 
diff --git a/gthumb/cairo-utils.c b/gthumb/cairo-utils.c
index 9aa7360..9023e43 100644
--- a/gthumb/cairo-utils.c
+++ b/gthumb/cairo-utils.c
@@ -507,6 +507,26 @@ _cairo_image_surface_transform (cairo_surface_t *source,
 
 
 void
+_cairo_copy_line_as_rgb (guchar *dest,
+			 guchar *src,
+			 guint   width,
+			 guint   alpha)
+{
+	guint x;
+
+	for (x = 0; x < width; x++) {
+		*(dest++) = src[CAIRO_RED];
+		*(dest++) = src[CAIRO_GREEN];
+		*(dest++) = src[CAIRO_BLUE];
+		if (alpha)
+			*(dest++) = src[CAIRO_ALPHA];
+
+		src += 4;
+	}
+}
+
+
+void
 _cairo_paint_full_gradient (cairo_surface_t *surface,
 			    GdkColor        *h_color1,
 			    GdkColor        *h_color2,
diff --git a/gthumb/cairo-utils.h b/gthumb/cairo-utils.h
index 0acbc1f..8f4be69 100644
--- a/gthumb/cairo-utils.h
+++ b/gthumb/cairo-utils.h
@@ -171,6 +171,10 @@ 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,
+							     guchar                *src,
+							     guint                  width,
+							     guint                  alpha);
 
 /* paint / draw */
 
diff --git a/gthumb/gth-file-chooser-dialog.c b/gthumb/gth-file-chooser-dialog.c
index cee80bc..59ad733 100644
--- a/gthumb/gth-file-chooser-dialog.c
+++ b/gthumb/gth-file-chooser-dialog.c
@@ -161,16 +161,16 @@ gth_file_chooser_dialog_construct (GthFileChooserDialog *self,
 
 	/**/
 
-	savers = gth_main_get_type_set (allowed_savers /*"pixbuf-saver"*/);
+	savers = gth_main_get_type_set (allowed_savers /*"image-saver"*/);
 	for (i = 0; (savers != NULL) && (i < savers->len); i++) {
-		GthPixbufSaver *saver;
+		GthImageSaver *saver;
 		Format         *format;
 
 		saver = g_object_new (g_array_index (savers, GType, i), NULL);
 		format = g_new (Format, 1);
-		format->type = get_static_string (gth_pixbuf_saver_get_mime_type (saver));
-		format->extensions = get_static_string (gth_pixbuf_saver_get_extensions (saver));
-		format->default_ext = get_static_string (gth_pixbuf_saver_get_default_ext (saver));
+		format->type = get_static_string (gth_image_saver_get_mime_type (saver));
+		format->extensions = get_static_string (gth_image_saver_get_extensions (saver));
+		format->default_ext = get_static_string (gth_image_saver_get_default_ext (saver));
 		self->priv->supported_formats = g_list_prepend (self->priv->supported_formats, format);
 
 		g_object_unref (saver);
diff --git a/gthumb/gth-pixbuf-list-task.c b/gthumb/gth-image-list-task.c
similarity index 70%
rename from gthumb/gth-pixbuf-list-task.c
rename to gthumb/gth-image-list-task.c
index 59dead2..ffad44f 100644
--- a/gthumb/gth-pixbuf-list-task.c
+++ b/gthumb/gth-image-list-task.c
@@ -24,12 +24,14 @@
 #include "glib-utils.h"
 #include "gth-main.h"
 #include "gth-overwrite-dialog.h"
-#include "gth-pixbuf-list-task.h"
+#include "gth-image.h"
+#include "gth-image-list-task.h"
+#include "gth-image-loader.h"
+#include "gth-image-saver.h"
 #include "gtk-utils.h"
-#include "pixbuf-io.h"
 
 
-struct _GthPixbufListTaskPrivate {
+struct _GthImageListTaskPrivate {
 	GthBrowser           *browser;
 	GList                *file_list;
 	GthTask              *task;
@@ -39,8 +41,8 @@ struct _GthPixbufListTaskPrivate {
 	GList                *current;
 	int                   n_current;
 	int                   n_files;
-	GdkPixbuf            *original_pixbuf;
-	GdkPixbuf            *new_pixbuf;
+	GthImage             *original_image;
+	GthImage             *new_image;
 	GFile                *destination_folder;
 	GthFileData          *destination_file_data;
 	GthOverwriteMode      overwrite_mode;
@@ -49,20 +51,20 @@ struct _GthPixbufListTaskPrivate {
 };
 
 
-G_DEFINE_TYPE (GthPixbufListTask, gth_pixbuf_list_task, GTH_TYPE_TASK)
+G_DEFINE_TYPE (GthImageListTask, gth_image_list_task, GTH_TYPE_TASK)
 
 
 static void
-gth_pixbuf_list_task_finalize (GObject *object)
+gth_image_list_task_finalize (GObject *object)
 {
-	GthPixbufListTask *self;
+	GthImageListTask *self;
 
-	self = GTH_PIXBUF_LIST_TASK (object);
+	self = GTH_IMAGE_LIST_TASK (object);
 
 	g_free (self->priv->mime_type);
 	_g_object_unref (self->priv->destination_folder);
-	_g_object_unref (self->priv->original_pixbuf);
-	_g_object_unref (self->priv->new_pixbuf);
+	_g_object_unref (self->priv->original_image);
+	_g_object_unref (self->priv->new_image);
 	g_signal_handler_disconnect (self->priv->task, self->priv->task_completed);
 	g_signal_handler_disconnect (self->priv->task, self->priv->task_progress);
 	g_signal_handler_disconnect (self->priv->task, self->priv->task_dialog);
@@ -70,15 +72,15 @@ gth_pixbuf_list_task_finalize (GObject *object)
 	_g_object_list_unref (self->priv->file_list);
 	_g_object_unref (self->priv->destination_file_data);
 
-	G_OBJECT_CLASS (gth_pixbuf_list_task_parent_class)->finalize (object);
+	G_OBJECT_CLASS (gth_image_list_task_parent_class)->finalize (object);
 }
 
 
-static void process_current_file (GthPixbufListTask *self);
+static void process_current_file (GthImageListTask *self);
 
 
 static void
-process_next_file (GthPixbufListTask *self)
+process_next_file (GthImageListTask *self)
 {
 	self->priv->n_current++;
 	self->priv->current = self->priv->current->next;
@@ -86,9 +88,9 @@ process_next_file (GthPixbufListTask *self)
 }
 
 
-static void pixbuf_task_save_current_pixbuf (GthPixbufListTask *self,
-					     GFile             *file,
-					     gboolean           replace);
+static void image_task_save_current_image (GthImageListTask *self,
+					   GFile            *file,
+					   gboolean          replace);
 
 
 static void
@@ -96,8 +98,8 @@ overwrite_dialog_response_cb (GtkDialog *dialog,
                               gint       response_id,
                               gpointer   user_data)
 {
-	GthPixbufListTask *self = user_data;
-	gboolean           close_overwrite_dialog = TRUE;
+	GthImageListTask *self = user_data;
+	gboolean          close_overwrite_dialog = TRUE;
 
 	if (response_id != GTK_RESPONSE_OK)
 		self->priv->overwrite_response = GTH_OVERWRITE_RESPONSE_CANCEL;
@@ -120,7 +122,7 @@ overwrite_dialog_response_cb (GtkDialog *dialog,
 	case GTH_OVERWRITE_RESPONSE_ALWAYS_YES:
 		if (self->priv->overwrite_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
 			self->priv->overwrite_mode = GTH_OVERWRITE_OVERWRITE;
-		pixbuf_task_save_current_pixbuf (self, NULL, TRUE);
+		image_task_save_current_image (self, NULL, TRUE);
 		break;
 
 	case GTH_OVERWRITE_RESPONSE_RENAME:
@@ -143,7 +145,7 @@ overwrite_dialog_response_cb (GtkDialog *dialog,
 				close_overwrite_dialog = FALSE;
 			}
 			else
-				pixbuf_task_save_current_pixbuf (self, new_destination, FALSE);
+				image_task_save_current_image (self, new_destination, FALSE);
 
 			g_object_unref (new_destination);
 			g_object_unref (parent);
@@ -166,11 +168,11 @@ overwrite_dialog_response_cb (GtkDialog *dialog,
 
 
 static void
-pixbuf_saved_cb (GthFileData *file_data,
-		 GError      *error,
-		 gpointer     user_data)
+image_saved_cb (GthFileData *file_data,
+		GError      *error,
+		gpointer     user_data)
 {
-	GthPixbufListTask *self = user_data;
+	GthImageListTask *self = user_data;
 	GFile             *parent;
 	GList             *file_list;
 
@@ -183,7 +185,7 @@ pixbuf_saved_cb (GthFileData *file_data,
 				GtkWidget *dialog;
 
 				dialog = gth_overwrite_dialog_new (NULL,
-								   self->priv->new_pixbuf,
+								   self->priv->new_image,
 								   self->priv->destination_file_data->file,
 								   GTH_OVERWRITE_RESPONSE_YES,
 								   (self->priv->n_files == 1));
@@ -216,24 +218,24 @@ pixbuf_saved_cb (GthFileData *file_data,
 
 
 static void
-pixbuf_task_dialog_cb (GthTask   *task,
-		       gboolean   opened,
-		       GtkWidget *dialog,
-		       gpointer   user_data)
+image_task_dialog_cb (GthTask   *task,
+		      gboolean   opened,
+		      GtkWidget *dialog,
+		      gpointer   user_data)
 {
 	gth_task_dialog (GTH_TASK (user_data), opened, dialog);
 }
 
 
 static void
-pixbuf_task_progress_cb (GthTask    *task,
-		         const char *description,
-		         const char *details,
-		         gboolean    pulse,
-		         double      fraction,
-		         gpointer    user_data)
+image_task_progress_cb (GthTask    *task,
+		        const char *description,
+		        const char *details,
+		        gboolean    pulse,
+		        double      fraction,
+		        gpointer    user_data)
 {
-	GthPixbufListTask *self = user_data;
+	GthImageListTask *self = user_data;
 	double             total_fraction;
 	double             file_fraction;
 
@@ -249,7 +251,7 @@ pixbuf_task_progress_cb (GthTask    *task,
 	}
 
 	gth_task_progress (GTH_TASK (self),
-			   description,
+			   gth_image_task_get_description (GTH_IMAGE_TASK (task)),
 			   details,
 			   FALSE,
 			   total_fraction + (file_fraction / (self->priv->n_files + 1)));
@@ -257,36 +259,39 @@ pixbuf_task_progress_cb (GthTask    *task,
 
 
 static void
-pixbuf_task_save_current_pixbuf (GthPixbufListTask *self,
-				 GFile             *file,
-				 gboolean           replace)
+image_task_save_current_image (GthImageListTask *self,
+			       GFile             *file,
+			       gboolean           replace)
 {
+	GthImage *destination;
+
 	if (file != NULL)
 		gth_file_data_set_file (self->priv->destination_file_data, file);
 
-	if (GTH_PIXBUF_TASK (self->priv->task)->dest == NULL) {
+	destination = gth_image_task_get_destination (GTH_IMAGE_TASK (self->priv->task));
+	if (destination == NULL) {
 		process_next_file (self);
 		return;
 	}
 
-	/* add a reference before unref-ing new_pixbuf because dest and
-	 * new_pixbuf can be the same object. */
+	/* add a reference before unref-ing new_image because dest and
+	 * new_image can be the same object. */
 
-	g_object_ref (GTH_PIXBUF_TASK (self->priv->task)->dest);
-	_g_object_unref (self->priv->new_pixbuf);
-	self->priv->new_pixbuf = GTH_PIXBUF_TASK (self->priv->task)->dest;
+	g_object_ref (destination);
+	_g_object_unref (self->priv->new_image);
+	self->priv->new_image = destination;
 
-	_gdk_pixbuf_save_async (self->priv->new_pixbuf,
-				self->priv->destination_file_data,
+	gth_image_save_to_file (self->priv->new_image,
 				gth_file_data_get_mime_type (self->priv->destination_file_data),
+				self->priv->destination_file_data,
 				replace,
-				pixbuf_saved_cb,
+				image_saved_cb,
 				self);
 }
 
 
 static void
-set_current_destination_file (GthPixbufListTask *self)
+set_current_destination_file (GthImageListTask *self)
 {
 	char  *display_name;
 	GFile *parent;
@@ -296,14 +301,14 @@ set_current_destination_file (GthPixbufListTask *self)
 	self->priv->destination_file_data = g_object_ref (self->priv->current->data);
 
 	if (self->priv->mime_type != NULL) {
-		char           *no_ext;
-		GthPixbufSaver *saver;
+		char          *no_ext;
+		GthImageSaver *saver;
 
 		no_ext = _g_uri_remove_extension (g_file_info_get_display_name (self->priv->destination_file_data->info));
-		saver = gth_main_get_pixbuf_saver (self->priv->mime_type);
+		saver = gth_main_get_image_saver (self->priv->mime_type);
 		g_return_if_fail (saver != NULL);
 
-		display_name = g_strconcat (no_ext, ".", gth_pixbuf_saver_get_default_ext (saver), NULL);
+		display_name = g_strconcat (no_ext, ".", gth_image_saver_get_default_ext (saver), NULL);
 		gth_file_data_set_mime_type (self->priv->destination_file_data, self->priv->mime_type);
 
 		g_object_unref (saver);
@@ -327,11 +332,11 @@ set_current_destination_file (GthPixbufListTask *self)
 
 
 static void
-pixbuf_task_completed_cb (GthTask  *task,
-			  GError   *error,
-			  gpointer  user_data)
+image_task_completed_cb (GthTask  *task,
+			 GError   *error,
+			 gpointer  user_data)
 {
-	GthPixbufListTask *self = user_data;
+	GthImageListTask *self = user_data;
 
 	if (g_error_matches (error, GTH_TASK_ERROR, GTH_TASK_ERROR_SKIP_TO_NEXT_FILE)) {
 		process_next_file (self);
@@ -344,7 +349,7 @@ pixbuf_task_completed_cb (GthTask  *task,
 	}
 
 	set_current_destination_file (self);
-	pixbuf_task_save_current_pixbuf (self, NULL, (self->priv->overwrite_mode == GTH_OVERWRITE_OVERWRITE));
+	image_task_save_current_image (self, NULL, (self->priv->overwrite_mode == GTH_OVERWRITE_OVERWRITE));
 }
 
 
@@ -354,9 +359,8 @@ file_buffer_ready_cb (void     **buffer,
 		      GError    *error,
 		      gpointer   user_data)
 {
-	GthPixbufListTask *self = user_data;
-	GInputStream      *istream;
-	GdkPixbuf         *pixbuf;
+	GthImageListTask *self = user_data;
+	GInputStream     *istream;
 
 	if (error != NULL) {
 		gth_task_completed (GTH_TASK (self), error);
@@ -364,22 +368,16 @@ file_buffer_ready_cb (void     **buffer,
 	}
 
 	istream = g_memory_input_stream_new_from_data (*buffer, count, NULL);
-	pixbuf = gdk_pixbuf_new_from_stream (istream, gth_task_get_cancellable (GTH_TASK (self)), &error);
-	if (pixbuf != NULL) {
-		self->priv->original_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
-		g_object_unref (pixbuf);
-	}
-	else
-		self->priv->original_pixbuf = NULL;
+	self->priv->original_image = gth_image_new_from_stream (istream, -1, NULL, NULL, gth_task_get_cancellable (GTH_TASK (self)), &error);
 
 	g_object_unref (istream);
 
-	if (self->priv->original_pixbuf == NULL) {
+	if (self->priv->original_image == NULL) {
 		gth_task_completed (GTH_TASK (self), error);
 		return;
 	}
 
-	gth_pixbuf_task_set_source (GTH_PIXBUF_TASK (self->priv->task), self->priv->original_pixbuf);
+	gth_image_task_set_source (GTH_IMAGE_TASK (self->priv->task), self->priv->original_image);
 	gth_task_exec (self->priv->task, gth_task_get_cancellable (GTH_TASK (self)));
 }
 
@@ -389,7 +387,7 @@ file_info_ready_cb (GList    *files,
 		    GError   *error,
 		    gpointer  user_data)
 {
-	GthPixbufListTask *self = user_data;
+	GthImageListTask *self = user_data;
 	GthFileData       *updated_file_data;
 	GthFileData       *source_file_data;
 
@@ -411,7 +409,7 @@ file_info_ready_cb (GList    *files,
 
 
 static void
-process_current_file (GthPixbufListTask *self)
+process_current_file (GthImageListTask *self)
 {
 	GthFileData *source_file_data;
 	GList       *source_singleton;
@@ -423,11 +421,11 @@ process_current_file (GthPixbufListTask *self)
 		return;
 	}
 
-	_g_object_unref (self->priv->original_pixbuf);
-	self->priv->original_pixbuf = NULL;
+	_g_object_unref (self->priv->original_image);
+	self->priv->original_image = NULL;
 
-	_g_object_unref (self->priv->new_pixbuf);
-	self->priv->new_pixbuf = NULL;
+	_g_object_unref (self->priv->new_image);
+	self->priv->new_image = NULL;
 
 	gth_task_progress (GTH_TASK (self),
 			   NULL,
@@ -449,13 +447,13 @@ process_current_file (GthPixbufListTask *self)
 
 
 static void
-gth_pixbuf_list_task_exec (GthTask *task)
+gth_image_list_task_exec (GthTask *task)
 {
-	GthPixbufListTask *self;
+	GthImageListTask *self;
 
-	g_return_if_fail (GTH_IS_PIXBUF_LIST_TASK (task));
+	g_return_if_fail (GTH_IS_IMAGE_LIST_TASK (task));
 
-	self = GTH_PIXBUF_LIST_TASK (task);
+	self = GTH_IMAGE_LIST_TASK (task);
 
 	self->priv->current = self->priv->file_list;
 	self->priv->n_current = 0;
@@ -465,27 +463,27 @@ gth_pixbuf_list_task_exec (GthTask *task)
 
 
 static void
-gth_pixbuf_list_task_class_init (GthPixbufListTaskClass *klass)
+gth_image_list_task_class_init (GthImageListTaskClass *klass)
 {
 	GObjectClass *object_class;
 	GthTaskClass *task_class;
 
-	g_type_class_add_private (klass, sizeof (GthPixbufListTaskPrivate));
+	g_type_class_add_private (klass, sizeof (GthImageListTaskPrivate));
 
 	object_class = G_OBJECT_CLASS (klass);
-	object_class->finalize = gth_pixbuf_list_task_finalize;
+	object_class->finalize = gth_image_list_task_finalize;
 
 	task_class = GTH_TASK_CLASS (klass);
-	task_class->exec = gth_pixbuf_list_task_exec;
+	task_class->exec = gth_image_list_task_exec;
 }
 
 
 static void
-gth_pixbuf_list_task_init (GthPixbufListTask *self)
+gth_image_list_task_init (GthImageListTask *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_PIXBUF_LIST_TASK, GthPixbufListTaskPrivate);
-	self->priv->original_pixbuf = NULL;
-	self->priv->new_pixbuf = NULL;
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_LIST_TASK, GthImageListTaskPrivate);
+	self->priv->original_image = NULL;
+	self->priv->new_image = NULL;
 	self->priv->destination_folder = NULL;
 	self->priv->overwrite_response = GTH_OVERWRITE_RESPONSE_UNSPECIFIED;
 	self->priv->mime_type = NULL;
@@ -493,30 +491,30 @@ gth_pixbuf_list_task_init (GthPixbufListTask *self)
 
 
 GthTask *
-gth_pixbuf_list_task_new (GthBrowser    *browser,
-			  GList         *file_list,
-			  GthPixbufTask *task)
+gth_image_list_task_new (GthBrowser    *browser,
+			  GList        *file_list,
+			  GthImageTask *task)
 {
-	GthPixbufListTask *self;
+	GthImageListTask *self;
 
 	g_return_val_if_fail (task != NULL, NULL);
-	g_return_val_if_fail (GTH_IS_PIXBUF_TASK (task), NULL);
+	g_return_val_if_fail (GTH_IS_IMAGE_TASK (task), NULL);
 
-	self = GTH_PIXBUF_LIST_TASK (g_object_new (GTH_TYPE_PIXBUF_LIST_TASK, NULL));
+	self = GTH_IMAGE_LIST_TASK (g_object_new (GTH_TYPE_IMAGE_LIST_TASK, NULL));
 	self->priv->browser = browser;
 	self->priv->file_list = _g_object_list_ref (file_list);
 	self->priv->task = g_object_ref (task);
 	self->priv->task_completed = g_signal_connect (self->priv->task,
 						       "completed",
-						       G_CALLBACK (pixbuf_task_completed_cb),
+						       G_CALLBACK (image_task_completed_cb),
 						       self);
 	self->priv->task_progress = g_signal_connect (self->priv->task,
 						      "progress",
-						      G_CALLBACK (pixbuf_task_progress_cb),
+						      G_CALLBACK (image_task_progress_cb),
 						      self);
 	self->priv->task_dialog = g_signal_connect (self->priv->task,
 						    "dialog",
-						    G_CALLBACK (pixbuf_task_dialog_cb),
+						    G_CALLBACK (image_task_dialog_cb),
 						    self);
 
 	return (GthTask *) self;
@@ -524,7 +522,7 @@ gth_pixbuf_list_task_new (GthBrowser    *browser,
 
 
 void
-gth_pixbuf_list_task_set_destination (GthPixbufListTask *self,
+gth_image_list_task_set_destination (GthImageListTask *self,
 				      GFile             *folder)
 {
 	_g_object_unref (self->priv->destination_folder);
@@ -533,7 +531,7 @@ gth_pixbuf_list_task_set_destination (GthPixbufListTask *self,
 
 
 void
-gth_pixbuf_list_task_set_overwrite_mode (GthPixbufListTask    *self,
+gth_image_list_task_set_overwrite_mode (GthImageListTask    *self,
 					 GthOverwriteMode      overwrite_mode)
 {
 	self->priv->overwrite_mode = overwrite_mode;
@@ -541,7 +539,7 @@ gth_pixbuf_list_task_set_overwrite_mode (GthPixbufListTask    *self,
 
 
 void
-gth_pixbuf_list_task_set_output_mime_type (GthPixbufListTask *self,
+gth_image_list_task_set_output_mime_type (GthImageListTask *self,
 					   const char        *mime_type)
 {
 	g_free (self->priv->mime_type);
diff --git a/gthumb/gth-image-list-task.h b/gthumb/gth-image-list-task.h
new file mode 100644
index 0000000..edda7f1
--- /dev/null
+++ b/gthumb/gth-image-list-task.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_IMAGE_LIST_TASK_H
+#define GTH_IMAGE_LIST_TASK_H
+
+#include <glib.h>
+#include "gth-browser.h"
+#include "gth-image-task.h"
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_IMAGE_LIST_TASK            (gth_image_list_task_get_type ())
+#define GTH_IMAGE_LIST_TASK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE_LIST_TASK, GthImageListTask))
+#define GTH_IMAGE_LIST_TASK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE_LIST_TASK, GthImageListTaskClass))
+#define GTH_IS_IMAGE_LIST_TASK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_IMAGE_LIST_TASK))
+#define GTH_IS_IMAGE_LIST_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_IMAGE_LIST_TASK))
+#define GTH_IMAGE_LIST_TASK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_IMAGE_LIST_TASK, GthImageListTaskClass))
+
+typedef struct _GthImageListTask        GthImageListTask;
+typedef struct _GthImageListTaskClass   GthImageListTaskClass;
+typedef struct _GthImageListTaskPrivate GthImageListTaskPrivate;
+
+struct _GthImageListTask {
+	GthTask __parent;
+	GthImageListTaskPrivate *priv;
+};
+
+struct _GthImageListTaskClass {
+	GthTaskClass __parent;
+};
+
+GType      gth_image_list_task_get_type             (void);
+GthTask *  gth_image_list_task_new                  (GthBrowser          *browser,
+						     GList               *file_list, /* GthFileData list */
+						     GthImageTask        *task);
+void       gth_image_list_task_set_destination      (GthImageListTask    *self,
+						     GFile               *folder);
+void       gth_image_list_task_set_overwrite_mode   (GthImageListTask    *self,
+						     GthOverwriteMode     overwrite_mode);
+void       gth_image_list_task_set_output_mime_type (GthImageListTask    *self,
+						     const char          *mime_type);
+
+G_END_DECLS
+
+#endif /* GTH_IMAGE_LIST_TASK_H */
diff --git a/gthumb/gth-image-loader.c b/gthumb/gth-image-loader.c
index 63cdebc..6318ed9 100644
--- a/gthumb/gth-image-loader.c
+++ b/gthumb/gth-image-loader.c
@@ -68,7 +68,7 @@ gth_image_loader_init (GthImageLoader *self)
 	self->priv->as_animation = FALSE;
 	self->priv->loader_func = NULL;
 	self->priv->loader_data = NULL;
-	self->priv->preferred_format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
+	self->priv->preferred_format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
 }
 
 
@@ -239,72 +239,6 @@ gth_image_loader_load (GthImageLoader      *loader,
 
 
 gboolean
-gth_image_loader_load_stream_sync (GthImageLoader  *self,
-				   GInputStream    *istream,
-				   int              requested_size,
-				   GthImage       **p_image,
-				   int             *p_original_width,
-				   int             *p_original_height,
-				   GCancellable    *cancellable,
-				   GError         **p_error)
-{
-	GthImage *image;
-	int       original_width;
-	int       original_height;
-	GError   *error = NULL;
-	gboolean  result;
-
-	if (self->priv->loader_func != NULL) {
-		image = (*self->priv->loader_func) (istream,
-						    NULL,
-						    requested_size,
-						    &original_width,
-						    &original_height,
-						    self->priv->loader_data,
-						    cancellable,
-						    &error);
-	}
-	else {
-		const char         *mime_type;
-		GthImageLoaderFunc  loader_func;
-
-		mime_type = _g_content_type_get_from_stream (istream, cancellable, &error);
-		loader_func = gth_main_get_image_loader_func (mime_type, self->priv->preferred_format);
-		if (loader_func != NULL)
-			image = loader_func (istream,
-					     NULL,
-				             requested_size,
-				             &original_width,
-				             &original_height,
-				             NULL,
-				             cancellable,
-				             &error);
-		else
-			error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("No suitable loader available for this file type"));
-	}
-
-	result = (error == NULL);
-
-	if (p_error != NULL)
-		*p_error = error;
-	else
-		_g_error_free (error);
-
-	if (p_image != NULL)
-		*p_image = image;
-	else
-		g_object_unref (image);
-
-	if (p_original_width != NULL)
-		*p_original_width = original_width;
-	if (p_original_height != NULL)
-		*p_original_height = original_height;
-
-	return result;
-}
-
-
-gboolean
 gth_image_loader_load_finish (GthImageLoader   *loader,
 			      GAsyncResult     *result,
 			      GthImage        **image,
@@ -332,3 +266,49 @@ gth_image_loader_load_finish (GthImageLoader   *loader,
 
 	  return TRUE;
 }
+
+
+GthImage *
+gth_image_new_from_stream (GInputStream  *istream,
+			   int            requested_size,
+			   int           *p_original_width,
+			   int           *p_original_height,
+			   GCancellable  *cancellable,
+			   GError       **p_error)
+{
+	const char         *mime_type;
+	GthImageLoaderFunc  loader_func;
+	GthImage           *image;
+	int                 original_width;
+	int                 original_height;
+	GError             *error = NULL;
+
+	mime_type = _g_content_type_get_from_stream (istream, cancellable, &error);
+	if (mime_type != NULL) {
+		loader_func = gth_main_get_image_loader_func (mime_type, GTH_IMAGE_FORMAT_CAIRO_SURFACE);
+		if (loader_func != NULL)
+			image = loader_func (istream,
+					     NULL,
+					     requested_size,
+					     &original_width,
+					     &original_height,
+					     NULL,
+					     cancellable,
+					     &error);
+	}
+
+	if ((image == NULL) && (error == NULL))
+		error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("No suitable loader available for this file type"));
+
+	if (p_error != NULL)
+		*p_error = error;
+	else
+		_g_error_free (error);
+
+	if (p_original_width != NULL)
+		*p_original_width = original_width;
+	if (p_original_height != NULL)
+		*p_original_height = original_height;
+
+	return image;
+}
diff --git a/gthumb/gth-image-loader.h b/gthumb/gth-image-loader.h
index ecf0494..e1847d7 100644
--- a/gthumb/gth-image-loader.h
+++ b/gthumb/gth-image-loader.h
@@ -64,19 +64,17 @@ void              gth_image_loader_load                   (GthImageLoader
 							   GCancellable         *cancellable,
 							   GAsyncReadyCallback   callback,
 							   gpointer              user_data);
-gboolean          gth_image_loader_load_stream_sync       (GthImageLoader       *loader,
-							   GInputStream         *istream,
-							   int                   requested_size,
+gboolean          gth_image_loader_load_finish            (GthImageLoader       *loader,
+							   GAsyncResult         *res,
 							   GthImage            **image,
 							   int                  *original_width,
 							   int                  *original_height,
-							   GCancellable         *cancellable,
 							   GError              **error);
-gboolean          gth_image_loader_load_finish            (GthImageLoader       *loader,
-							   GAsyncResult         *res,
-							   GthImage            **image,
+GthImage *        gth_image_new_from_stream               (GInputStream         *istream,
+							   int                   requested_size,
 							   int                  *original_width,
 							   int                  *original_height,
+							   GCancellable         *cancellable,
 							   GError              **error);
 
 G_END_DECLS
diff --git a/gthumb/gth-image-saver.c b/gthumb/gth-image-saver.c
new file mode 100644
index 0000000..6cb807f
--- /dev/null
+++ b/gthumb/gth-image-saver.c
@@ -0,0 +1,367 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "gio-utils.h"
+#include "glib-utils.h"
+#include "gth-error.h"
+#include "gth-hook.h"
+#include "gth-main.h"
+#include "gth-image-saver.h"
+
+
+G_DEFINE_TYPE (GthImageSaver, gth_image_saver, G_TYPE_OBJECT)
+
+
+static GtkWidget *
+base_get_control (GthImageSaver *self)
+{
+	return gtk_label_new (_("No options available for this file type"));
+}
+
+
+static void
+base_save_options (GthImageSaver *self)
+{
+	/* void */
+}
+
+
+static gboolean
+base_can_save (GthImageSaver *self,
+	       const char    *mime_type)
+{
+	return FALSE;
+}
+
+
+static gboolean
+base_save_image (GthImageSaver  *self,
+		 GthImage       *image,
+		 char          **buffer,
+		 gsize          *buffer_size,
+		 const char     *mime_type,
+		 GError        **error)
+{
+	return FALSE;
+}
+
+
+static void
+gth_image_saver_class_init (GthImageSaverClass *klass)
+{
+	klass->id = "";
+	klass->display_name = "";
+	klass->get_control = base_get_control;
+	klass->save_options = base_save_options;
+	klass->can_save = base_can_save;
+	klass->save_image = base_save_image;
+}
+
+
+static void
+gth_image_saver_init (GthImageSaver *self)
+{
+	/* void */
+}
+
+
+const char *
+gth_image_saver_get_id (GthImageSaver *self)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->id;
+}
+
+
+const char *
+gth_image_saver_get_display_name (GthImageSaver *self)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->display_name;
+}
+
+
+const char *
+gth_image_saver_get_mime_type (GthImageSaver *self)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->mime_type;
+}
+
+
+const char *
+gth_image_saver_get_extensions (GthImageSaver *self)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->extensions;
+}
+
+
+const char *
+gth_image_saver_get_default_ext (GthImageSaver *self)
+{
+	if (GTH_IMAGE_SAVER_GET_CLASS (self)->get_default_ext != NULL)
+		return GTH_IMAGE_SAVER_GET_CLASS (self)->get_default_ext (self);
+	else
+		return gth_image_saver_get_extensions (self);
+}
+
+
+GtkWidget *
+gth_image_saver_get_control (GthImageSaver *self)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->get_control (self);
+}
+
+
+void
+gth_image_saver_save_options (GthImageSaver *self)
+{
+	GTH_IMAGE_SAVER_GET_CLASS (self)->save_options (self);
+}
+
+
+gboolean
+gth_image_saver_can_save (GthImageSaver *self,
+			  const char    *mime_type)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->can_save (self, mime_type);
+}
+
+
+static gboolean
+gth_image_saver_save_image (GthImageSaver  *self,
+			    GthImage       *image,
+			    char          **buffer,
+			    gsize          *buffer_size,
+			    const char     *mime_type,
+			    GError        **error)
+{
+	return GTH_IMAGE_SAVER_GET_CLASS (self)->save_image (self,
+							     image,
+							     buffer,
+							     buffer_size,
+							     mime_type,
+							     error);
+}
+
+
+gboolean
+gth_image_save_to_buffer (GthImage    *image,
+			  const char  *mime_type,
+			  char       **buffer,
+			  gsize       *buffer_size,
+			  GError     **p_error)
+{
+	GthImageSaver *saver;
+	gboolean       result;
+	GError        *error = NULL;
+
+	saver = gth_main_get_image_saver (mime_type);
+	if (saver == NULL) {
+		if (p_error != NULL)
+			*p_error = g_error_new (GTH_ERROR, GTH_ERROR_GENERIC, _("Could not find a suitable module to save the image as \"%s\""), mime_type);
+		return FALSE;
+	}
+
+	result = gth_image_saver_save_image (saver, image, buffer, buffer_size, mime_type, &error);
+
+	if (p_error != NULL)
+		*p_error = error;
+	else
+		_g_error_free (error);
+
+	g_object_unref (saver);
+
+	return result;
+}
+
+
+/*
+gboolean
+_cairo_image_surface_to_mime_type (cairo_surface_t  *image,
+				   char            **buffer,
+				   gsize            *buffer_size,
+				   const char       *mime_type,
+				   GError          **error)
+{
+
+}
+*/
+
+
+/* -- gth_image_save_to_file -- */
+
+
+typedef struct {
+	GthImageSaveData *data;
+	GthFileDataFunc   ready_func;
+	gpointer          ready_data;
+	GList            *current;
+} SaveData;
+
+
+static void
+gth_image_save_file_free (GthImageSaveFile *file)
+{
+	g_object_unref (file->file);
+	g_free (file->buffer);
+	g_free (file);
+}
+
+
+static void
+gth_image_save_data_free (GthImageSaveData *data)
+{
+	g_object_unref (data->file_data);
+	g_object_unref (data->image);
+	g_list_foreach (data->files, (GFunc) gth_image_save_file_free, NULL);
+	g_list_free (data->files);
+	g_free (data);
+}
+
+
+static void
+save_completed (SaveData *save_data)
+{
+	if (save_data->data->error != NULL)
+		(*save_data->ready_func) (save_data->data->file_data, *save_data->data->error, save_data->ready_data);
+	else
+		(*save_data->ready_func) (save_data->data->file_data, NULL, save_data->ready_data);
+	gth_image_save_data_free (save_data->data);
+	g_free (save_data);
+}
+
+
+static void save_current_file (SaveData *save_data);
+
+
+static void
+file_saved_cb (void     **buffer,
+	       gsize      count,
+	       GError    *error,
+	       gpointer   user_data)
+{
+	SaveData *save_data = user_data;
+
+	*buffer = NULL; /* do not free the buffer, it's owned by file->buffer */
+
+	if (error != NULL) {
+		save_data->data->error = &error;
+		save_completed (save_data);
+		return;
+	}
+
+	save_data->current = save_data->current->next;
+	save_current_file (save_data);
+}
+
+
+static void
+save_current_file (SaveData *save_data)
+{
+	GthImageSaveFile *file;
+
+	if (save_data->current == NULL) {
+		save_completed (save_data);
+		return;
+	}
+
+	file = save_data->current->data;
+	_g_file_write_async (file->file,
+			     file->buffer,
+			     file->buffer_size,
+			     (g_file_equal (save_data->data->file_data->file, file->file) ? save_data->data->replace : TRUE),
+			     G_PRIORITY_DEFAULT,
+			     NULL,
+			     file_saved_cb,
+			     save_data);
+}
+
+
+static void
+save_files (GthImageSaveData *data,
+	    GthFileDataFunc   ready_func,
+	    gpointer          ready_data)
+{
+	SaveData *save_data;
+
+	save_data = g_new0 (SaveData, 1);
+	save_data->data = data;
+	save_data->ready_func = ready_func;
+	save_data->ready_data = ready_data;
+
+	save_data->current = save_data->data->files;
+	save_current_file (save_data);
+}
+
+
+void
+gth_image_save_to_file (GthImage        *image,
+			const char      *mime_type,
+			GthFileData     *file_data,
+			gboolean         replace,
+			GthFileDataFunc  ready_func,
+			gpointer         user_data)
+{
+	void             *buffer;
+	gsize             buffer_size;
+	GError           *error = NULL;
+	GthImageSaveData *data;
+
+	if (! gth_image_save_to_buffer (image,
+					mime_type,
+					(char **) &buffer,
+					&buffer_size,
+					&error))
+	{
+		gth_file_data_ready_with_error (file_data, ready_func, user_data, error);
+		return;
+	}
+
+	data = g_new0 (GthImageSaveData, 1);
+	data->file_data = g_object_ref (file_data);
+	data->image = gth_image_copy (image);
+	data->mime_type = mime_type;
+	data->replace = replace;
+	data->buffer = buffer;
+	data->buffer_size = buffer_size;
+	data->files = NULL;
+	data->error = NULL;
+	gth_hook_invoke ("save-image", data);
+
+	if (data->error == NULL) {
+		GthImageSaveFile *file;
+
+		file = g_new0 (GthImageSaveFile, 1);
+		file->file = g_object_ref (data->file_data->file);
+		file->buffer = data->buffer;
+		file->buffer_size = data->buffer_size;
+		data->files = g_list_prepend (data->files, file);
+	}
+	else {
+		gth_image_save_data_free (data);
+		g_free (buffer);
+		gth_file_data_ready_with_error (file_data, ready_func, user_data, error);
+		return;
+	}
+
+	save_files (data, ready_func, user_data);
+}
diff --git a/gthumb/gth-image-saver.h b/gthumb/gth-image-saver.h
new file mode 100644
index 0000000..5ba1522
--- /dev/null
+++ b/gthumb/gth-image-saver.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_IMAGE_SAVER_H
+#define GTH_IMAGE_SAVER_H
+
+#include <gtk/gtk.h>
+#include "gth-file-data.h"
+#include "gth-image.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_IMAGE_SAVER              (gth_image_saver_get_type ())
+#define GTH_IMAGE_SAVER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE_SAVER, GthImageSaver))
+#define GTH_IMAGE_SAVER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE_SAVER, GthImageSaverClass))
+#define GTH_IS_IMAGE_SAVER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_IMAGE_SAVER))
+#define GTH_IS_IMAGE_SAVER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_IMAGE_SAVER))
+#define GTH_IMAGE_SAVER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_IMAGE_SAVER, GthImageSaverClass))
+
+typedef struct _GthImageSaver         GthImageSaver;
+typedef struct _GthImageSaverClass    GthImageSaverClass;
+typedef struct _GthImageSaverPrivate  GthImageSaverPrivate;
+
+struct _GthImageSaver
+{
+	GObject __parent;
+	GthImageSaverPrivate *priv;
+};
+
+struct _GthImageSaverClass
+{
+	GObjectClass __parent_class;
+
+	/*< class attributes >*/
+
+	const char *id;
+	const char *display_name;
+	const char *mime_type;
+	const char *extensions;
+
+	/*< virtual functions >*/
+
+	const char * (*get_default_ext) (GthImageSaver   *self);
+	GtkWidget *  (*get_control)     (GthImageSaver   *self);
+	void         (*save_options)    (GthImageSaver   *self);
+	gboolean     (*can_save)        (GthImageSaver   *self,
+				         const char      *mime_type);
+	gboolean     (*save_image)      (GthImageSaver   *self,
+					 GthImage        *image,
+				         char           **buffer,
+				         gsize           *buffer_size,
+				         const char      *mime_type,
+				         GError         **error);
+};
+
+typedef struct {
+	GFile *file;
+	void  *buffer;
+	gsize  buffer_size;
+} GthImageSaveFile;
+
+
+typedef struct {
+	GthImage     *image;
+	GthFileData  *file_data;
+	const char   *mime_type;
+	gboolean      replace;
+	void         *buffer;
+	gsize         buffer_size;
+	GList        *files; 		/* GthImageSaveFile list */
+	GError      **error;
+} GthImageSaveData;
+
+
+GType         gth_image_saver_get_type          (void);
+const char *  gth_image_saver_get_id            (GthImageSaver    *self);
+const char *  gth_image_saver_get_display_name  (GthImageSaver    *self);
+const char *  gth_image_saver_get_mime_type     (GthImageSaver    *self);
+const char *  gth_image_saver_get_extensions    (GthImageSaver    *self);
+const char *  gth_image_saver_get_default_ext   (GthImageSaver    *self);
+GtkWidget *   gth_image_saver_get_control       (GthImageSaver    *self);
+void          gth_image_saver_save_options      (GthImageSaver    *self);
+gboolean      gth_image_saver_can_save          (GthImageSaver    *self,
+					         const char       *mime_type);
+gboolean      gth_image_save_to_buffer          (GthImage         *image,
+						 const char       *mime_type,
+						 char            **buffer,
+						 gsize            *buffer_size,
+						 GError          **error);
+/*
+gboolean      _cairo_image_surface_to_mime_type (cairo_surface_t  *image,
+						 char            **buffer,
+						 gsize            *buffer_size,
+						 const char       *mime_type,
+						 GError          **error);
+*/
+void          gth_image_save_to_file            (GthImage         *image,
+						 const char       *mime_type,
+						 GthFileData      *file_data,
+						 gboolean          replace,
+						 GthFileDataFunc   ready_func,
+						 gpointer          user_data);
+
+G_END_DECLS
+
+#endif /* GTH_IMAGE_SAVER_H */
diff --git a/gthumb/gth-image-task.c b/gthumb/gth-image-task.c
new file mode 100644
index 0000000..effe1b0
--- /dev/null
+++ b/gthumb/gth-image-task.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001, 2009, 2012 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include "glib-utils.h"
+#include "gth-image-task.h"
+
+
+struct _GthImageTaskPrivate {
+	GthImage *source;
+	GthImage *destination;
+	char     *description;
+};
+
+
+G_DEFINE_TYPE (GthImageTask, gth_image_task, GTH_TYPE_ASYNC_TASK)
+
+
+static void
+gth_image_task_finalize (GObject *object)
+{
+	GthImageTask *self;
+
+	g_return_if_fail (GTH_IS_IMAGE_TASK (object));
+
+	self = GTH_IMAGE_TASK (object);
+	_g_object_unref (self->priv->source);
+	_g_object_unref (self->priv->destination);
+	g_free (self->priv->description);
+
+	G_OBJECT_CLASS (gth_image_task_parent_class)->finalize (object);
+}
+
+
+static void
+gth_image_task_class_init (GthImageTaskClass *class)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (class, sizeof (GthImageTaskPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = gth_image_task_finalize;
+}
+
+
+static void
+gth_image_task_init (GthImageTask *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_IMAGE_TASK, GthImageTaskPrivate);
+	self->priv->source = NULL;
+	self->priv->destination = NULL;
+	self->priv->description = NULL;
+}
+
+
+GthTask *
+gth_image_task_new (const char         *description,
+		    GthAsyncInitFunc    before_func,
+		    GthAsyncThreadFunc  exec_func,
+		    GthAsyncReadyFunc   after_func,
+		    gpointer            user_data,
+		    GDestroyNotify      user_data_destroy_func)
+{
+	GthImageTask *self;
+
+	self = (GthImageTask *) g_object_new (GTH_TYPE_IMAGE_TASK,
+					      "before-thread", before_func,
+					      "thread-func", exec_func,
+					      "after-thread", after_func,
+					      "user-data", user_data,
+					      "user-data-destroy-func", user_data_destroy_func,
+					      NULL);
+	self->priv->description = g_strdup (description);
+
+	return (GthTask *) self;
+}
+
+
+void
+gth_image_task_set_source (GthImageTask *self,
+			   GthImage     *source)
+{
+	g_return_if_fail (GTH_IS_IMAGE (source));
+
+	_g_object_ref (source);
+	_g_object_unref (self->priv->source);
+	self->priv->source = source;
+}
+
+
+GthImage *
+gth_image_task_get_source (GthImageTask *self)
+{
+	return self->priv->source;
+}
+
+
+void
+gth_image_task_set_destination (GthImageTask *self,
+				GthImage     *destination)
+{
+	g_return_if_fail (GTH_IS_IMAGE (destination));
+
+	_g_object_ref (destination);
+	_g_object_unref (self->priv->destination);
+	self->priv->destination = destination;
+}
+
+
+GthImage *
+gth_image_task_get_destination (GthImageTask *self)
+{
+	return self->priv->destination;
+}
+
+
+void
+gth_image_task_copy_source_to_destination (GthImageTask *self)
+{
+	g_return_if_fail (self->priv->source != NULL);
+
+	_g_object_unref (self->priv->destination);
+	self->priv->destination = gth_image_copy (self->priv->source);
+}
+
+
+const char *
+gth_image_task_get_description (GthImageTask *self)
+{
+	return self->priv->description;
+}
diff --git a/gthumb/gth-image-task.h b/gthumb/gth-image-task.h
new file mode 100644
index 0000000..d06fd9f
--- /dev/null
+++ b/gthumb/gth-image-task.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001-2009 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_IMAGE_TASK_H
+#define GTH_IMAGE_TASK_H
+
+#include <glib.h>
+#include "gth-async-task.h"
+#include "gth-image.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_IMAGE_TASK            (gth_image_task_get_type ())
+#define GTH_IMAGE_TASK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE_TASK, GthImageTask))
+#define GTH_IMAGE_TASK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE_TASK, GthImageTaskClass))
+#define GTH_IS_IMAGE_TASK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_IMAGE_TASK))
+#define GTH_IS_IMAGE_TASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_IMAGE_TASK))
+#define GTH_IMAGE_TASK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_IMAGE_TASK, GthImageTaskClass))
+
+typedef struct _GthImageTask        GthImageTask;
+typedef struct _GthImageTaskClass   GthImageTaskClass;
+typedef struct _GthImageTaskPrivate GthImageTaskPrivate;
+
+struct _GthImageTask {
+	GthAsyncTask __parent;
+	GthImageTaskPrivate *priv;
+};
+
+struct _GthImageTaskClass {
+	GthAsyncTaskClass __parent;
+};
+
+GType         gth_image_task_get_type        (void);
+GthTask *     gth_image_task_new             (const char         *description,
+					      GthAsyncInitFunc    before_func,
+					      GthAsyncThreadFunc  exec_func,
+					      GthAsyncReadyFunc   after_func,
+					      gpointer            user_data,
+					      GDestroyNotify      user_data_destroy_func);
+void          gth_image_task_set_source      (GthImageTask       *self,
+					      GthImage           *source);
+GthImage *    gth_image_task_get_source      (GthImageTask       *self);
+void          gth_image_task_set_destination (GthImageTask       *self,
+					      GthImage           *destination);
+GthImage *    gth_image_task_get_destination (GthImageTask       *self);
+void          gth_image_task_copy_source_to_destination
+					     (GthImageTask       *self);
+const char *  gth_image_task_get_description (GthImageTask       *self);
+
+G_END_DECLS
+
+#endif /* GTH_IMAGE_TASK_H */
diff --git a/gthumb/gth-image.c b/gthumb/gth-image.c
index 9c27a52..81f84cf 100644
--- a/gthumb/gth-image.c
+++ b/gthumb/gth-image.c
@@ -129,6 +129,18 @@ gth_image_new (void)
 
 
 GthImage *
+gth_image_new_for_surface (cairo_surface_t *surface)
+{
+	GthImage *image;
+
+	image = gth_image_new ();
+	gth_image_set_cairo_surface (image, surface);
+
+	return image;
+}
+
+
+GthImage *
 gth_image_new_for_pixbuf (GdkPixbuf *value)
 {
 	GthImage *image;
@@ -140,6 +152,37 @@ gth_image_new_for_pixbuf (GdkPixbuf *value)
 }
 
 
+GthImage *
+gth_image_copy (GthImage *image)
+{
+	GthImage *new_image;
+
+	new_image = gth_image_new ();
+
+	switch (image->priv->format) {
+	case GTH_IMAGE_FORMAT_CAIRO_SURFACE:
+		new_image->priv->format = GTH_IMAGE_FORMAT_CAIRO_SURFACE;
+		new_image->priv->data.surface = _cairo_image_surface_copy (image->priv->data.surface);
+		break;
+
+	case GTH_IMAGE_FORMAT_GDK_PIXBUF:
+		new_image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
+		new_image->priv->data.pixbuf = gdk_pixbuf_copy (image->priv->data.pixbuf);
+		break;
+
+	case GTH_IMAGE_FORMAT_GDK_PIXBUF_ANIMATION:
+		new_image->priv->format = GTH_IMAGE_FORMAT_GDK_PIXBUF;
+		new_image->priv->data.pixbuf = gdk_pixbuf_copy (gdk_pixbuf_animation_get_static_image (image->priv->data.pixbuf_animation));
+		break;
+
+	default:
+		break;
+	}
+
+	return new_image;
+}
+
+
 void
 gth_image_set_cairo_surface (GthImage        *image,
 			     cairo_surface_t *value)
diff --git a/gthumb/gth-image.h b/gthumb/gth-image.h
index 4338ef1..f7e598d 100644
--- a/gthumb/gth-image.h
+++ b/gthumb/gth-image.h
@@ -77,7 +77,9 @@ typedef GthImage * (*GthImageLoaderFunc) (GInputStream  *istream,
 
 GType                 gth_image_get_type                    (void);
 GthImage *            gth_image_new                         (void);
+GthImage *            gth_image_new_for_surface             (cairo_surface_t    *surface);
 GthImage *            gth_image_new_for_pixbuf              (GdkPixbuf          *value);
+GthImage *            gth_image_copy                        (GthImage           *image);
 void                  gth_image_set_cairo_surface           (GthImage           *image,
 						             cairo_surface_t    *value);
 cairo_surface_t *     gth_image_get_cairo_surface           (GthImage           *image);
diff --git a/gthumb/gth-main-default-hooks.c b/gthumb/gth-main-default-hooks.c
index 3a0c26e..68f114a 100644
--- a/gthumb/gth-main-default-hooks.c
+++ b/gthumb/gth-main-default-hooks.c
@@ -189,11 +189,11 @@ gth_main_register_default_hooks (void)
 	gth_hook_register ("gth-browser-file-renamed", 3);
 
 	/**
-	 * Called in _gdk_pixbuf_save_async
+	 * Called in gth_image_save_to_file
 	 *
-	 * @data (SavePixbufData*):
+	 * @data (GthImageSaveData*):
 	 **/
-	gth_hook_register ("save-pixbuf", 1);
+	gth_hook_register ("save-image", 1);
 
 	/**
 	 * Called when copying files in _g_copy_files_async with the
diff --git a/gthumb/gth-main.c b/gthumb/gth-main.c
index cd04c1e..45406cd 100644
--- a/gthumb/gth-main.c
+++ b/gthumb/gth-main.c
@@ -716,23 +716,23 @@ gth_main_get_image_loader_func (const char     *mime_type,
 }
 
 
-GthPixbufSaver *
-gth_main_get_pixbuf_saver (const char *mime_type)
+GthImageSaver *
+gth_main_get_image_saver (const char *mime_type)
 {
 	GArray *savers;
 	int     i;
 
-	savers = gth_main_get_type_set ("pixbuf-saver");
+	savers = gth_main_get_type_set ("image-saver");
 	if (savers == NULL)
 		return NULL;
 
 	for (i = 0; i < savers->len; i++) {
 		GType           saver_type;
-		GthPixbufSaver *saver;
+		GthImageSaver *saver;
 
 		saver_type = g_array_index (savers, GType, i);
 		saver = g_object_new (saver_type, NULL);
-		if (gth_pixbuf_saver_can_save (saver, mime_type))
+		if (gth_image_saver_can_save (saver, mime_type))
 			return saver;
 
 		g_object_unref (saver);
diff --git a/gthumb/gth-main.h b/gthumb/gth-main.h
index 00d6a01..90ec628 100644
--- a/gthumb/gth-main.h
+++ b/gthumb/gth-main.h
@@ -31,9 +31,9 @@
 #include "gth-filter-file.h"
 #include "gth-hook.h"
 #include "gth-image.h"
+#include "gth-image-saver.h"
 #include "gth-metadata-provider.h"
 #include "gth-monitor.h"
-#include "gth-pixbuf-saver.h"
 #include "gth-tags-file.h"
 #include "gth-test.h"
 #include "pixbuf-io.h"
@@ -92,7 +92,7 @@ void                   gth_main_register_image_loader_func    (GthImageLoaderFun
 						               ...);
 GthImageLoaderFunc     gth_main_get_image_loader_func         (const char           *mime_type,
 							       GthImageFormat        preferred_format);
-GthPixbufSaver *       gth_main_get_pixbuf_saver              (const char           *mime_type);
+GthImageSaver *        gth_main_get_image_saver               (const char           *mime_type);
 GthTest *              gth_main_get_general_filter            (void);
 GthTest *              gth_main_add_general_filter            (GthTest              *filter);
 void		       gth_main_register_object               (GType                 superclass_type,
diff --git a/gthumb/gth-overwrite-dialog.c b/gthumb/gth-overwrite-dialog.c
index 9299c9e..076ab04 100644
--- a/gthumb/gth-overwrite-dialog.c
+++ b/gthumb/gth-overwrite-dialog.c
@@ -36,7 +36,7 @@
 struct _GthOverwriteDialogPrivate {
 	GtkBuilder     *builder;
 	GFile          *source;
-	GdkPixbuf      *source_pixbuf;
+	GthImage       *source_image;
 	GFile          *destination;
 	GtkWidget      *old_image_viewer;
 	GtkWidget      *new_image_viewer;
@@ -61,7 +61,7 @@ gth_overwrite_dialog_finalize (GObject *object)
 	_g_object_unref (dialog->priv->destination_data);
 	g_object_unref (dialog->priv->builder);
 	_g_object_unref (dialog->priv->source);
-	_g_object_unref (dialog->priv->source_pixbuf);
+	_g_object_unref (dialog->priv->source_image);
 	g_object_unref (dialog->priv->destination);
 	_g_object_unref (dialog->priv->old_image_loader);
 	_g_object_unref (dialog->priv->new_image_loader);
@@ -174,11 +174,11 @@ info_ready_cb (GList    *files,
 				       image_loader_ready_cb,
 				       self);
 	}
-	else if (self->priv->source_pixbuf != NULL) {
+	else if (self->priv->source_image != NULL) {
 		gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_filename_label"));
 		gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_size_label"));
 		gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_modified_label"));
-		gth_image_viewer_set_pixbuf (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), self->priv->source_pixbuf, -1, -1);
+		gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), self->priv->source_image, -1, -1);
 	}
 
 	/* old image  */
@@ -326,7 +326,7 @@ gth_overwrite_dialog_construct (GthOverwriteDialog   *self,
 
 GtkWidget *
 gth_overwrite_dialog_new (GFile                *source,
-			  GdkPixbuf            *source_pixbuf,
+			  GthImage             *source_image,
 			  GFile                *destination,
 			  GthOverwriteResponse  default_respose,
 			  gboolean              single_file)
@@ -335,7 +335,7 @@ gth_overwrite_dialog_new (GFile                *source,
 
 	self = g_object_new (GTH_TYPE_OVERWRITE_DIALOG, NULL);
 	self->priv->source = _g_object_ref (source);
-	self->priv->source_pixbuf = _g_object_ref (source_pixbuf);
+	self->priv->source_image = _g_object_ref (source_image);
 	self->priv->destination = g_object_ref (destination);
 	gth_overwrite_dialog_construct (self, default_respose, single_file);
 
diff --git a/gthumb/gth-overwrite-dialog.h b/gthumb/gth-overwrite-dialog.h
index 6c69155..5de39ff 100644
--- a/gthumb/gth-overwrite-dialog.h
+++ b/gthumb/gth-overwrite-dialog.h
@@ -23,6 +23,7 @@
 #define GTH_OVERWRITE_DIALOG_H
 
 #include <gtk/gtk.h>
+#include "gth-image.h"
 
 G_BEGIN_DECLS
 
@@ -58,7 +59,7 @@ struct _GthOverwriteDialogClass {
 
 GType                 gth_overwrite_dialog_get_type      (void);
 GtkWidget *           gth_overwrite_dialog_new           (GFile                *source,
-							  GdkPixbuf            *source_pixbuf,
+							  GthImage             *source_image,
 						          GFile                *destination,
 						          GthOverwriteResponse  default_respose,
 						          gboolean              single_file);
diff --git a/gthumb/pixbuf-io.c b/gthumb/pixbuf-io.c
index eeb0919..719b669 100644
--- a/gthumb/pixbuf-io.c
+++ b/gthumb/pixbuf-io.c
@@ -28,7 +28,7 @@
 #include "gth-error.h"
 #include "gth-hook.h"
 #include "gth-main.h"
-#include "gth-pixbuf-saver.h"
+#include "gth-image-saver.h"
 #include "pixbuf-io.h"
 #include "pixbuf-utils.h"
 
@@ -51,178 +51,6 @@ get_pixbuf_type_from_mime_type (const char *mime_type)
 }
 
 
-typedef struct {
-	SavePixbufData  *data;
-	GthFileDataFunc  ready_func;
-	gpointer         ready_data;
-	GList           *current;
-} SaveData;
-
-
-static void
-save_pixbuf_file_free (SavePixbufFile *file)
-{
-	g_object_unref (file->file);
-	g_free (file->buffer);
-	g_free (file);
-}
-
-
-static void
-save_pixbuf_data_free (SavePixbufData *data)
-{
-	g_object_unref (data->file_data);
-	g_object_unref (data->pixbuf);
-	g_list_foreach (data->files, (GFunc) save_pixbuf_file_free, NULL);
-	g_list_free (data->files);
-	g_free (data);
-}
-
-
-static void
-save_completed (SaveData *save_data)
-{
-	if (save_data->data->error != NULL)
-		(*save_data->ready_func) (save_data->data->file_data, *save_data->data->error, save_data->ready_data);
-	else
-		(*save_data->ready_func) (save_data->data->file_data, NULL, save_data->ready_data);
-	save_pixbuf_data_free (save_data->data);
-	g_free (save_data);
-}
-
-
-static void save_current_file (SaveData *save_data);
-
-
-static void
-file_saved_cb (void     **buffer,
-	       gsize      count,
-	       GError    *error,
-	       gpointer   user_data)
-{
-	SaveData *save_data = user_data;
-
-	*buffer = NULL; /* do not free the buffer, it's owned by file->buffer */
-
-	if (error != NULL) {
-		save_data->data->error = &error;
-		save_completed (save_data);
-		return;
-	}
-
-	save_data->current = save_data->current->next;
-	save_current_file (save_data);
-}
-
-
-static void
-save_current_file (SaveData *save_data)
-{
-	SavePixbufFile *file;
-
-	if (save_data->current == NULL) {
-		save_completed (save_data);
-		return;
-	}
-
-	file = save_data->current->data;
-	_g_file_write_async (file->file,
-			     file->buffer,
-			     file->buffer_size,
-			     (g_file_equal (save_data->data->file_data->file, file->file) ? save_data->data->replace : TRUE),
-			     G_PRIORITY_DEFAULT,
-			     NULL,
-			     file_saved_cb,
-			     save_data);
-}
-
-
-static void
-save_files (SavePixbufData  *data,
-	    GthFileDataFunc  ready_func,
-	    gpointer         ready_data)
-{
-	SaveData *save_data;
-
-	save_data = g_new0 (SaveData, 1);
-	save_data->data = data;
-	save_data->ready_func = ready_func;
-	save_data->ready_data = ready_data;
-
-	save_data->current = save_data->data->files;
-	save_current_file (save_data);
-}
-
-
-void
-_gdk_pixbuf_save_async (GdkPixbuf        *pixbuf,
-			GthFileData      *file_data,
-			const char       *mime_type,
-			gboolean          replace,
-			GthFileDataFunc   ready_func,
-			gpointer          ready_data)
-{
-	GthPixbufSaver *saver;
-	GError         *error = NULL;
-	void           *buffer;
-	gsize           buffer_size;
-	GdkPixbuf      *tmp_pixbuf;
-	SavePixbufData *data;
-
-	saver = gth_main_get_pixbuf_saver (mime_type);
-	if (saver == NULL) {
-		error = g_error_new (GTH_ERROR, GTH_ERROR_GENERIC, _("Could not find a suitable module to save the image as \"%s\""), mime_type);
-		gth_file_data_ready_with_error (file_data, ready_func, ready_data, error);
-		return;
-	}
-
-	tmp_pixbuf = gdk_pixbuf_copy (pixbuf);
-	if (! gth_pixbuf_saver_save_pixbuf (saver,
-					    tmp_pixbuf,
-					    (char **)&buffer,
-					    &buffer_size,
-					    mime_type,
-					    &error))
-	{
-		g_object_unref (saver);
-		g_object_unref (tmp_pixbuf);
-		gth_file_data_ready_with_error (file_data, ready_func, ready_data, error);
-		return;
-	}
-
-	g_object_unref (saver);
-
-	data = g_new0 (SavePixbufData, 1);
-	data->file_data = g_object_ref (file_data);
-	data->pixbuf = tmp_pixbuf;
-	data->mime_type = mime_type;
-	data->replace = replace;
-	data->buffer = buffer;
-	data->buffer_size = buffer_size;
-	data->files = NULL;
-	data->error = NULL;
-	gth_hook_invoke ("save-pixbuf", data);
-
-	if (data->error == NULL) {
-		SavePixbufFile *file;
-
-		file = g_new0 (SavePixbufFile, 1);
-		file->file = g_object_ref (data->file_data->file);
-		file->buffer = data->buffer;
-		file->buffer_size = data->buffer_size;
-		data->files = g_list_prepend (data->files, file);
-	}
-	else {
-		save_pixbuf_data_free (data);
-		g_free (buffer);
-		gth_file_data_ready_with_error (file_data, ready_func, ready_data, error);
-		return;
-	}
-
-	save_files (data, ready_func, ready_data);
-}
-
-
 #ifdef USE_PIXBUF_LOADER
 
 
diff --git a/gthumb/pixbuf-io.h b/gthumb/pixbuf-io.h
index c2faaa3..15eb29d 100644
--- a/gthumb/pixbuf-io.h
+++ b/gthumb/pixbuf-io.h
@@ -38,31 +38,8 @@ typedef GdkPixbufAnimation* (*PixbufLoader) (GthFileData   *file_data,
 					     GCancellable  *cancellable,
 				   	     GError       **error);
 
-typedef struct {
-	GFile *file;
-	void  *buffer;
-	gsize  buffer_size;
-} SavePixbufFile;
-
-
-typedef struct {
-	GthFileData  *file_data;
-	GdkPixbuf    *pixbuf;
-	const char   *mime_type;
-	gboolean      replace;
-	void         *buffer;
-	gsize         buffer_size;
-	GList        *files; 		/* SavePixbufFile list */
-	GError      **error;
-} SavePixbufData;
 
 char *      get_pixbuf_type_from_mime_type     (const char       *mime_type);
-void        _gdk_pixbuf_save_async             (GdkPixbuf        *pixbuf,
-						GthFileData      *file_data,
-						const char       *mime_type,
-						gboolean          replace,
-						GthFileDataFunc   ready_func,
-						gpointer          data);
 GthImage  * gth_pixbuf_new_from_file           (GInputStream     *istream,
 						GthFileData      *file,
 						int               requested_size,
diff --git a/gthumb/typedefs.h b/gthumb/typedefs.h
index 62e4dc3..95c3736 100644
--- a/gthumb/typedefs.h
+++ b/gthumb/typedefs.h
@@ -124,6 +124,14 @@ typedef enum {
 } GthGridType;
 
 
+typedef enum /*< skip >*/ {
+        GTH_CHANNEL_RED   = 0,
+        GTH_CHANNEL_GREEN = 1,
+        GTH_CHANNEL_BLUE  = 2,
+        GTH_CHANNEL_ALPHA = 3
+} GthChannel;
+
+
 typedef void (*DataFunc)         (gpointer    user_data);
 typedef void (*ReadyFunc)        (GError     *error,
 			 	  gpointer    user_data);



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