+/* -*- mode: C; c-file-style: "linux" -*- */
/* GdkPixbuf library - Main loading interface.
*
* Copyright (C) 1999 The Free Software Foundation
* Federico Mena-Quintero <federico@gimp.org>
*
* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
*
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-#include <config.h>
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
#include <string.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"
-\f
+#include <glib/gstdio.h>
-static gboolean
-pixbuf_check_png (guchar *buffer, int size)
+#ifdef G_OS_WIN32
+#define STRICT
+#include <windows.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)
{
- if (size < 28)
- return FALSE;
+ int i, j;
+ gchar m;
+ GdkPixbufModulePattern *pattern;
+ gboolean anchored;
+ guchar *prefix;
+ gchar *mask;
- if (buffer [0] != 0x89 ||
- buffer [1] != 'P' ||
- buffer [2] != 'N' ||
- buffer [3] != 'G' ||
- buffer [4] != 0x0d ||
- buffer [5] != 0x0a ||
- buffer [6] != 0x1a ||
- buffer [7] != 0x0a)
- return FALSE;
+ for (pattern = module->info->signature; pattern->prefix; pattern++) {
+ if (pattern->mask && pattern->mask[0] == '*') {
+ prefix = (guchar *)pattern->prefix + 1;
+ mask = pattern->mask + 1;
+ anchored = FALSE;
+ }
+ else {
+ prefix = (guchar *)pattern->prefix;
+ mask = pattern->mask;
+ anchored = TRUE;
+ }
+ for (i = 0; i < size; i++) {
+ for (j = 0; i + j < size && prefix[j] != 0; j++) {
+ m = mask ? mask[j] : ' ';
+ if (m == ' ') {
+ if (buffer[i + j] != prefix[j])
+ break;
+ }
+ else if (m == '!') {
+ if (buffer[i + j] == prefix[j])
+ break;
+ }
+ else if (m == 'z') {
+ if (buffer[i + j] != 0)
+ break;
+ }
+ else if (m == 'n') {
+ if (buffer[i + j] == 0)
+ break;
+ }
+ }
- return TRUE;
+ if (prefix[j] == 0)
+ return pattern->relevance;
+
+ if (anchored)
+ break;
+ }
+ }
+ return 0;
+}
+#endif
+
+G_LOCK_DEFINE_STATIC (init_lock);
+G_LOCK_DEFINE_STATIC (threadunsafe_loader_lock);
+
+gboolean
+_gdk_pixbuf_lock (GdkPixbufModule *image_module)
+{
+ if (g_threads_got_initialized &&
+ !(image_module->info->flags & GDK_PIXBUF_FORMAT_THREADSAFE)) {
+ G_LOCK (threadunsafe_loader_lock);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+_gdk_pixbuf_unlock (GdkPixbufModule *image_module)
+{
+ if (!(image_module->info->flags & GDK_PIXBUF_FORMAT_THREADSAFE)) {
+ G_UNLOCK (threadunsafe_loader_lock);
+ }
+}
+
+static GSList *file_formats = NULL;
+
+static void gdk_pixbuf_io_init (void);
+
+static GSList *
+get_file_formats (void)
+{
+ G_LOCK (init_lock);
+ if (file_formats == NULL)
+ gdk_pixbuf_io_init ();
+ G_UNLOCK (init_lock);
+
+ return file_formats;
}
+
+#ifdef USE_GMODULE
+
static gboolean
-pixbuf_check_jpeg (guchar *buffer, int size)
+scan_string (const char **pos, GString *out)
{
- if (size < 10)
+ const char *p = *pos, *q = *pos;
+ char *tmp, *tmp2;
+ gboolean quoted;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ if (!*p)
return FALSE;
+ else if (*p == '"') {
+ p++;
+ quoted = FALSE;
+ for (q = p; (*q != '"') || quoted; q++) {
+ if (!*q)
+ return FALSE;
+ quoted = (*q == '\\') && !quoted;
+ }
+
+ tmp = g_strndup (p, q - p);
+ tmp2 = g_strcompress (tmp);
+ g_string_truncate (out, 0);
+ g_string_append (out, tmp2);
+ g_free (tmp);
+ g_free (tmp2);
+ }
+
+ q++;
+ *pos = q;
+
+ return TRUE;
+}
- if (buffer [0] != 0xff || buffer [1] != 0xd8)
+static gboolean
+scan_int (const char **pos, int *out)
+{
+ int i = 0;
+ char buf[32];
+ const char *p = *pos;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ if (*p < '0' || *p > '9')
return FALSE;
+
+ while ((*p >= '0') && (*p <= '9') && i < sizeof (buf)) {
+ buf[i] = *p;
+ i++;
+ p++;
+ }
+
+ if (i == sizeof (buf))
+ return FALSE;
+ else
+ buf[i] = '\0';
+
+ *out = atoi (buf);
+
+ *pos = p;
return TRUE;
}
static gboolean
-pixbuf_check_tiff (guchar *buffer, int size)
+skip_space (const char **pos)
{
- if (size < 10)
- return FALSE;
+ const char *p = *pos;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ *pos = p;
+
+ return !(*p == '\0');
+}
+
+#ifdef G_OS_WIN32
- if (buffer [0] == 'M' &&
- buffer [1] == 'M' &&
- buffer [2] == 0 &&
- buffer [3] == 0x2a)
- return TRUE;
+/* DllMain function needed to tuck away the gdk-pixbuf DLL handle */
- if (buffer [0] == 'I' &&
- buffer [1] == 'I' &&
- buffer [2] == 0x2a &&
- buffer [3] == 0)
- return TRUE;
+static HMODULE gdk_pixbuf_dll;
- return FALSE;
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ gdk_pixbuf_dll = (HMODULE) hinstDLL;
+ break;
+ }
+
+ return TRUE;
}
-static gboolean
-pixbuf_check_gif (guchar *buffer, int size)
+static char *
+get_toplevel (void)
{
- if (size < 20)
- return FALSE;
+ static char *toplevel = NULL;
- if (strncmp (buffer, "GIF8", 4) == 0)
- return TRUE;
+ if (toplevel == NULL)
+ toplevel = g_win32_get_package_installation_directory_of_module (gdk_pixbuf_dll);
- return FALSE;
+ return toplevel;
}
-static gboolean
-pixbuf_check_xpm (guchar *buffer, int size)
+static char *
+get_sysconfdir (void)
{
- if (size < 20)
- return FALSE;
+ static char *sysconfdir = NULL;
- if (strncmp (buffer, "/* XPM */", 9) == 0)
- return TRUE;
+ if (sysconfdir == NULL)
+ sysconfdir = g_build_filename (get_toplevel (), "etc", NULL);
- return FALSE;
+ return sysconfdir;
}
-static gboolean
-pixbuf_check_pnm (guchar *buffer, int size)
-{
- if (size < 20)
- return FALSE;
+#undef GTK_SYSCONFDIR
+#define GTK_SYSCONFDIR get_sysconfdir()
- if (buffer [0] == 'P') {
- if (buffer [1] == '1' ||
- buffer [1] == '2' ||
- buffer [1] == '3' ||
- buffer [1] == '4' ||
- buffer [1] == '5' ||
- buffer [1] == '6')
- return TRUE;
+static void
+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;
}
- return FALSE;
+
+ /* 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
+ * distributed in such a package contains paths from the package
+ * builder's machine. Replace the build-time prefix with the
+ * installation prefix on this machine.
+ */
+ tem = *path;
+ *path = g_strconcat (get_toplevel (), tem + strlen (GTK_PREFIX), NULL);
+ g_free (tem);
+ }
}
-static gboolean
-pixbuf_check_sunras (guchar *buffer, int size)
+
+#endif /* G_OS_WIN32 */
+
+static gchar *
+gdk_pixbuf_get_module_file (void)
{
- if (size < 32)
- return FALSE;
+ gchar *result = g_strdup (g_getenv ("GDK_PIXBUF_MODULE_FILE"));
- if (buffer [0] != 0x59 ||
- buffer [1] != 0xA6 ||
- buffer [2] != 0x6A ||
- buffer [3] != 0x95)
- return FALSE;
+ if (!result)
+ result = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gdk-pixbuf.loaders", NULL);
- return TRUE;
+ return result;
}
+#endif /* USE_GMODULE */
+
-#if 0
static gboolean
-pixbuf_check_bmp (guchar *buffer, int size)
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+ GError **error);
+
+static void
+gdk_pixbuf_io_init (void)
{
- if (size < 20)
- return FALSE;
+#ifdef USE_GMODULE
+ GIOChannel *channel;
+ gchar *line_buf;
+ gsize term;
+ GString *tmp_buf = g_string_new (NULL);
+ gboolean have_error = FALSE;
+ GdkPixbufModule *module = NULL;
+ gchar *filename = gdk_pixbuf_get_module_file ();
+ int flags;
+ int n_patterns = 0;
+ GdkPixbufModulePattern *pattern;
+ GError *error = NULL;
+#endif
+ GdkPixbufModule *builtin_module ;
- if (buffer [0] != 'B' || buffer [1] != 'M')
- return FALSE;
+ /* initialize on separate line to avoid compiler warnings in the
+ * common case of no compiled-in modules.
+ */
+ builtin_module = NULL;
- return TRUE;
-}
+#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_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) {
+ /* 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;
+ }
+
+ while (!have_error && g_io_channel_read_line (channel, &line_buf, NULL, &term, NULL) == G_IO_STATUS_NORMAL) {
+ const char *p;
+
+ p = line_buf;
+
+ line_buf[term] = 0;
+ if (!skip_space (&p)) {
+ /* Blank line marking the end of a module
+ */
+ if (module && *p != '#') {
+#ifdef G_OS_WIN32
+ correct_prefix (&module->module_path);
#endif
+ file_formats = g_slist_prepend (file_formats, module);
+ module = NULL;
+ }
+
+ goto next_line;
+ }
+
+ if (*p == '#')
+ goto next_line;
+
+ if (!module) {
+ /* Read a module location
+ */
+ module = g_new0 (GdkPixbufModule, 1);
+ n_patterns = 0;
+
+ if (!scan_string (&p, tmp_buf)) {
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ have_error = TRUE;
+ }
+ module->module_path = g_strdup (tmp_buf->str);
+ }
+ else if (!module->module_name) {
+ module->info = g_new0 (GdkPixbufFormat, 1);
+ if (!scan_string (&p, tmp_buf)) {
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ have_error = TRUE;
+ }
+ module->info->name = g_strdup (tmp_buf->str);
+ module->module_name = module->info->name;
+
+ if (!scan_int (&p, &flags)) {
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ have_error = TRUE;
+ }
+ module->info->flags = flags;
+
+ if (!scan_string (&p, tmp_buf)) {
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ have_error = TRUE;
+ }
+ if (tmp_buf->str[0] != 0)
+ module->info->domain = g_strdup (tmp_buf->str);
+
+ if (!scan_string (&p, tmp_buf)) {
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ 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;
+ module->info->mime_types = g_new0 (gchar*, 1);
+ while (scan_string (&p, tmp_buf)) {
+ if (tmp_buf->str[0] != 0) {
+ module->info->mime_types =
+ g_realloc (module->info->mime_types, (n + 1) * sizeof (gchar*));
+ module->info->mime_types[n - 1] = g_strdup (tmp_buf->str);
+ module->info->mime_types[n] = NULL;
+ n++;
+ }
+ }
+ }
+ else if (!module->info->extensions) {
+ int n = 1;
+ module->info->extensions = g_new0 (gchar*, 1);
+ while (scan_string (&p, tmp_buf)) {
+ if (tmp_buf->str[0] != 0) {
+ module->info->extensions =
+ g_realloc (module->info->extensions, (n + 1) * sizeof (gchar*));
+ module->info->extensions[n - 1] = g_strdup (tmp_buf->str);
+ module->info->extensions[n] = NULL;
+ n++;
+ }
+ }
+ }
+ else {
+ n_patterns++;
+ module->info->signature = (GdkPixbufModulePattern *)
+ g_realloc (module->info->signature, (n_patterns + 1) * sizeof (GdkPixbufModulePattern));
+ pattern = module->info->signature + n_patterns;
+ pattern->prefix = NULL;
+ pattern->mask = NULL;
+ pattern->relevance = 0;
+ pattern--;
+ if (!scan_string (&p, tmp_buf))
+ goto context_error;
+ pattern->prefix = g_strdup (tmp_buf->str);
+
+ if (!scan_string (&p, tmp_buf))
+ goto context_error;
+ if (*tmp_buf->str)
+ pattern->mask = g_strdup (tmp_buf->str);
+ else
+ pattern->mask = NULL;
+
+ if (!scan_int (&p, &pattern->relevance))
+ goto context_error;
+
+ goto next_line;
-GdkPixbufModule file_formats [] = {
- { "png", pixbuf_check_png, NULL, NULL, NULL, NULL, NULL, NULL },
- { "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL },
- { "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL },
- { "gif", pixbuf_check_gif, NULL, NULL, NULL, NULL, NULL, NULL },
-#define XPM_FILE_FORMAT_INDEX 4
- { "xpm", pixbuf_check_xpm, NULL, NULL, NULL, NULL, NULL, NULL },
- { "pnm", pixbuf_check_pnm, NULL, NULL, NULL, NULL, NULL, NULL },
- { "ras", pixbuf_check_sunras, NULL, NULL, NULL, NULL, NULL, NULL },
-#if 0
- { "bmp", pixbuf_check_bmp, NULL, NULL, NULL, NULL, NULL, NULL },
+ context_error:
+ g_free (pattern->prefix);
+ g_free (pattern->mask);
+ g_free (pattern);
+ g_warning ("Error parsing loader info in '%s'\n %s",
+ filename, line_buf);
+ have_error = TRUE;
+ }
+ next_line:
+ g_free (line_buf);
+ }
+ g_string_free (tmp_buf, TRUE);
+ g_io_channel_unref (channel);
+ g_free (filename);
#endif
- { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
-};
+}
+
+#define module(type) \
+ extern void _gdk_pixbuf__##type##_fill_info (GdkPixbufFormat *info); \
+ extern void _gdk_pixbuf__##type##_fill_vtable (GdkPixbufModule *module)
+
+module (png);
+module (jpeg);
+module (gif);
+module (ico);
+module (ani);
+module (ras);
+module (xpm);
+module (tiff);
+module (pnm);
+module (bmp);
+module (wbmp);
+module (xbm);
+module (tga);
+module (pcx);
+module (icns);
+module (jasper);
+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
/* 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 */
-void
-gdk_pixbuf_load_module (GdkPixbufModule *image_module)
+static gboolean
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+ GError **error)
{
- char *module_name;
- char *path;
- GModule *module;
- gpointer load_sym;
+ GdkPixbufModuleFillInfoFunc fill_info = NULL;
+ GdkPixbufModuleFillVtableFunc fill_vtable = NULL;
+
+ if (image_module->module != NULL)
+ return TRUE;
- g_return_if_fail(image_module->module == NULL);
+#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
+ try_module (bmp,bmp);
+#endif
+#ifdef INCLUDE_wbmp
+ try_module (wbmp,wbmp);
+#endif
+#ifdef INCLUDE_gif
+ try_module (gif,gif);
+#endif
+#ifdef INCLUDE_ico
+ try_module (ico,ico);
+#endif
+#ifdef INCLUDE_ani
+ try_module (ani,ani);
+#endif
+#ifdef INCLUDE_jpeg
+ try_module (jpeg,jpeg);
+#endif
+#ifdef INCLUDE_pnm
+ try_module (pnm,pnm);
+#endif
+#ifdef INCLUDE_ras
+ try_module (ras,ras);
+#endif
+#ifdef INCLUDE_tiff
+ try_module (tiff,tiff);
+#endif
+#ifdef INCLUDE_xpm
+ try_module (xpm,xpm);
+#endif
+#ifdef INCLUDE_xbm
+ try_module (xbm,xbm);
+#endif
+#ifdef INCLUDE_tga
+ try_module (tga,tga);
+#endif
+#ifdef INCLUDE_pcx
+ try_module (pcx,pcx);
+#endif
+#ifdef INCLUDE_icns
+ try_module (icns,icns);
+#endif
+#ifdef INCLUDE_jasper
+ try_module (jasper,jasper);
+#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
- module_name = g_strconcat ("pixbuf-", image_module->module_name, NULL);
- path = g_module_build_path (PIXBUF_LIBDIR, module_name);
+#undef try_module
- module = g_module_open (path, G_MODULE_BIND_LAZY);
- if (!module) {
- /* Debug feature, check in present working directory */
- g_free(path);
- path = g_module_build_path("", module_name);
- module = g_module_open(path, G_MODULE_BIND_LAZY);
-
- if (!module) {
- g_warning ("Unable to load module: %s: %s", path, g_module_error());
- g_free (module_name);
- g_free(path);
- return;
- } else {
- printf("loaded module `%s'\n", module_name);
- }
- g_free(path);
- } else {
- printf("loaded module `%s'\n", path);
- g_free (path);
- }
+ if (fill_vtable) {
+ image_module->module = (void *) 1;
+ (* fill_vtable) (image_module);
+ if (image_module->info == NULL) {
+ image_module->info = g_new0 (GdkPixbufFormat, 1);
+ (* fill_info) (image_module->info);
+ }
+ return TRUE;
+ }
+ 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);
- g_free (module_name);
+ 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;
+ 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 */
+}
- if (g_module_symbol (module, "image_load", &load_sym))
- image_module->load = load_sym;
- if (g_module_symbol (module, "image_load_xpm_data", &load_sym))
- image_module->load_xpm_data = load_sym;
+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;
+ }
- if (g_module_symbol (module, "image_begin_load", &load_sym))
- image_module->begin_load = load_sym;
+ ret = gdk_pixbuf_load_module_unlocked (image_module, error);
- if (g_module_symbol (module, "image_stop_load", &load_sym))
- image_module->stop_load = load_sym;
+ if (locked)
+ G_UNLOCK (init_lock);
- if (g_module_symbol (module, "image_load_increment", &load_sym))
- image_module->load_increment = load_sym;
+ return ret;
}
\f
GdkPixbufModule *
-gdk_pixbuf_get_module (gchar *buffer, gint size)
+_gdk_pixbuf_get_named_module (const char *name,
+ GError **error)
+{
+ GSList *modules;
+
+ for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
+ GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
+
+ if (module->info->disabled)
+ continue;
+
+ if (!strcmp (name, module->module_name))
+ return module;
+ }
+
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+ _("Image type '%s' is not supported"),
+ name);
+
+ return NULL;
+}
+
+GdkPixbufModule *
+_gdk_pixbuf_get_module (guchar *buffer, guint size,
+ const gchar *filename,
+ GError **error)
{
- gint i;
+ GSList *modules;
+
+ 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;
+
+ if (module->info->disabled)
+ continue;
+
+ score = format_check (module, buffer, size);
+ if (score > best) {
+ best = score;
+ selected = module;
+ }
+ if (score >= 100)
+ break;
+ }
+#endif
+
+ if (selected != NULL)
+ return selected;
- for (i = 0; file_formats [i].module_name; i++) {
- if ((* file_formats [i].format_check) (buffer, size))
- return &(file_formats[i]);
+ if (filename)
+ {
+ display_name = g_filename_display_name (filename);
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+ _("Couldn't recognize the image file format for file '%s'"),
+ display_name);
+ g_free (display_name);
}
+ else
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+ _("Unrecognized image file format"));
+
+
return NULL;
}
+
+static void
+prepared_notify (GdkPixbuf *pixbuf,
+ GdkPixbufAnimation *anim,
+ gpointer user_data)
+{
+ if (pixbuf != NULL)
+ g_object_ref (pixbuf);
+ *((GdkPixbuf **)user_data) = pixbuf;
+}
+
+GdkPixbuf *
+_gdk_pixbuf_generic_image_load (GdkPixbufModule *module,
+ FILE *f,
+ GError **error)
+{
+ guchar buffer[LOAD_BUFFER_SIZE];
+ size_t length;
+ GdkPixbuf *pixbuf = NULL;
+ GdkPixbufAnimation *animation = NULL;
+ gpointer context;
+ gboolean locked;
+
+ locked = _gdk_pixbuf_lock (module);
+
+ if (module->load != NULL) {
+ pixbuf = (* module->load) (f, error);
+ } else if (module->begin_load != NULL) {
+
+ context = module->begin_load (NULL, prepared_notify, NULL, &pixbuf, error);
+
+ if (!context)
+ goto out;
+
+ while (!feof (f) && !ferror (f)) {
+ length = fread (buffer, 1, sizeof (buffer), f);
+ if (length > 0)
+ if (!module->load_increment (context, buffer, length, error)) {
+ module->stop_load (context, NULL);
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
+ pixbuf = NULL;
+ }
+ goto out;
+ }
+ }
+
+ if (!module->stop_load (context, error)) {
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
+ pixbuf = NULL;
+ }
+ }
+ } else if (module->load_animation != NULL) {
+ animation = (* module->load_animation) (f, error);
+ if (animation != NULL) {
+ pixbuf = gdk_pixbuf_animation_get_static_image (animation);
+
+ g_object_ref (pixbuf);
+ g_object_unref (animation);
+ }
+ }
+
+ out:
+ if (locked)
+ _gdk_pixbuf_unlock (module);
+ return pixbuf;
+}
+
/**
* gdk_pixbuf_new_from_file:
- * @filename: Name of file to load.
- *
+ * @filename: Name of file to load, in the GLib file name encoding
+ * @error: Return location for an error
+ *
* Creates a new pixbuf by loading an image from a file. The file format is
- * detected automatically.
- *
- * Return value: A newly-created pixbuf, or NULL if any of several error
- * conditions occurred: the file could not be opened, there was no loader for
- * the file's format, there was not enough memory to allocate the image buffer,
- * or the image file contained invalid data.
+ * detected automatically. If %NULL is returned, then @error will be set.
+ * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
+ *
+ * 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 be opened,
+ * there was no loader for the file's format, there was not enough memory to
+ * allocate the image buffer, or the image file contained invalid data.
**/
GdkPixbuf *
-gdk_pixbuf_new_from_file (const char *filename)
+gdk_pixbuf_new_from_file (const char *filename,
+ GError **error)
{
GdkPixbuf *pixbuf;
- gint size;
+ int size;
FILE *f;
- char buffer [128];
+ guchar buffer[SNIFF_BUFFER_SIZE];
GdkPixbufModule *image_module;
+ gchar *display_name;
- f = fopen (filename, "r");
- if (!f)
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ display_name = g_filename_display_name (filename);
+
+ f = g_fopen (filename, "rb");
+ if (!f) {
+ gint save_errno = errno;
+ g_set_error (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (save_errno),
+ _("Failed to open file '%s': %s"),
+ display_name,
+ g_strerror (save_errno));
+ g_free (display_name);
return NULL;
+ }
size = fread (&buffer, 1, sizeof (buffer), f);
-
if (size == 0) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Image file '%s' contains no data"),
+ display_name);
+ g_free (display_name);
fclose (f);
return NULL;
}
- image_module = gdk_pixbuf_get_module (buffer, size);
- if (image_module){
- if (image_module->module == NULL)
- gdk_pixbuf_load_module (image_module);
+ image_module = _gdk_pixbuf_get_module (buffer, size, filename, error);
+ if (image_module == NULL) {
+ g_free (display_name);
+ fclose (f);
+ return NULL;
+ }
- if (image_module->load == NULL) {
- 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 = (* image_module->load) (f);
- fclose (f);
+ fseek (f, 0, SEEK_SET);
+ pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
+ fclose (f);
- if (pixbuf)
- g_assert (pixbuf->ref_count != 0);
+ if (pixbuf == NULL && error != NULL && *error == NULL) {
- return pixbuf;
- } else {
- g_warning ("Unable to find handler for file: %s", filename);
- }
+ /* I don't trust these crufty longjmp()'ing image libs
+ * to maintain proper error invariants, and I don't
+ * want user code to segfault as a result. We need to maintain
+ * the invariant that error gets set if NULL is returned.
+ */
- fclose (f);
- return NULL;
+ g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.", image_module->module_name);
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Failed to load image '%s': reason not known, probably a corrupt image file"),
+ display_name);
+ } else if (error != NULL && *error != NULL) {
+
+ /* Add the filename to the error message */
+ GError *e = *error;
+ gchar *old;
+
+ old = e->message;
+ e->message = g_strdup_printf (_("Failed to load image '%s': %s"),
+ display_name,
+ old);
+ g_free (old);
+ }
+
+ g_free (display_name);
+ return pixbuf;
}
+#ifdef G_OS_WIN32
+
+#undef gdk_pixbuf_new_from_file
GdkPixbuf *
-gdk_pixbuf_new_from_xpm_data (const gchar **data)
+gdk_pixbuf_new_from_file (const char *filename,
+ GError **error)
{
- GdkPixbuf *(* load_xpm_data) (const gchar **data);
- GdkPixbuf *pixbuf;
+ gchar *utf8_filename =
+ g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+ GdkPixbuf *retval;
- if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
- gdk_pixbuf_load_module(&file_formats[XPM_FILE_FORMAT_INDEX]);
- }
+ if (utf8_filename == NULL)
+ return NULL;
- if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
- g_warning("Can't find gdk-pixbuf module for parsing inline XPM data");
- return NULL;
- } else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
- g_warning("gdk-pixbuf XPM module lacks XPM data capability");
- return NULL;
- } else {
- load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data;
- }
+ retval = gdk_pixbuf_new_from_file_utf8 (utf8_filename, error);
+
+ g_free (utf8_filename);
- pixbuf = load_xpm_data(data);
+ return retval;
+}
+#endif
+
+
+/**
+ * gdk_pixbuf_new_from_file_at_size:
+ * @filename: Name of file to load, in the GLib file name encoding
+ * @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
+ * @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.
+ *
+ * The image will be scaled to fit in the requested size, preserving
+ * 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
+ * be opened, there was no loader for the file's format, there was not
+ * enough memory to allocate the image buffer, or the image file contained
+ * invalid data.
+ *
+ * Since: 2.4
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_file_at_size (const char *filename,
+ int width,
+ int height,
+ GError **error)
+{
+ return gdk_pixbuf_new_from_file_at_scale (filename,
+ width, height,
+ TRUE, error);
+}
+
+#ifdef G_OS_WIN32
+
+#undef gdk_pixbuf_new_from_file_at_size
+
+GdkPixbuf *
+gdk_pixbuf_new_from_file_at_size (const char *filename,
+ int width,
+ int height,
+ GError **error)
+{
+ gchar *utf8_filename =
+ g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+ GdkPixbuf *retval;
+
+ if (utf8_filename == NULL)
+ return NULL;
- return pixbuf;
+ retval = gdk_pixbuf_new_from_file_at_size_utf8 (utf8_filename,
+ width, height,
+ error);
+
+ g_free (utf8_filename);
+
+ return retval;
}
+#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
+ * @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
+ * @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.
+ * 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. Negative values for @width and @height are
+ * allowed since 2.8.
+ *
+ * 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 be opened,
+ * there was no loader for the file's format, there was not enough memory to
+ * allocate the image buffer, or the image file contained invalid data.
+ *
+ * Since: 2.6
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_file_at_scale (const char *filename,
+ int width,
+ int height,
+ gboolean preserve_aspect_ratio,
+ GError **error)
+{
+
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf;
+ guchar buffer[LOAD_BUFFER_SIZE];
+ int length;
+ FILE *f;
+ 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);
+ g_return_val_if_fail (height > 0 || height == -1, NULL);
+
+ 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 (save_errno),
+ _("Failed to open file '%s': %s"),
+ display_name,
+ g_strerror (save_errno));
+ g_free (display_name);
+ return NULL;
+ }
+
+ 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);
+
+ 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)) {
+ gdk_pixbuf_loader_close (loader, NULL);
+ fclose (f);
+ 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) && !has_frame) {
+ g_object_unref (loader);
+ return NULL;
+ }
+
+ pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+ if (!pixbuf) {
+ gchar *display_name = g_filename_display_name (filename);
+ g_object_unref (loader);
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Failed to load image '%s': reason not known, probably a corrupt image file"),
+ display_name);
+ g_free (display_name);
+ return NULL;
+ }
+
+ g_object_ref (pixbuf);
+
+ g_object_unref (loader);
+
+ return pixbuf;
+}
+
+#ifdef G_OS_WIN32
+
+#undef gdk_pixbuf_new_from_file_at_scale
+
+GdkPixbuf *
+gdk_pixbuf_new_from_file_at_scale (const char *filename,
+ int width,
+ int height,
+ gboolean preserve_aspect_ratio,
+ GError **error)
+{
+ gchar *utf8_filename =
+ g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+ GdkPixbuf *retval;
+
+ if (utf8_filename == NULL)
+ return NULL;
+
+ retval = gdk_pixbuf_new_from_file_at_scale_utf8 (utf8_filename,
+ width, height,
+ preserve_aspect_ratio,
+ error);
+
+ g_free (utf8_filename);
+
+ return retval;
+}
+#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,
+ int height,
+ gpointer data)
+{
+ struct {
+ GdkPixbufFormat *format;
+ int width;
+ int height;
+ } *info = data;
+
+ g_return_if_fail (width > 0 && height > 0);
+
+ info->format = gdk_pixbuf_loader_get_format (loader);
+ info->width = width;
+ info->height = height;
+
+ gdk_pixbuf_loader_set_size (loader, 0, 0);
+}
+
+/**
+ * gdk_pixbuf_get_file_info:
+ * @filename: The name of the file to identify.
+ * @width: Return location for the width of the image, or %NULL
+ * @height: Return location for the height of the image, or %NULL
+ *
+ * Parses an image file far enough to determine its format and size.
+ *
+ * Returns: A #GdkPixbufFormat describing the image format of the file
+ * or %NULL if the image format wasn't recognized. The return value
+ * is owned by GdkPixbuf and should not be freed.
+ *
+ * Since: 2.4
+ **/
+GdkPixbufFormat *
+gdk_pixbuf_get_file_info (const gchar *filename,
+ gint *width,
+ gint *height)
+{
+ GdkPixbufLoader *loader;
+ guchar buffer[SNIFF_BUFFER_SIZE];
+ int length;
+ FILE *f;
+ struct {
+ GdkPixbufFormat *format;
+ gint width;
+ gint height;
+ } info;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ f = g_fopen (filename, "rb");
+ if (!f)
+ return NULL;
+
+ loader = gdk_pixbuf_loader_new ();
+
+ info.format = NULL;
+ info.width = -1;
+ info.height = -1;
+
+ g_signal_connect (loader, "size-prepared", G_CALLBACK (info_cb), &info);
+
+ while (!feof (f) && !ferror (f)) {
+ length = fread (buffer, 1, sizeof (buffer), f);
+ if (length > 0) {
+ if (!gdk_pixbuf_loader_write (loader, buffer, length, NULL))
+ break;
+ }
+ if (info.format != NULL)
+ break;
+ }
+
+ fclose (f);
+ gdk_pixbuf_loader_close (loader, NULL);
+ g_object_unref (loader);
+
+ if (width)
+ *width = info.width;
+ if (height)
+ *height = info.height;
+
+ return info.format;
+}
+
+/**
+ * gdk_pixbuf_new_from_xpm_data:
+ * @data: Pointer to inline XPM data.
+ *
+ * Creates a new pixbuf by parsing XPM data in memory. This data is commonly
+ * the result of including an XPM file into a program's C source.
+ *
+ * Return value: A newly-created pixbuf with a reference count of 1.
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_xpm_data (const char **data)
+{
+ GdkPixbuf *(* load_xpm_data) (const char **data);
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+ 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);
+ 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);
+
+ if (xpm_module->load_xpm_data == NULL) {
+ g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
+ pixbuf = NULL;
+ } else {
+ load_xpm_data = xpm_module->load_xpm_data;
+ pixbuf = (* load_xpm_data) (data);
+ }
+
+ if (locked)
+ _gdk_pixbuf_unlock (xpm_module);
+ return pixbuf;
+}
+
+static void
+collect_save_options (va_list opts,
+ gchar ***keys,
+ gchar ***vals)
+{
+ gchar *key;
+ gchar *val;
+ gchar *next;
+ gint count;
+
+ count = 0;
+ *keys = NULL;
+ *vals = NULL;
+
+ next = va_arg (opts, gchar*);
+ while (next)
+ {
+ key = next;
+ val = va_arg (opts, gchar*);
+
+ ++count;
+
+ /* woo, slow */
+ *keys = g_realloc (*keys, sizeof(gchar*) * (count + 1));
+ *vals = g_realloc (*vals, sizeof(gchar*) * (count + 1));
+
+ (*keys)[count-1] = g_strdup (key);
+ (*vals)[count-1] = g_strdup (val);
+
+ (*keys)[count] = NULL;
+ (*vals)[count] = NULL;
+
+ next = va_arg (opts, gchar*);
+ }
+}
+
+static gboolean
+save_to_file_callback (const gchar *buf,
+ gsize count,
+ GError **error,
+ gpointer data)
+{
+ FILE *filehandle = data;
+ gsize n;
+
+ 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 (save_errno),
+ _("Error writing to image file: %s"),
+ g_strerror (save_errno));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
+ FILE *filehandle,
+ const char *type,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ gboolean ret;
+ GdkPixbufModule *image_module = NULL;
+ gboolean locked;
+
+ image_module = _gdk_pixbuf_get_named_module (type, error);
+
+ if (image_module == NULL)
+ return FALSE;
+
+ if (!_gdk_pixbuf_load_module (image_module, error))
+ return FALSE;
+
+ locked = _gdk_pixbuf_lock (image_module);
+
+ if (image_module->save) {
+ /* save normally */
+ ret = (* image_module->save) (filehandle, pixbuf,
+ keys, values,
+ error);
+ } else if (image_module->save_to_callback) {
+ /* save with simple callback */
+ ret = (* image_module->save_to_callback) (save_to_file_callback,
+ filehandle, pixbuf,
+ keys, values,
+ error);
+ } else {
+ /* can't save */
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+ _("This build of gdk-pixbuf does not support saving the image format: %s"),
+ type);
+ ret = FALSE;
+ }
+
+ if (locked)
+ _gdk_pixbuf_unlock (image_module);
+ return ret;
+}
+
+#define TMP_FILE_BUF_SIZE 4096
+
+static gboolean
+save_to_callback_with_tmp_file (GdkPixbufModule *image_module,
+ GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ int fd;
+ FILE *f = NULL;
+ gboolean retval = FALSE;
+ gchar *buf = NULL;
+ gsize n;
+ gchar *filename = NULL;
+ gboolean locked;
+
+ buf = g_try_malloc (TMP_FILE_BUF_SIZE);
+ if (buf == NULL) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image to callback"));
+ goto end;
+ }
+
+ fd = g_file_open_tmp ("gdkpixbuf-save-tmp.XXXXXX", &filename, error);
+ if (fd == -1)
+ goto end;
+ f = fdopen (fd, "wb+");
+ if (f == NULL) {
+ 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;
+ }
+
+ locked = _gdk_pixbuf_lock (image_module);
+ retval = (image_module->save) (f, pixbuf, keys, values, error);
+ if (locked)
+ _gdk_pixbuf_unlock (image_module);
+ if (!retval)
+ goto end;
+
+ rewind (f);
+ for (;;) {
+ n = fread (buf, 1, TMP_FILE_BUF_SIZE, f);
+ if (n > 0) {
+ if (!save_func (buf, n, error, user_data))
+ goto end;
+ }
+ if (n != TMP_FILE_BUF_SIZE)
+ break;
+ }
+ if (ferror (f)) {
+ 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;
+
+ end:
+ /* cleanup and return retval */
+ if (f)
+ fclose (f);
+ if (filename) {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ g_free (buf);
+
+ return retval;
+}
+
+static gboolean
+gdk_pixbuf_real_save_to_callback (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ gboolean ret;
+ GdkPixbufModule *image_module = NULL;
+ gboolean locked;
+
+ image_module = _gdk_pixbuf_get_named_module (type, error);
+
+ if (image_module == NULL)
+ return FALSE;
+
+ if (!_gdk_pixbuf_load_module (image_module, error))
+ return FALSE;
+
+ locked = _gdk_pixbuf_lock (image_module);
+
+ if (image_module->save_to_callback) {
+ /* save normally */
+ ret = (* image_module->save_to_callback) (save_func, user_data,
+ pixbuf, keys, values,
+ error);
+ } else if (image_module->save) {
+ /* use a temporary file */
+ ret = save_to_callback_with_tmp_file (image_module, pixbuf,
+ save_func, user_data,
+ keys, values,
+ error);
+ } else {
+ /* can't save */
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
+ _("This build of gdk-pixbuf does not support saving the image format: %s"),
+ type);
+ ret = FALSE;
+ }
+
+ if (locked)
+ _gdk_pixbuf_unlock (image_module);
+ return ret;
+}
+
+
+/**
+ * gdk_pixbuf_save:
+ * @pixbuf: a #GdkPixbuf.
+ * @filename: name of file to save.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf to a file in format @type. By default, "jpeg", "png", "ico"
+ * and "bmp" are possible file formats to save in, but more formats may be
+ * installed. The list of all writable formats can be determined in the
+ * following way:
+ *
+ * |[
+ * 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 *writable_formats = NULL;
+ * g_slist_foreach (formats, add_if_writable, &writable_formats);
+ * g_slist_free (formats);
+ * ]|
+ *
+ * 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.
+ *
+ * The variable argument list should be %NULL-terminated; if not empty,
+ * it should contain pairs of strings that modify the save
+ * parameters. For example:
+ * <informalexample><programlisting>
+ * gdk_pixbuf_save (pixbuf, handle, "jpeg", &error,
+ * "quality", "100", NULL);
+ * </programlisting></informalexample>
+ *
+ * Currently only few parameters exist. JPEG images can be saved with a
+ * "quality" parameter; its value should be in the range [0,100].
+ *
+ * Text chunks can be attached to PNG images by specifying parameters of
+ * the form "tEXt::key", where key is an ASCII string of length 1-79.
+ * The values are UTF-8 encoded strings. The PNG compression level can
+ * be specified using the "compression" parameter; it's value is in an
+ * integer in the range of [0,9].
+ *
+ * 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.
+ *
+ * Return value: whether an error was set
+ **/
+
+gboolean
+gdk_pixbuf_save (GdkPixbuf *pixbuf,
+ const char *filename,
+ const char *type,
+ GError **error,
+ ...)
+{
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_savev (pixbuf, filename, type,
+ keys, values,
+ error);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+#ifdef G_OS_WIN32
+
+#undef gdk_pixbuf_save
+
+gboolean
+gdk_pixbuf_save (GdkPixbuf *pixbuf,
+ const char *filename,
+ const char *type,
+ GError **error,
+ ...)
+{
+ char *utf8_filename;
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+
+ if (utf8_filename == NULL)
+ return FALSE;
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_savev_utf8 (pixbuf, utf8_filename, type,
+ keys, values,
+ error);
+
+ g_free (utf8_filename);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+#endif
+
+/**
+ * gdk_pixbuf_savev:
+ * @pixbuf: a #GdkPixbuf.
+ * @filename: name of file to save.
+ * @type: name of file format.
+ * @option_keys: name of options to set, %NULL-terminated
+ * @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", "tiff", "ico" or "bmp".
+ * If @error is set, %FALSE will be returned.
+ * See gdk_pixbuf_save () for more details.
+ *
+ * Return value: whether an error was set
+ **/
+
+gboolean
+gdk_pixbuf_savev (GdkPixbuf *pixbuf,
+ const char *filename,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ FILE *f = NULL;
+ gboolean result;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (type != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ 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 (save_errno),
+ _("Failed to open '%s' for writing: %s"),
+ display_name,
+ g_strerror (save_errno));
+ g_free (display_name);
+ return FALSE;
+ }
+
+
+ result = gdk_pixbuf_real_save (pixbuf, f, type,
+ option_keys, option_values,
+ error);
+
+
+ if (!result) {
+ g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
+ fclose (f);
+ return FALSE;
+ }
+
+ 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 (save_errno),
+ _("Failed to close '%s' while writing image, all data may not have been saved: %s"),
+ display_name,
+ g_strerror (save_errno));
+ g_free (display_name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#ifdef G_OS_WIN32
+
+#undef gdk_pixbuf_savev
+
+gboolean
+gdk_pixbuf_savev (GdkPixbuf *pixbuf,
+ const char *filename,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ char *utf8_filename;
+ gboolean retval;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
+
+ if (utf8_filename == NULL)
+ return FALSE;
+
+ retval = gdk_pixbuf_savev_utf8 (pixbuf, utf8_filename, type,
+ option_keys, option_values, error);
+
+ g_free (utf8_filename);
+
+ return retval;
+}
+
+#endif
+
+/**
+ * gdk_pixbuf_save_to_callback:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ * the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf in format @type by feeding the produced data to a
+ * callback. Can be used when you want to store the image to something
+ * other than a file, such as an in-memory buffer or a socket.
+ * If @error is set, %FALSE will be returned. Possible errors
+ * include those in the #GDK_PIXBUF_ERROR domain and whatever the save
+ * function generates.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callback (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ GError **error,
+ ...)
+{
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_save_to_callbackv (pixbuf, save_func, user_data,
+ type, keys, values,
+ error);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+/**
+ * gdk_pixbuf_save_to_callbackv:
+ * @pixbuf: a #GdkPixbuf.
+ * @save_func: a function that is called to save each block of data that
+ * the save routine generates.
+ * @user_data: user data to pass to the save function.
+ * @type: name of file format.
+ * @option_keys: name of options to set, %NULL-terminated
+ * @option_values: values for named options
+ * @error: return location for error, or %NULL
+ *
+ * Saves pixbuf to a callback in format @type, which is currently "jpeg",
+ * "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
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_callbackv (GdkPixbuf *pixbuf,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ gboolean result;
+
+
+ g_return_val_if_fail (save_func != NULL, FALSE);
+ g_return_val_if_fail (type != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ result = gdk_pixbuf_real_save_to_callback (pixbuf,
+ save_func, user_data, type,
+ option_keys, option_values,
+ error);
+
+ if (!result) {
+ g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_buffer:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: name of file format.
+ * @error: return location for error, or %NULL
+ * @Varargs: list of key-value save options
+ *
+ * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
+ * "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 @buffer will be set to
+ * %NULL. Possible errors include those in the #GDK_PIXBUF_ERROR
+ * domain.
+ *
+ * See gdk_pixbuf_save() for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_buffer (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ GError **error,
+ ...)
+{
+ gchar **keys = NULL;
+ gchar **values = NULL;
+ va_list args;
+ gboolean result;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ va_start (args, error);
+
+ collect_save_options (args, &keys, &values);
+
+ va_end (args);
+
+ result = gdk_pixbuf_save_to_bufferv (pixbuf, buffer, buffer_size,
+ type, keys, values,
+ error);
+
+ g_strfreev (keys);
+ g_strfreev (values);
+
+ return result;
+}
+
+struct SaveToBufferData {
+ gchar *buffer;
+ gsize len, max;
+};
+
+static gboolean
+save_to_buffer_callback (const gchar *data,
+ gsize count,
+ GError **error,
+ gpointer user_data)
+{
+ struct SaveToBufferData *sdata = user_data;
+ gchar *new_buffer;
+ gsize new_max;
+
+ if (sdata->len + count > sdata->max) {
+ new_max = MAX (sdata->max*2, sdata->len + count);
+ new_buffer = g_try_realloc (sdata->buffer, new_max);
+ if (!new_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;
+ sdata->max = new_max;
+ }
+ memcpy (sdata->buffer + sdata->len, data, count);
+ sdata->len += count;
+ return TRUE;
+}
+
+/**
+ * gdk_pixbuf_save_to_bufferv:
+ * @pixbuf: a #GdkPixbuf.
+ * @buffer: location to receive a pointer to the new buffer.
+ * @buffer_size: location to receive the size of the new buffer.
+ * @type: name of file format.
+ * @option_keys: name of options to set, %NULL-terminated
+ * @option_values: values for named options
+ * @error: return location for error, or %NULL
+ *
+ * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
+ * "tiff", "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer()
+ * for more details.
+ *
+ * Return value: whether an error was set
+ *
+ * Since: 2.4
+ **/
+gboolean
+gdk_pixbuf_save_to_bufferv (GdkPixbuf *pixbuf,
+ gchar **buffer,
+ gsize *buffer_size,
+ const char *type,
+ char **option_keys,
+ char **option_values,
+ GError **error)
+{
+ static const gint initial_max = 1024;
+ struct SaveToBufferData sdata;
+
+ *buffer = NULL;
+ *buffer_size = 0;
+
+ sdata.buffer = g_try_malloc (initial_max);
+ sdata.max = initial_max;
+ sdata.len = 0;
+ if (!sdata.buffer) {
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image into a buffer"));
+ return FALSE;
+ }
+
+ if (!gdk_pixbuf_save_to_callbackv (pixbuf,
+ save_to_buffer_callback, &sdata,
+ type, option_keys, option_values,
+ error)) {
+ g_free (sdata.buffer);
+ return FALSE;
+ }
+
+ *buffer = sdata.buffer;
+ *buffer_size = sdata.len;
+ 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
+ *
+ * Returns the name of the format.
+ *
+ * Return value: the name of the format.
+ *
+ * Since: 2.2
+ */
+gchar *
+gdk_pixbuf_format_get_name (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, NULL);
+
+ return g_strdup (format->name);
+}
+
+/**
+ * gdk_pixbuf_format_get_description:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns a description of the format.
+ *
+ * Return value: a description of the format.
+ *
+ * Since: 2.2
+ */
+gchar *
+gdk_pixbuf_format_get_description (GdkPixbufFormat *format)
+{
+ gchar *domain;
+ const gchar *description;
+ g_return_val_if_fail (format != NULL, NULL);
+
+ if (format->domain != NULL)
+ domain = format->domain;
+ else
+ domain = GETTEXT_PACKAGE;
+ description = g_dgettext (domain, format->description);
+
+ return g_strdup (description);
+}
+
+/**
+ * gdk_pixbuf_format_get_mime_types:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns the mime types supported by the format.
+ *
+ * Return value: a %NULL-terminated array of mime types which must be freed with
+ * g_strfreev() when it is no longer needed.
+ *
+ * Since: 2.2
+ */
+gchar **
+gdk_pixbuf_format_get_mime_types (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, NULL);
+
+ return g_strdupv (format->mime_types);
+}
+
+/**
+ * gdk_pixbuf_format_get_extensions:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns the filename extensions typically used for files in the
+ * given format.
+ *
+ * Return value: a %NULL-terminated array of filename extensions which must be
+ * freed with g_strfreev() when it is no longer needed.
+ *
+ * Since: 2.2
+ */
+gchar **
+gdk_pixbuf_format_get_extensions (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, NULL);
+
+ return g_strdupv (format->extensions);
+}
+
+/**
+ * gdk_pixbuf_format_is_writable:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns whether pixbufs can be saved in the given format.
+ *
+ * Return value: whether pixbufs can be saved in the given format.
+ *
+ * Since: 2.2
+ */
+gboolean
+gdk_pixbuf_format_is_writable (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, FALSE);
+
+ return (format->flags & GDK_PIXBUF_FORMAT_WRITABLE) != 0;
+}
+
+/**
+ * gdk_pixbuf_format_is_scalable:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns whether this image format is scalable. If a file is in a
+ * scalable format, it is preferable to load it at the desired size,
+ * rather than loading it at the default size and scaling the
+ * resulting pixbuf to the desired size.
+ *
+ * Return value: whether this image format is scalable.
+ *
+ * Since: 2.6
+ */
+gboolean
+gdk_pixbuf_format_is_scalable (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, FALSE);
+
+ return (format->flags & GDK_PIXBUF_FORMAT_SCALABLE) != 0;
+}
+
+/**
+ * gdk_pixbuf_format_is_disabled:
+ * @format: a #GdkPixbufFormat
+ *
+ * Returns whether this image format is disabled. See
+ * gdk_pixbuf_format_set_disabled().
+ *
+ * Return value: whether this image format is disabled.
+ *
+ * Since: 2.6
+ */
+gboolean
+gdk_pixbuf_format_is_disabled (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, FALSE);
+
+ return format->disabled;
+}
+
+/**
+ * gdk_pixbuf_format_set_disabled:
+ * @format: a #GdkPixbufFormat
+ * @disabled: %TRUE to disable the format @format
+ *
+ * Disables or enables an image format. If a format is disabled,
+ * gdk-pixbuf won't use the image loader for this format to load
+ * images. Applications can use this to avoid using image loaders
+ * with an inappropriate license, see gdk_pixbuf_format_get_license().
+ *
+ * Since: 2.6
+ */
+void
+gdk_pixbuf_format_set_disabled (GdkPixbufFormat *format,
+ gboolean disabled)
+{
+ g_return_if_fail (format != NULL);
+
+ format->disabled = disabled != FALSE;
+}
+
+/**
+ * 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. This
+ * string should be freed with g_free() when it's no longer needed.
+ *
+ * Returns: a string describing the license of @format.
+ *
+ * Since: 2.6
+ */
+gchar*
+gdk_pixbuf_format_get_license (GdkPixbufFormat *format)
+{
+ g_return_val_if_fail (format != NULL, NULL);
+
+ return g_strdup (format->license);
+}
+
+GdkPixbufFormat *
+_gdk_pixbuf_get_format (GdkPixbufModule *module)
+{
+ g_return_val_if_fail (module != NULL, NULL);
+
+ return module->info;
+}
+
+/**
+ * gdk_pixbuf_get_formats:
+ *
+ * Obtains the available information about the image formats supported
+ * by GdkPixbuf.
+ *
+ * Returns: A list of #GdkPixbufFormat<!-- -->s describing the supported
+ * image formats. The list should be freed when it is no longer needed,
+ * but the structures themselves are owned by #GdkPixbuf and should not be
+ * freed.
+ *
+ * Since: 2.2
+ */
+GSList *
+gdk_pixbuf_get_formats (void)
+{
+ GSList *result = NULL;
+ GSList *modules;
+
+ for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
+ GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
+ GdkPixbufFormat *info = _gdk_pixbuf_get_format (module);
+ result = g_slist_prepend (result, info);
+ }
+
+ return result;
+}
+
+#define __GDK_PIXBUF_IO_C__
+#include "gdk-pixbuf-aliasdef.c"