/* -*- mode: C; c-file-style: "linux" -*- */ /* GdkPixbuf library - Main loading interface. * * Copyright (C) 1999 The Free Software Foundation * * Authors: Miguel de Icaza * Federico Mena-Quintero * * This library is free software; you can redistribute it and/or * 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 * Lesser General Public License for more details. * * 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 #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include "gdk-pixbuf-private.h" #include "gdk-pixbuf-io.h" #include "gdk-pixbuf-loader.h" #include "gdk-pixbuf-alias.h" #include #ifdef G_OS_WIN32 #define STRICT #include #undef STRICT #endif #define SNIFF_BUFFER_SIZE 4096 #define LOAD_BUFFER_SIZE 65536 static gint format_check (GdkPixbufModule *module, guchar *buffer, int size) { int i, j; gchar m; GdkPixbufModulePattern *pattern; gboolean anchored; guchar *prefix; gchar *mask; 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; } } if (prefix[j] == 0) return pattern->relevance; if (anchored) break; } } return 0; } 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 scan_string (const char **pos, GString *out) { 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; } 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 skip_space (const char **pos) { const char *p = *pos; while (g_ascii_isspace (*p)) p++; *pos = p; return !(*p == '\0'); } #ifdef G_OS_WIN32 /* DllMain function needed to tuck away the gdk-pixbuf DLL name */ G_WIN32_DLLMAIN_FOR_DLL_NAME (static, dll_name) static char * get_toplevel (void) { static char *toplevel = NULL; if (toplevel == NULL) toplevel = g_win32_get_package_installation_subdirectory (GETTEXT_PACKAGE, dll_name, ""); return toplevel; } static char * get_sysconfdir (void) { static char *sysconfdir = NULL; if (sysconfdir == NULL) sysconfdir = g_win32_get_package_installation_subdirectory (GETTEXT_PACKAGE, dll_name, "etc"); return sysconfdir; } #undef GTK_SYSCONFDIR #define GTK_SYSCONFDIR get_sysconfdir() static void correct_prefix (gchar **path) { if (strncmp (*path, GTK_PREFIX "/", strlen (GTK_PREFIX "/")) == 0 || strncmp (*path, GTK_PREFIX "\\", strlen (GTK_PREFIX "\\")) == 0) { /* 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. */ gchar *tem = *path; *path = g_strconcat (get_toplevel (), tem + strlen (GTK_PREFIX), NULL); g_free (tem); } } #endif /* G_OS_WIN32 */ static gchar * gdk_pixbuf_get_module_file (void) { gchar *result = g_strdup (g_getenv ("GDK_PIXBUF_MODULE_FILE")); if (!result) result = g_build_filename (GTK_SYSCONFDIR, "gtk-2.0", "gdk-pixbuf.loaders", NULL); return result; } #endif /* USE_GMODULE */ static void gdk_pixbuf_io_init (void) { #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 ; /* 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 (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_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); 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); } 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; 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 } #ifdef USE_GMODULE /* 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 | 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)) { 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; } } #endif /* !USE_GMODULE */ #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 (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) { gboolean ret; gboolean locked = FALSE; GdkPixbufModuleFillInfoFunc fill_info = NULL; GdkPixbufModuleFillVtableFunc fill_vtable = 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_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); return TRUE; } #ifdef USE_GMODULE /* 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 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 } GdkPixbufModule * _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) { GSList *modules; gint score, best = 0; GdkPixbufModule *selected = NULL; gchar *display_name = NULL; 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; } if (selected != NULL) return selected; 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 (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, 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. 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, GError **error) { GdkPixbuf *pixbuf; int size; FILE *f; guchar buffer[SNIFF_BUFFER_SIZE]; GdkPixbufModule *image_module; gchar *display_name; 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, filename, error); if (image_module == NULL) { g_free (display_name); fclose (f); return NULL; } if (image_module->module == 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); fclose (f); if (pixbuf == NULL && error != NULL && *error == NULL) { /* 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. */ 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_file (const char *filename, 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_utf8 (utf8_filename, error); g_free (utf8_filename); 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. * * 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; 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 (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; } } 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 (image_module->module == NULL) 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 (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 (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 (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 (image_module->module == NULL) 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: * * gdk_pixbuf_save (pixbuf, handle, "jpeg", &error, * "quality", "100", NULL); * * * 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 @string 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 (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 (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 (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; if (!gdk_pixbuf_save_to_callbackv (pixbuf, save_to_stream, &data, type, keys, values, error)) { error = NULL; /* Ignore further errors */ res = FALSE; } 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; 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); 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 #GdkPixbufFormats 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"