]> Pileus Git - ~andy/gtk/blobdiff - gdk-pixbuf/gdk-pixbuf-io.c
Add color management support to gdk_pixbuf_save
[~andy/gtk] / gdk-pixbuf / gdk-pixbuf-io.c
index 56dfefe627571d64e18749efb69a6d2069317a65..ed765ed407460dddf79cc4689da24b7656f63b19 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <glib.h>
 #include <errno.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
+#include <glib.h>
+#include <gio/gio.h>
+
 #include "gdk-pixbuf-private.h"
 #include "gdk-pixbuf-io.h"
+#include "gdk-pixbuf-loader.h"
 #include "gdk-pixbuf-alias.h"
 
 #include <glib/gstdio.h>
 #undef STRICT
 #endif
 
+#define SNIFF_BUFFER_SIZE 4096
+#define LOAD_BUFFER_SIZE 65536
+
+#ifndef GDK_PIXBUF_USE_GIO_MIME 
 static gint 
 format_check (GdkPixbufModule *module, guchar *buffer, int size)
 {
@@ -96,6 +103,7 @@ format_check (GdkPixbufModule *module, guchar *buffer, int size)
        }
        return 0;
 }
+#endif
 
 G_LOCK_DEFINE_STATIC (init_lock);
 G_LOCK_DEFINE_STATIC (threadunsafe_loader_lock);
@@ -123,7 +131,7 @@ _gdk_pixbuf_unlock (GdkPixbufModule *image_module)
 
 static GSList *file_formats = NULL;
 
-static void gdk_pixbuf_io_init ();
+static void gdk_pixbuf_io_init (void);
 
 static GSList *
 get_file_formats (void)
@@ -220,8 +228,23 @@ skip_space (const char **pos)
   
 #ifdef G_OS_WIN32
 
-/* DllMain function needed to tuck away the gdk-pixbuf DLL name */
-G_WIN32_DLLMAIN_FOR_DLL_NAME (static, dll_name)
+/* DllMain function needed to tuck away the gdk-pixbuf DLL handle */
+
+static HMODULE gdk_pixbuf_dll;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+        DWORD     fdwReason,
+        LPVOID    lpvReserved)
+{
+       switch (fdwReason) {
+       case DLL_PROCESS_ATTACH:
+               gdk_pixbuf_dll = (HMODULE) hinstDLL;
+               break;
+       }
+
+  return TRUE;
+}
 
 static char *
 get_toplevel (void)
@@ -229,8 +252,7 @@ get_toplevel (void)
   static char *toplevel = NULL;
 
   if (toplevel == NULL)
-    toplevel = g_win32_get_package_installation_subdirectory
-      (GETTEXT_PACKAGE, dll_name, "");
+         toplevel = g_win32_get_package_installation_directory_of_module (gdk_pixbuf_dll);
 
   return toplevel;
 }
@@ -241,8 +263,7 @@ get_sysconfdir (void)
   static char *sysconfdir = NULL;
 
   if (sysconfdir == NULL)
-    sysconfdir = g_win32_get_package_installation_subdirectory
-      (GETTEXT_PACKAGE, dll_name, "etc");
+         sysconfdir = g_build_filename (get_toplevel (), "etc", NULL);
 
   return sysconfdir;
 }
@@ -256,6 +277,13 @@ correct_prefix (gchar **path)
   if (strncmp (*path, GTK_PREFIX "/", strlen (GTK_PREFIX "/")) == 0 ||
       strncmp (*path, GTK_PREFIX "\\", strlen (GTK_PREFIX "\\")) == 0)
     {
+         gchar *tem = NULL;
+      if (strlen(*path) > 5 && strncmp (*path - 5, ".libs", 5) == 0)
+        {
+          /* We are being run from inside the build tree, and shouldn't mess about. */
+          return;
+       }
+
       /* This is an entry put there by gdk-pixbuf-query-loaders on the
        * packager's system. On Windows a prebuilt GTK+ package can be
        * installed in a random location. The gdk-pixbuf.loaders file
@@ -263,7 +291,7 @@ correct_prefix (gchar **path)
        * builder's machine. Replace the build-time prefix with the
        * installation prefix on this machine.
        */
-      gchar *tem = *path;
+      tem = *path;
       *path = g_strconcat (get_toplevel (), tem + strlen (GTK_PREFIX), NULL);
       g_free (tem);
     }
@@ -282,9 +310,17 @@ gdk_pixbuf_get_module_file (void)
   return result;
 }
 
+#endif /* USE_GMODULE */
+
+
+static gboolean
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+                                GError         **error);
+
 static void 
 gdk_pixbuf_io_init (void)
 {
+#ifdef USE_GMODULE
        GIOChannel *channel;
        gchar *line_buf;
        gsize term;
@@ -296,11 +332,101 @@ gdk_pixbuf_io_init (void)
        int n_patterns = 0;
        GdkPixbufModulePattern *pattern;
        GError *error = NULL;
+#endif
+       GdkPixbufModule *builtin_module ;
+
+        /*  initialize on separate line to avoid compiler warnings in the
+         *  common case of no compiled-in modules.
+         */
+       builtin_module = NULL;
+
+#define load_one_builtin_module(format)                                        \
+       builtin_module = g_new0 (GdkPixbufModule, 1);                   \
+       builtin_module->module_name = #format;                          \
+       if (gdk_pixbuf_load_module_unlocked (builtin_module, NULL))             \
+               file_formats = g_slist_prepend (file_formats, builtin_module);\
+       else                                                            \
+               g_free (builtin_module)
+
+#ifdef INCLUDE_ani
+       load_one_builtin_module (ani);
+#endif
+#ifdef INCLUDE_png
+       load_one_builtin_module (png);
+#endif
+#ifdef INCLUDE_bmp
+       load_one_builtin_module (bmp);
+#endif
+#ifdef INCLUDE_wbmp
+       load_one_builtin_module (wbmp);
+#endif
+#ifdef INCLUDE_gif
+       load_one_builtin_module (gif);
+#endif
+#ifdef INCLUDE_ico
+       load_one_builtin_module (ico);
+#endif
+#ifdef INCLUDE_jpeg
+       load_one_builtin_module (jpeg);
+#endif
+#ifdef INCLUDE_pnm
+       load_one_builtin_module (pnm);
+#endif
+#ifdef INCLUDE_ras
+       load_one_builtin_module (ras);
+#endif
+#ifdef INCLUDE_tiff
+       load_one_builtin_module (tiff);
+#endif
+#ifdef INCLUDE_xpm
+       load_one_builtin_module (xpm);
+#endif
+#ifdef INCLUDE_xbm
+       load_one_builtin_module (xbm);
+#endif
+#ifdef INCLUDE_tga
+       load_one_builtin_module (tga);
+#endif
+#ifdef INCLUDE_pcx
+       load_one_builtin_module (pcx);
+#endif
+#ifdef INCLUDE_icns
+       load_one_builtin_module (icns);
+#endif
+#ifdef INCLUDE_jasper
+       load_one_builtin_module (jasper);
+#endif
+#ifdef INCLUDE_qtif
+       load_one_builtin_module (qtif);
+#endif
+#ifdef INCLUDE_gdiplus
+       /* We don't bother having the GDI+ loaders individually selectable
+        * for building in or not.
+        */
+       load_one_builtin_module (ico);
+       load_one_builtin_module (wmf);
+       load_one_builtin_module (emf);
+       load_one_builtin_module (bmp);
+       load_one_builtin_module (gif);
+       load_one_builtin_module (jpeg);
+       load_one_builtin_module (tiff);
+#endif
+#ifdef INCLUDE_gdip_png
+       /* Except the gdip-png loader which normally isn't built at all even */
+       load_one_builtin_module (png);
+#endif
 
+#undef load_one_builtin_module
+
+#ifdef USE_GMODULE
        channel = g_io_channel_new_file (filename, "r",  &error);
        if (!channel) {
-               g_warning ("Cannot open pixbuf loader module file '%s': %s",
-                          filename, error->message);
+               /* Don't bother warning if we have some built-in loaders */
+               if (file_formats == NULL)
+                       g_warning ("Cannot open pixbuf loader module file '%s': %s",
+                                  filename, error->message);
+               g_string_free (tmp_buf, TRUE);
+               g_free (filename);
                return;
        }
        
@@ -372,6 +498,10 @@ gdk_pixbuf_io_init (void)
                                have_error = TRUE;
                        }                       
                        module->info->description = g_strdup (tmp_buf->str);
+
+                       if (scan_string (&p, tmp_buf)) {
+                               module->info->license = g_strdup (tmp_buf->str);
+                       }
                }
                else if (!module->info->mime_types) {
                        int n = 1;
@@ -438,242 +568,203 @@ gdk_pixbuf_io_init (void)
        g_string_free (tmp_buf, TRUE);
        g_io_channel_unref (channel);
        g_free (filename);
+#endif
 }
 
-/* actually load the image handler - gdk_pixbuf_get_module only get a */
-/* reference to the module to load, it doesn't actually load it       */
-/* perhaps these actions should be combined in one function           */
-static gboolean
-_gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
-                                 GError         **error)
-{
-       char *path;
-       GModule *module;
-       gpointer sym;
-               
-        g_return_val_if_fail (image_module->module == NULL, FALSE);
-
-       path = image_module->module_path;
-       module = g_module_open (path, G_MODULE_BIND_LAZY);
-
-        if (!module) {
-                g_set_error (error,
-                             GDK_PIXBUF_ERROR,
-                             GDK_PIXBUF_ERROR_FAILED,
-                             _("Unable to load image-loading module: %s: %s"),
-                             path, g_module_error ());
-                return FALSE;
-        }
-
-       image_module->module = module;        
-        
-        if (g_module_symbol (module, "fill_vtable", &sym)) {
-                GdkPixbufModuleFillVtableFunc func = (GdkPixbufModuleFillVtableFunc) sym;
-                (* func) (image_module);
-                return TRUE;
-        } else {
-                g_set_error (error,
-                             GDK_PIXBUF_ERROR,
-                             GDK_PIXBUF_ERROR_FAILED,
-                             _("Image-loading module %s does not export the proper interface; perhaps it's from a different GTK version?"),
-                             path);
-                return FALSE;
-        }
-}
-
-gboolean
-_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
-                        GError         **error)
-{
-       gboolean ret;
-       gboolean locked = FALSE;
-
-       /* be extra careful, maybe the module initializes
-        * the thread system
-        */
-       if (g_threads_got_initialized)
-       {
-               G_LOCK (init_lock);
-               locked = TRUE;
-       }
-       ret = _gdk_pixbuf_load_module_unlocked (image_module, error);
-       if (locked)
-               G_UNLOCK (init_lock);
-       return ret;
-}
-
-#else  /* !USE_GMODULE */
 
 #define module(type) \
-  extern void MODULE_ENTRY (type, fill_info)   (GdkPixbufFormat *info);   \
-  extern void MODULE_ENTRY (type, fill_vtable) (GdkPixbufModule *module)
+  extern void _gdk_pixbuf__##type##_fill_info   (GdkPixbufFormat *info);   \
+  extern void _gdk_pixbuf__##type##_fill_vtable (GdkPixbufModule *module)
 
 module (png);
-module (bmp);
-module (wbmp);
+module (jpeg);
 module (gif);
 module (ico);
 module (ani);
-module (jpeg);
-module (pnm);
 module (ras);
-module (tiff);
 module (xpm);
+module (tiff);
+module (pnm);
+module (bmp);
+module (wbmp);
 module (xbm);
 module (tga);
 module (pcx);
+module (icns);
+module (jasper);
+module (qtif);
+module (gdip_ico);
+module (gdip_wmf);
+module (gdip_emf);
+module (gdip_bmp);
+module (gdip_gif);
+module (gdip_jpeg);
+module (gdip_png);
+module (gdip_tiff);
+
+#undef module
 
-gboolean
-_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
-                         GError         **error)
+/* actually load the image handler - gdk_pixbuf_get_module only get a */
+/* reference to the module to load, it doesn't actually load it       */
+/* perhaps these actions should be combined in one function           */
+static gboolean
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+                                GError         **error)
 {
        GdkPixbufModuleFillInfoFunc fill_info = NULL;
         GdkPixbufModuleFillVtableFunc fill_vtable = NULL;
-
-       image_module->module = (void *) 1;
-
-        if (FALSE) {
-                /* Ugly hack so we can use else if unconditionally below ;-) */
-        }
-        
-#ifdef INCLUDE_png     
-       else if (strcmp (image_module->module_name, "png") == 0) {
-                fill_info = MODULE_ENTRY (png, fill_info);
-                fill_vtable = MODULE_ENTRY (png, fill_vtable);
+               
+        if (image_module->module != NULL)
+               return TRUE;
+
+#define try_module(format,id)                                          \
+       if (fill_info == NULL &&                                        \
+           strcmp (image_module->module_name, #format) == 0) {         \
+                fill_info = _gdk_pixbuf__##id##_fill_info;             \
+                fill_vtable = _gdk_pixbuf__##id##_fill_vtable; \
        }
+#ifdef INCLUDE_png     
+       try_module (png,png);
 #endif
-
-#ifdef INCLUDE_bmp     
-       else if (strcmp (image_module->module_name, "bmp") == 0) {
-                fill_info = MODULE_ENTRY (bmp, fill_info);
-                fill_vtable = MODULE_ENTRY (bmp, fill_vtable);
-       }
+#ifdef INCLUDE_bmp
+       try_module (bmp,bmp);
 #endif
-
 #ifdef INCLUDE_wbmp
-       else if (strcmp (image_module->module_name, "wbmp") == 0) {
-                fill_info = MODULE_ENTRY (wbmp, fill_info);
-                fill_vtable = MODULE_ENTRY (wbmp, fill_vtable);
-       }
+       try_module (wbmp,wbmp);
 #endif
-
 #ifdef INCLUDE_gif
-       else if (strcmp (image_module->module_name, "gif") == 0) {
-                fill_info = MODULE_ENTRY (gif, fill_info);
-                fill_vtable = MODULE_ENTRY (gif, fill_vtable);
-       }
+       try_module (gif,gif);
 #endif
-
 #ifdef INCLUDE_ico
-       else if (strcmp (image_module->module_name, "ico") == 0) {
-                fill_info = MODULE_ENTRY (ico, fill_info);
-                fill_vtable = MODULE_ENTRY (ico, fill_vtable);
-       }
+       try_module (ico,ico);
 #endif
-
 #ifdef INCLUDE_ani
-       else if (strcmp (image_module->module_name, "ani") == 0) {
-                fill_info = MODULE_ENTRY (ani, fill_info);
-                fill_vtable = MODULE_ENTRY (ani, fill_vtable);
-       }
+       try_module (ani,ani);
 #endif
-
 #ifdef INCLUDE_jpeg
-       else if (strcmp (image_module->module_name, "jpeg") == 0) {
-                fill_info = MODULE_ENTRY (jpeg, fill_info);
-                fill_vtable = MODULE_ENTRY (jpeg, fill_vtable);
-       }
+       try_module (jpeg,jpeg);
 #endif
-
 #ifdef INCLUDE_pnm
-       else if (strcmp (image_module->module_name, "pnm") == 0) {
-                fill_info = MODULE_ENTRY (pnm, fill_info);
-                fill_vtable = MODULE_ENTRY (pnm, fill_vtable);
-       }
+       try_module (pnm,pnm);
 #endif
-
 #ifdef INCLUDE_ras
-       else if (strcmp (image_module->module_name, "ras") == 0) {
-                fill_info = MODULE_ENTRY (ras, fill_info);
-                fill_vtable = MODULE_ENTRY (ras, fill_vtable);
-       }
+       try_module (ras,ras);
 #endif
-
 #ifdef INCLUDE_tiff
-       else if (strcmp (image_module->module_name, "tiff") == 0) {
-                fill_info = MODULE_ENTRY (tiff, fill_info);
-                fill_vtable = MODULE_ENTRY (tiff, fill_vtable);
-       }
+       try_module (tiff,tiff);
 #endif
-
 #ifdef INCLUDE_xpm
-       else if (strcmp (image_module->module_name, "xpm") == 0) {
-                fill_info = MODULE_ENTRY (xpm, fill_info);
-                fill_vtable = MODULE_ENTRY (xpm, fill_vtable);
-       }
+       try_module (xpm,xpm);
 #endif
-
 #ifdef INCLUDE_xbm
-       else if (strcmp (image_module->module_name, "xbm") == 0) {
-                fill_info = MODULE_ENTRY (xbm, fill_info);
-                fill_vtable = MODULE_ENTRY (xbm, fill_vtable);
-       }
+       try_module (xbm,xbm);
 #endif
-
 #ifdef INCLUDE_tga
-       else if (strcmp (image_module->module_name, "tga") == 0) {
-                fill_info = MODULE_ENTRY (tga, fill_info);
-               fill_vtable = MODULE_ENTRY (tga, fill_vtable);
-       }
+       try_module (tga,tga);
 #endif
-        
 #ifdef INCLUDE_pcx
-       else if (strcmp (image_module->module_name, "pcx") == 0) {
-                fill_info = MODULE_ENTRY (pcx, fill_info);
-               fill_vtable = MODULE_ENTRY (pcx, fill_vtable);
-       }
+       try_module (pcx,pcx);
+#endif
+#ifdef INCLUDE_icns
+       try_module (icns,icns);
+#endif
+#ifdef INCLUDE_jasper
+       try_module (jasper,jasper);
+#endif
+#ifdef INCLUDE_qtif
+       try_module (qtif,qtif);
 #endif
+#ifdef INCLUDE_gdiplus
+       try_module (ico,gdip_ico);
+       try_module (wmf,gdip_wmf);
+       try_module (emf,gdip_emf);
+       try_module (bmp,gdip_bmp);
+       try_module (gif,gdip_gif);
+       try_module (jpeg,gdip_jpeg);
+       try_module (tiff,gdip_tiff);
+#endif
+#ifdef INCLUDE_gdip_png
+       try_module (png,gdip_png);
+#endif
+
+#undef try_module
         
         if (fill_vtable) {
+               image_module->module = (void *) 1;
                 (* fill_vtable) (image_module);
-               image_module->info = g_new0 (GdkPixbufFormat, 1);
-               (* fill_info) (image_module->info);
-
+               if (image_module->info == NULL) {
+                       image_module->info = g_new0 (GdkPixbufFormat, 1);
+                       (* fill_info) (image_module->info);
+               }
                 return TRUE;
-        } else {
-                g_set_error (error,
-                             GDK_PIXBUF_ERROR,
-                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                             _("Image type '%s' is not supported"),
-                             image_module->module_name);
-
-                return FALSE;
-        }
+       }
+       else 
+#ifdef USE_GMODULE
+       {
+               char *path;
+               GModule *module;
+               gpointer sym;
+
+               path = image_module->module_path;
+               module = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+
+               if (!module) {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_FAILED,
+                                    _("Unable to load image-loading module: %s: %s"),
+                                    path, g_module_error ());
+                       return FALSE;
+               }
+
+               image_module->module = module;        
+        
+               if (g_module_symbol (module, "fill_vtable", &sym)) {
+                       fill_vtable = (GdkPixbufModuleFillVtableFunc) sym;
+                       (* fill_vtable) (image_module);
+                       return TRUE;
+               } else {
+                       g_set_error (error,
+                                    GDK_PIXBUF_ERROR,
+                                    GDK_PIXBUF_ERROR_FAILED,
+                                    _("Image-loading module %s does not export the proper interface; perhaps it's from a different GTK version?"),
+                                    path);
+                       return FALSE;
+               }
+       }
+#else
+       g_set_error (error,
+                    GDK_PIXBUF_ERROR,
+                    GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+                    _("Image type '%s' is not supported"),
+                    image_module->module_name);
+       return FALSE;
+#endif  /* !USE_GMODULE */
 }
 
-static void 
-gdk_pixbuf_io_init ()
+
+gboolean
+_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
+                        GError         **error)
 {
-       gchar *included_formats[] = { 
-               "ani", "png", "bmp", "wbmp", "gif", 
-               "ico", "jpeg", "pnm", "ras", "tiff", 
-               "xpm", "xbm", "tga", "pcx",
-               NULL
-       };
-       gchar **name;
-       GdkPixbufModule *module = NULL;
+       gboolean ret;
+       gboolean locked = FALSE;
 
-       for (name = included_formats; *name; name++) {
-               module = g_new0 (GdkPixbufModule, 1);
-               module->module_name = *name;
-               if (_gdk_pixbuf_load_module (module, NULL))
-                       file_formats = g_slist_prepend (file_formats, module);
-               else
-                       g_free (module);
+       /* be extra careful, maybe the module initializes
+        * the thread system
+        */
+       if (g_threads_got_initialized) {
+               G_LOCK (init_lock);
+               locked = TRUE;
        }
-}
 
-#endif  /* !USE_GMODULE */
+        ret = gdk_pixbuf_load_module_unlocked (image_module, error);
+
+       if (locked)
+               G_UNLOCK (init_lock);
+
+       return ret;
+}
 
 \f
 
@@ -709,9 +800,40 @@ _gdk_pixbuf_get_module (guchar *buffer, guint size,
 {
        GSList *modules;
 
-       gint score, best = 0;
        GdkPixbufModule *selected = NULL;
        gchar *display_name = NULL;
+#ifdef GDK_PIXBUF_USE_GIO_MIME
+       gchar *mime_type;
+       gchar **mimes;
+       gchar *type;
+       gint j;
+       gboolean uncertain;
+
+       mime_type = g_content_type_guess (NULL, buffer, size, &uncertain);
+       if (uncertain)
+               mime_type = g_content_type_guess (filename, buffer, size, NULL);
+
+       for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
+               GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
+               GdkPixbufFormat *info = module->info;
+
+               if (info->disabled)
+                       continue;
+
+               mimes = info->mime_types;
+               for (j = 0; mimes[j] != NULL; j++) {
+                       type = g_content_type_from_mime_type (mimes[j]);
+                       if (g_ascii_strcasecmp (type, mime_type) == 0) {
+                               g_free (type);
+                               selected = module;
+                               break;
+                       }
+                       g_free (type);
+               }
+       }
+       g_free (mime_type);
+#else
+       gint score, best = 0;
 
        for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
                GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
@@ -727,6 +849,8 @@ _gdk_pixbuf_get_module (guchar *buffer, guint size,
                if (score >= 100) 
                        break;
        }
+#endif
+
        if (selected != NULL)
                return selected;
 
@@ -741,10 +865,10 @@ _gdk_pixbuf_get_module (guchar *buffer, guint size,
                g_free (display_name);
        }
         else
-                g_set_error (error,
-                             GDK_PIXBUF_ERROR,
-                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                             _("Unrecognized image file format"));
+                g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+                                     _("Unrecognized image file format"));
 
 
        return NULL;
@@ -766,7 +890,7 @@ _gdk_pixbuf_generic_image_load (GdkPixbufModule *module,
                                FILE *f,
                                GError **error)
 {
-       guchar buffer[4096];
+       guchar buffer[LOAD_BUFFER_SIZE];
        size_t length;
        GdkPixbuf *pixbuf = NULL;
        GdkPixbufAnimation *animation = NULL;
@@ -840,7 +964,7 @@ gdk_pixbuf_new_from_file (const char *filename,
        GdkPixbuf *pixbuf;
        int size;
        FILE *f;
-       guchar buffer[256];
+       guchar buffer[SNIFF_BUFFER_SIZE];
        GdkPixbufModule *image_module;
        gchar *display_name;
 
@@ -851,12 +975,13 @@ gdk_pixbuf_new_from_file (const char *filename,
 
        f = g_fopen (filename, "rb");
        if (!f) {
+               gint save_errno = errno;
                 g_set_error (error,
                              G_FILE_ERROR,
-                             g_file_error_from_errno (errno),
+                             g_file_error_from_errno (save_errno),
                              _("Failed to open file '%s': %s"),
                              display_name,
-                             g_strerror (errno));
+                             g_strerror (save_errno));
                 g_free (display_name);
                return NULL;
         }
@@ -880,12 +1005,11 @@ gdk_pixbuf_new_from_file (const char *filename,
                 return NULL;
         }
 
-       if (image_module->module == NULL)
-                if (!_gdk_pixbuf_load_module (image_module, error)) {
-                       g_free (display_name);
-                        fclose (f);
-                        return NULL;
-                }
+        if (!_gdk_pixbuf_load_module (image_module, error)) {
+               g_free (display_name);
+               fclose (f);
+               return NULL;
+        }
 
        fseek (f, 0, SEEK_SET);
        pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
@@ -944,49 +1068,6 @@ gdk_pixbuf_new_from_file (const char *filename,
 }
 #endif
 
-static void
-size_prepared_cb (GdkPixbufLoader *loader, 
-                 int              width,
-                 int              height,
-                 gpointer         data)
-{
-       struct {
-               gint width;
-               gint height;
-               gboolean preserve_aspect_ratio;
-       } *info = data;
-
-       g_return_if_fail (width > 0 && height > 0);
-
-       if (info->preserve_aspect_ratio && 
-           (info->width > 0 || info->height > 0)) {
-               if (info->width < 0)
-               {
-                       width = width * (double)info->height/(double)height;
-                       height = info->height;
-               }
-               else if (info->height < 0)
-               {
-                       height = height * (double)info->width/(double)width;
-                       width = info->width;
-               }
-               else if ((double)height * (double)info->width >
-                        (double)width * (double)info->height) {
-                       width = 0.5 + (double)width * (double)info->height / (double)height;
-                       height = info->height;
-               } else {
-                       height = 0.5 + (double)height * (double)info->width / (double)width;
-                       width = info->width;
-               }
-       } else {
-               if (info->width > 0)
-                       width = info->width;
-               if (info->height > 0)
-                       height = info->height;
-       }
-       
-       gdk_pixbuf_loader_set_size (loader, width, height);
-}
 
 /**
  * gdk_pixbuf_new_from_file_at_size:
@@ -995,11 +1076,16 @@ size_prepared_cb (GdkPixbufLoader *loader,
  * @height: The height the image should have or -1 to not constrain the height
  * @error: Return location for an error
  *
- * Creates a new pixbuf by loading an image from a file.  The file format is
- * detected automatically. If %NULL is returned, then @error will be set.
- * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
+ * Creates a new pixbuf by loading an image from a file.  
+ * The file format is detected automatically. If %NULL is returned, then 
+ * @error will be set. Possible errors are in the #GDK_PIXBUF_ERROR and 
+ * #G_FILE_ERROR domains.
+ *
  * The image will be scaled to fit in the requested size, preserving
- * the image's aspect ratio.
+ * the image's aspect ratio. Note that the returned pixbuf may be smaller
+ * than @width x @height, if the aspect ratio requires it. To load
+ * and image at the requested size, regardless of aspect ratio, use
+ * gdk_pixbuf_new_from_file_at_scale().
  *
  * Return value: A newly-created pixbuf with a reference count of 1, or 
  * %NULL if any of several error conditions occurred:  the file could not 
@@ -1047,6 +1133,55 @@ gdk_pixbuf_new_from_file_at_size (const char *filename,
 }
 #endif
 
+typedef        struct {
+       gint width;
+       gint height;
+       gboolean preserve_aspect_ratio;
+} AtScaleData; 
+
+static void
+at_scale_size_prepared_cb (GdkPixbufLoader *loader, 
+                          int              width,
+                          int              height,
+                          gpointer         data)
+{
+       AtScaleData *info = data;
+
+       g_return_if_fail (width > 0 && height > 0);
+
+       if (info->preserve_aspect_ratio && 
+           (info->width > 0 || info->height > 0)) {
+               if (info->width < 0)
+               {
+                       width = width * (double)info->height/(double)height;
+                       height = info->height;
+               }
+               else if (info->height < 0)
+               {
+                       height = height * (double)info->width/(double)width;
+                       width = info->width;
+               }
+               else if ((double)height * (double)info->width >
+                        (double)width * (double)info->height) {
+                       width = 0.5 + (double)width * (double)info->height / (double)height;
+                       height = info->height;
+               } else {
+                       height = 0.5 + (double)height * (double)info->width / (double)width;
+                       width = info->width;
+               }
+       } else {
+               if (info->width > 0)
+                       width = info->width;
+               if (info->height > 0)
+                       height = info->height;
+       }
+       
+       width = MAX (width, 1);
+        height = MAX (height, 1);
+
+       gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
 /**
  * gdk_pixbuf_new_from_file_at_scale:
  * @filename: Name of file to load, in the GLib file name encoding
@@ -1085,15 +1220,13 @@ gdk_pixbuf_new_from_file_at_scale (const char *filename,
 
        GdkPixbufLoader *loader;
        GdkPixbuf       *pixbuf;
-
-       guchar buffer [4096];
+       guchar buffer[LOAD_BUFFER_SIZE];
        int length;
        FILE *f;
-       struct {
-               gint width;
-               gint height;
-               gboolean preserve_aspect_ratio;
-       } info;
+       AtScaleData info;
+       GdkPixbufAnimation *animation;
+       GdkPixbufAnimationIter *iter;
+       gboolean has_frame;
 
        g_return_val_if_fail (filename != NULL, NULL);
         g_return_val_if_fail (width > 0 || width == -1, NULL);
@@ -1101,13 +1234,14 @@ gdk_pixbuf_new_from_file_at_scale (const char *filename,
 
        f = g_fopen (filename, "rb");
        if (!f) {
+               gint save_errno = errno;
                 gchar *display_name = g_filename_display_name (filename);
                 g_set_error (error,
                              G_FILE_ERROR,
-                             g_file_error_from_errno (errno),
+                             g_file_error_from_errno (save_errno),
                              _("Failed to open file '%s': %s"),
                              display_name,
-                             g_strerror (errno));
+                             g_strerror (save_errno));
                 g_free (display_name);
                return NULL;
         }
@@ -1118,9 +1252,11 @@ gdk_pixbuf_new_from_file_at_scale (const char *filename,
        info.height = height;
         info.preserve_aspect_ratio = preserve_aspect_ratio;
 
-       g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info);
+       g_signal_connect (loader, "size-prepared", 
+                         G_CALLBACK (at_scale_size_prepared_cb), &info);
 
-       while (!feof (f) && !ferror (f)) {
+       has_frame = FALSE;
+       while (!has_frame && !feof (f) && !ferror (f)) {
                length = fread (buffer, 1, sizeof (buffer), f);
                if (length > 0)
                        if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
@@ -1129,11 +1265,20 @@ gdk_pixbuf_new_from_file_at_scale (const char *filename,
                                g_object_unref (loader);
                                return NULL;
                        }
+               
+               animation = gdk_pixbuf_loader_get_animation (loader);
+               if (animation) {
+                       iter = gdk_pixbuf_animation_get_iter (animation, NULL);
+                       if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
+                               has_frame = TRUE;
+                       }
+                       g_object_unref (iter);
+               }
        }
 
        fclose (f);
 
-       if (!gdk_pixbuf_loader_close (loader, error)) {
+       if (!gdk_pixbuf_loader_close (loader, error) && !has_frame) {
                g_object_unref (loader);
                return NULL;
        }
@@ -1189,6 +1334,157 @@ gdk_pixbuf_new_from_file_at_scale (const char *filename,
 #endif
 
 
+static GdkPixbuf *
+load_from_stream (GdkPixbufLoader  *loader,
+                 GInputStream     *stream,
+                 GCancellable     *cancellable,
+                 GError          **error)
+{
+       GdkPixbuf *pixbuf;
+       gssize n_read;
+       guchar buffer[LOAD_BUFFER_SIZE];
+       gboolean res;
+
+       res = TRUE;
+       while (1) { 
+               n_read = g_input_stream_read (stream, 
+                                             buffer, 
+                                             sizeof (buffer), 
+                                             cancellable, 
+                                             error);
+               if (n_read < 0) {
+                       res = FALSE;
+                       error = NULL; /* Ignore further errors */
+                       break;
+               }
+
+               if (n_read == 0)
+                       break;
+
+               if (!gdk_pixbuf_loader_write (loader, 
+                                             buffer, 
+                                             n_read, 
+                                             error)) {
+                       res = FALSE;
+                       error = NULL;
+                       break;
+               }
+       }
+
+       if (!gdk_pixbuf_loader_close (loader, error)) {
+               res = FALSE;
+               error = NULL;
+       }
+
+       pixbuf = NULL;
+       if (res) {
+               pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+               if (pixbuf)
+                       g_object_ref (pixbuf);
+       }
+
+       return pixbuf;
+}
+
+
+/**
+ * gdk_pixbuf_new_from_stream_at_scale:
+ * @stream:  a #GInputStream to load the pixbuf from
+ * @width: The width the image should have or -1 to not constrain the width
+ * @height: The height the image should have or -1 to not constrain the height
+ * @preserve_aspect_ratio: %TRUE to preserve the image's aspect ratio
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: Return location for an error
+ *
+ * Creates a new pixbuf by loading an image from an input stream.  
+ *
+ * The file format is detected automatically. If %NULL is returned, then 
+ * @error will be set. The @cancellable can be used to abort the operation
+ * from another thread. If the operation was cancelled, the error 
+ * %GIO_ERROR_CANCELLED will be returned. Other possible errors are in 
+ * the #GDK_PIXBUF_ERROR and %G_IO_ERROR domains. 
+ *
+ * The image will be scaled to fit in the requested size, optionally 
+ * preserving the image's aspect ratio. When preserving the aspect ratio, 
+ * a @width of -1 will cause the image to be scaled to the exact given 
+ * height, and a @height of -1 will cause the image to be scaled to the 
+ * exact given width. When not preserving aspect ratio, a @width or 
+ * @height of -1 means to not scale the image at all in that dimension.
+ *
+ * The stream is not closed.
+ *
+ * Return value: A newly-created pixbuf, or %NULL if any of several error 
+ * conditions occurred: the file could not be opened, the image format is 
+ * not supported, there was not enough memory to allocate the image buffer, 
+ * the stream contained invalid data, or the operation was cancelled.
+ *
+ * Since: 2.14
+ */
+GdkPixbuf *
+gdk_pixbuf_new_from_stream_at_scale (GInputStream  *stream,
+                                    gint           width,
+                                    gint           height,
+                                    gboolean       preserve_aspect_ratio,
+                                    GCancellable  *cancellable,
+                                    GError       **error)
+{
+       GdkPixbufLoader *loader;
+       GdkPixbuf *pixbuf;
+       AtScaleData info;
+
+       loader = gdk_pixbuf_loader_new ();
+
+       info.width = width;
+       info.height = height;
+        info.preserve_aspect_ratio = preserve_aspect_ratio;
+
+       g_signal_connect (loader, "size-prepared", 
+                         G_CALLBACK (at_scale_size_prepared_cb), &info);
+
+       pixbuf = load_from_stream (loader, stream, cancellable, error);
+       g_object_unref (loader);
+
+       return pixbuf;
+}
+
+/**
+ * gdk_pixbuf_new_from_stream:
+ * @stream:  a #GInputStream to load the pixbuf from
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: Return location for an error
+ *
+ * Creates a new pixbuf by loading an image from an input stream.  
+ *
+ * The file format is detected automatically. If %NULL is returned, then 
+ * @error will be set. The @cancellable can be used to abort the operation
+ * from another thread. If the operation was cancelled, the error 
+ * %GIO_ERROR_CANCELLED will be returned. Other possible errors are in 
+ * the #GDK_PIXBUF_ERROR and %G_IO_ERROR domains. 
+ *
+ * The stream is not closed.
+ *
+ * Return value: A newly-created pixbuf, or %NULL if any of several error 
+ * conditions occurred: the file could not be opened, the image format is 
+ * not supported, there was not enough memory to allocate the image buffer, 
+ * the stream contained invalid data, or the operation was cancelled.
+ *
+ * Since: 2.14
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_stream (GInputStream  *stream,
+                           GCancellable  *cancellable,
+                           GError       **error)
+{
+       GdkPixbuf *pixbuf;
+       GdkPixbufLoader *loader;
+
+       loader = gdk_pixbuf_loader_new ();
+       pixbuf = load_from_stream (loader, stream, cancellable, error);
+       g_object_unref (loader);
+
+       return pixbuf;
+}
+
 static void
 info_cb (GdkPixbufLoader *loader, 
         int              width,
@@ -1230,7 +1526,7 @@ gdk_pixbuf_get_file_info (const gchar  *filename,
                          gint         *height)
 {
        GdkPixbufLoader *loader;
-       guchar buffer [4096];
+       guchar buffer[SNIFF_BUFFER_SIZE];
        int length;
        FILE *f;
        struct {
@@ -1293,6 +1589,8 @@ gdk_pixbuf_new_from_xpm_data (const char **data)
        GdkPixbufModule *xpm_module;
        gboolean locked;
 
+       g_return_val_if_fail (data != NULL, NULL);
+
        xpm_module = _gdk_pixbuf_get_named_module ("xpm", &error);
        if (xpm_module == NULL) {
                g_warning ("Error loading XPM image loader: %s", error->message);
@@ -1300,12 +1598,10 @@ gdk_pixbuf_new_from_xpm_data (const char **data)
                return NULL;
        }
 
-       if (xpm_module->module == NULL) {
-                if (!_gdk_pixbuf_load_module (xpm_module, &error)) {
-                        g_warning ("Error loading XPM image loader: %s", error->message);
-                        g_error_free (error);
-                        return NULL;
-                }
+        if (!_gdk_pixbuf_load_module (xpm_module, &error)) {
+                g_warning ("Error loading XPM image loader: %s", error->message);
+                g_error_free (error);
+                return NULL;
         }
 
        locked = _gdk_pixbuf_lock (xpm_module);
@@ -1370,11 +1666,12 @@ save_to_file_callback (const gchar *buf,
 
        n = fwrite (buf, 1, count, filehandle);
        if (n != count) {
+               gint save_errno = errno;
                 g_set_error (error,
                              G_FILE_ERROR,
-                             g_file_error_from_errno (errno),
+                             g_file_error_from_errno (save_errno),
                              _("Error writing to image file: %s"),
-                             g_strerror (errno));
+                             g_strerror (save_errno));
                 return FALSE;
        }
        return TRUE;
@@ -1397,9 +1694,8 @@ gdk_pixbuf_real_save (GdkPixbuf     *pixbuf,
        if (image_module == NULL)
                return FALSE;
        
-       if (image_module->module == NULL)
-               if (!_gdk_pixbuf_load_module (image_module, error))
-                       return FALSE;
+       if (!_gdk_pixbuf_load_module (image_module, error))
+               return FALSE;
 
        locked = _gdk_pixbuf_lock (image_module);
 
@@ -1450,10 +1746,10 @@ save_to_callback_with_tmp_file (GdkPixbufModule   *image_module,
 
        buf = g_try_malloc (TMP_FILE_BUF_SIZE);
        if (buf == NULL) {
-               g_set_error (error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                            _("Insufficient memory to save image to callback"));
+               g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                     _("Insufficient memory to save image to callback"));
                goto end;
        }
 
@@ -1462,10 +1758,11 @@ save_to_callback_with_tmp_file (GdkPixbufModule   *image_module,
                goto end;
        f = fdopen (fd, "wb+");
        if (f == NULL) {
-               g_set_error (error,
-                            G_FILE_ERROR,
-                            g_file_error_from_errno (errno),
-                            _("Failed to open temporary file"));
+               gint save_errno = errno;
+               g_set_error_literal (error,
+                                     G_FILE_ERROR,
+                                     g_file_error_from_errno (save_errno),
+                                     _("Failed to open temporary file"));
                goto end;
        }
 
@@ -1487,10 +1784,11 @@ save_to_callback_with_tmp_file (GdkPixbufModule   *image_module,
                        break;
        }
        if (ferror (f)) {
-               g_set_error (error,
-                            G_FILE_ERROR,
-                            g_file_error_from_errno (errno),
-                            _("Failed to read from temporary file"));
+               gint save_errno = errno;
+               g_set_error_literal (error,
+                                     G_FILE_ERROR,
+                                     g_file_error_from_errno (save_errno),
+                                     _("Failed to read from temporary file"));
                goto end;
        }
        retval = TRUE;
@@ -1526,9 +1824,8 @@ gdk_pixbuf_real_save_to_callback (GdkPixbuf         *pixbuf,
        if (image_module == NULL)
                return FALSE;
        
-       if (image_module->module == NULL)
-               if (!_gdk_pixbuf_load_module (image_module, error))
-                       return FALSE;
+       if (!_gdk_pixbuf_load_module (image_module, error))
+               return FALSE;
 
        locked = _gdk_pixbuf_lock (image_module);
 
@@ -1572,18 +1869,18 @@ gdk_pixbuf_real_save_to_callback (GdkPixbuf         *pixbuf,
  * installed. The list of all writable formats can be determined in the 
  * following way:
  *
- * <informalexample><programlisting>
+ * |[
  * void add_if_writable (GdkPixbufFormat *data, GSList **list)
  * {
  *   if (gdk_pixbuf_format_is_writable (data))
  *     *list = g_slist_prepend (*list, data);
  * }
- * <!-- -->
- * GSList *formats = gdk_pixbuf_get_formats (<!-- -->);
+ * 
+ * GSList *formats = gdk_pixbuf_get_formats ();
  * GSList *writable_formats = NULL;
- * g_slist_foreach (formats, add_if_writable, &amp;writable_formats);
+ * g_slist_foreach (formats, add_if_writable, &writable_formats);
  * g_slist_free (formats);
- * </programlisting></informalexample>
+ * ]|
  *
  * If @error is set, %FALSE will be returned. Possible errors include 
  * those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
@@ -1605,6 +1902,25 @@ gdk_pixbuf_real_save_to_callback (GdkPixbuf         *pixbuf,
  * be specified using the "compression" parameter; it's value is in an
  * integer in the range of [0,9].
  *
+ * ICC color profiles can also be embedded into PNG images.
+ * The "icc-profile" value should be the complete ICC profile encoded
+ * into base64.
+ *
+ * <informalexample><programlisting>
+ * gchar *contents;
+ * gchar *contents_encode;
+ * gsize length;
+ * g_file_get_contents ("/home/hughsie/.color/icc/L225W.icm", &contents, &length, NULL);
+ * contents_encode = g_base64_encode ((const guchar *) contents, length);
+ * gdk_pixbuf_save (pixbuf, handle, "png", &amp;error,
+ *                  "icc-profile", contents_encode,
+ *                  NULL);
+ * </programlisting></informalexample>
+ *
+ * TIFF images recognize a "compression" option which acceps an integer value.
+ * Among the codecs are 1 None, 2 Huffman, 5 LZW, 7 JPEG and 8 Deflate, see
+ * the libtiff documentation and tiff.h for all supported codec values.
+ *
  * ICO images can be saved in depth 16, 24, or 32, by using the "depth"
  * parameter. When the ICO saver is given "x_hot" and "y_hot" parameters,
  * it produces a CUR instead of an ICO.
@@ -1695,7 +2011,7 @@ gdk_pixbuf_save (GdkPixbuf  *pixbuf,
  * @option_values: values for named options
  * @error: return location for error, or %NULL
  *
- * Saves pixbuf to a file in @type, which is currently "jpeg", "png", "ico" or "bmp".
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png", "tiff", "ico" or "bmp".
  * If @error is set, %FALSE will be returned. 
  * See gdk_pixbuf_save () for more details.
  *
@@ -1720,13 +2036,14 @@ gdk_pixbuf_savev (GdkPixbuf  *pixbuf,
         f = g_fopen (filename, "wb");
         
         if (f == NULL) {
+               gint save_errno = errno;
                 gchar *display_name = g_filename_display_name (filename);
                 g_set_error (error,
                              G_FILE_ERROR,
-                             g_file_error_from_errno (errno),
+                             g_file_error_from_errno (save_errno),
                              _("Failed to open '%s' for writing: %s"),
                              display_name,
-                             g_strerror (errno));
+                             g_strerror (save_errno));
                 g_free (display_name);
                 return FALSE;
         }
@@ -1744,13 +2061,14 @@ gdk_pixbuf_savev (GdkPixbuf  *pixbuf,
        }
 
        if (fclose (f) < 0) {
+              gint save_errno = errno;
                gchar *display_name = g_filename_display_name (filename);
                g_set_error (error,
                             G_FILE_ERROR,
-                            g_file_error_from_errno (errno),
+                            g_file_error_from_errno (save_errno),
                             _("Failed to close '%s' while writing image, all data may not have been saved: %s"),
                             display_name,
-                            g_strerror (errno));
+                            g_strerror (save_errno));
                g_free (display_name);
                return FALSE;
        }
@@ -1856,7 +2174,7 @@ gdk_pixbuf_save_to_callback    (GdkPixbuf  *pixbuf,
  * @error: return location for error, or %NULL
  *
  * Saves pixbuf to a callback in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp".  If @error is set, %FALSE will be returned. See
+ * "png", "tiff", "ico" or "bmp".  If @error is set, %FALSE will be returned. See
  * gdk_pixbuf_save_to_callback () for more details.
  *
  * Return value: whether an error was set
@@ -1902,10 +2220,10 @@ gdk_pixbuf_save_to_callbackv   (GdkPixbuf  *pixbuf,
  * @Varargs: list of key-value save options
  *
  * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp".  This is a convenience function that uses
+ * "png", "tiff", "ico" or "bmp".  This is a convenience function that uses
  * gdk_pixbuf_save_to_callback() to do the real work. Note that the buffer 
  * is not nul-terminated and may contain embedded  nuls.
- * If @error is set, %FALSE will be returned and @string will be set to
+ * If @error is set, %FALSE will be returned and @buffer will be set to
  * %NULL. Possible errors include those in the #GDK_PIXBUF_ERROR
  * domain.
  *
@@ -1965,10 +2283,10 @@ save_to_buffer_callback (const gchar *data,
                new_max = MAX (sdata->max*2, sdata->len + count);
                new_buffer = g_try_realloc (sdata->buffer, new_max);
                if (!new_buffer) {
-                       g_set_error (error,
-                                    GDK_PIXBUF_ERROR,
-                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                    _("Insufficient memory to save image into a buffer"));
+                       g_set_error_literal (error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                             _("Insufficient memory to save image into a buffer"));
                        return FALSE;
                }
                sdata->buffer = new_buffer;
@@ -1990,7 +2308,8 @@ save_to_buffer_callback (const gchar *data,
  * @error: return location for error, or %NULL
  *
  * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp".  See gdk_pixbuf_save_to_buffer() for more details.
+ * "tiff", "png", "ico" or "bmp".  See gdk_pixbuf_save_to_buffer() 
+ * for more details.
  *
  * Return value: whether an error was set
  *
@@ -2015,10 +2334,10 @@ gdk_pixbuf_save_to_bufferv     (GdkPixbuf  *pixbuf,
        sdata.max = initial_max;
        sdata.len = 0;
        if (!sdata.buffer) {
-                g_set_error (error,
-                             GDK_PIXBUF_ERROR,
-                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                            _("Insufficient memory to save image into a buffer"));
+                g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                     _("Insufficient memory to save image into a buffer"));
                return FALSE;
        }
 
@@ -2035,6 +2354,105 @@ gdk_pixbuf_save_to_bufferv     (GdkPixbuf  *pixbuf,
        return TRUE;
 }
 
+typedef struct {
+       GOutputStream *stream;
+       GCancellable  *cancellable;
+} SaveToStreamData;
+
+static gboolean
+save_to_stream (const gchar  *buffer,
+               gsize         count,
+               GError      **error,
+               gpointer      data)
+{
+       SaveToStreamData *sdata = (SaveToStreamData *)data;
+       gsize remaining;
+       gssize written;
+        GError *my_error = NULL;
+
+       remaining = count;
+       written = 0;
+       while (remaining > 0) {
+               buffer += written;
+               remaining -= written;
+               written = g_output_stream_write (sdata->stream, 
+                                                buffer, remaining, 
+                                                sdata->cancellable, 
+                                                &my_error);
+               if (written < 0) {
+                       if (!my_error) {
+                               g_set_error_literal (error,
+                                                     G_IO_ERROR, 0,
+                                                     _("Error writing to image stream"));
+                       }
+                       else {
+                               g_propagate_error (error, my_error);
+                       }
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+/** 
+ * gdk_pixbuf_save_to_stream:
+ * @pixbuf: a #GdkPixbuf
+ * @stream: a #GOutputStream to save the pixbuf to
+ * @type: name of file format
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves @pixbuf to an output stream.
+ *
+ * Supported file formats are currently "jpeg", "tiff", "png", "ico" or 
+ * "bmp". See gdk_pixbuf_save_to_buffer() for more details.
+ *
+ * The @cancellable can be used to abort the operation from another 
+ * thread. If the operation was cancelled, the error %GIO_ERROR_CANCELLED 
+ * will be returned. Other possible errors are in the #GDK_PIXBUF_ERROR 
+ * and %G_IO_ERROR domains. 
+ *
+ * The stream is not closed.
+ *
+ * Returns: %TRUE if the pixbuf was saved successfully, %FALSE if an
+ *     error was set.
+ *
+ * Since: 2.14
+ */
+gboolean
+gdk_pixbuf_save_to_stream (GdkPixbuf      *pixbuf,
+                          GOutputStream  *stream,
+                          const char     *type,
+                          GCancellable   *cancellable,
+                          GError        **error,
+                          ...)
+{
+       gboolean res;
+       gchar **keys = NULL;
+       gchar **values = NULL;
+       va_list args;
+       SaveToStreamData data;
+
+       va_start (args, error);
+       collect_save_options (args, &keys, &values);
+       va_end (args);
+
+       data.stream = stream;
+       data.cancellable = cancellable;
+
+       res = gdk_pixbuf_save_to_callbackv (pixbuf, save_to_stream, 
+                                           &data, type, 
+                                           keys, values, 
+                                           error);
+
+       g_strfreev (keys);
+       g_strfreev (values);
+
+       return res;
+}
+
 /**
  * gdk_pixbuf_format_get_name:
  * @format: a #GdkPixbufFormat
@@ -2067,14 +2485,14 @@ gchar *
 gdk_pixbuf_format_get_description (GdkPixbufFormat *format)
 {
        gchar *domain;
-       gchar *description;
+       const gchar *description;
        g_return_val_if_fail (format != NULL, NULL);
 
        if (format->domain != NULL) 
                domain = format->domain;
        else 
                domain = GETTEXT_PACKAGE;
-       description = dgettext (domain, format->description);
+       description = g_dgettext (domain, format->description);
 
        return g_strdup (description);
 }
@@ -2201,10 +2619,10 @@ gdk_pixbuf_format_set_disabled (GdkPixbufFormat *format,
  * gdk_pixbuf_format_get_license:
  * @format: a #GdkPixbufFormat
  *
- * Returns information about the license of the image loader
- * for the format. The returned string should be a shorthand for 
- * a wellknown license, e.g. "LGPL", "GPL", "QPL", "GPL/QPL",
- * or "other" to indicate some other license.  
+ * Returns information about the license of the image loader for the format. The
+ * returned string should be a shorthand for a wellknown license, e.g. "LGPL",
+ * "GPL", "QPL", "GPL/QPL", or "other" to indicate some other license.  This
+ * string should be freed with g_free() when it's no longer needed.
  *
  * Returns: a string describing the license of @format. 
  *
@@ -2257,7 +2675,3 @@ gdk_pixbuf_get_formats (void)
 
 #define __GDK_PIXBUF_IO_C__
 #include "gdk-pixbuf-aliasdef.c"
-
-
-
-