* Boston, MA 02111-1307, USA.
*/
-#include <config.h>
+#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <glib.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#include <glib.h>
+#include <gio/gio.h>
+
#include "gdk-pixbuf-private.h"
#include "gdk-pixbuf-io.h"
+#include "gdk-pixbuf-loader.h"
#include "gdk-pixbuf-alias.h"
#include <glib/gstdio.h>
#undef STRICT
#endif
+#define SNIFF_BUFFER_SIZE 4096
+#define LOAD_BUFFER_SIZE 65536
+
+#ifndef GDK_PIXBUF_USE_GIO_MIME
static gint
format_check (GdkPixbufModule *module, guchar *buffer, int size)
{
}
return 0;
}
+#endif
G_LOCK_DEFINE_STATIC (init_lock);
G_LOCK_DEFINE_STATIC (threadunsafe_loader_lock);
static GSList *file_formats = NULL;
-static void gdk_pixbuf_io_init ();
+static void gdk_pixbuf_io_init (void);
static GSList *
get_file_formats (void)
#ifdef G_OS_WIN32
-/* DllMain function needed to tuck away the gdk-pixbuf DLL name */
-G_WIN32_DLLMAIN_FOR_DLL_NAME (static, dll_name)
+/* DllMain function needed to tuck away the gdk-pixbuf DLL handle */
+
+static HMODULE gdk_pixbuf_dll;
+
+BOOL WINAPI
+DllMain (HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ gdk_pixbuf_dll = (HMODULE) hinstDLL;
+ break;
+ }
+
+ return TRUE;
+}
static char *
get_toplevel (void)
static char *toplevel = NULL;
if (toplevel == NULL)
- toplevel = g_win32_get_package_installation_subdirectory
- (GETTEXT_PACKAGE, dll_name, "");
+ toplevel = g_win32_get_package_installation_directory_of_module (gdk_pixbuf_dll);
return toplevel;
}
static char *sysconfdir = NULL;
if (sysconfdir == NULL)
- sysconfdir = g_win32_get_package_installation_subdirectory
- (GETTEXT_PACKAGE, dll_name, "etc");
+ sysconfdir = g_build_filename (get_toplevel (), "etc", NULL);
return sysconfdir;
}
if (strncmp (*path, GTK_PREFIX "/", strlen (GTK_PREFIX "/")) == 0 ||
strncmp (*path, GTK_PREFIX "\\", strlen (GTK_PREFIX "\\")) == 0)
{
+ gchar *tem = NULL;
+ if (strlen(*path) > 5 && strncmp (*path - 5, ".libs", 5) == 0)
+ {
+ /* We are being run from inside the build tree, and shouldn't mess about. */
+ return;
+ }
+
/* This is an entry put there by gdk-pixbuf-query-loaders on the
* packager's system. On Windows a prebuilt GTK+ package can be
* installed in a random location. The gdk-pixbuf.loaders file
* builder's machine. Replace the build-time prefix with the
* installation prefix on this machine.
*/
- gchar *tem = *path;
+ tem = *path;
*path = g_strconcat (get_toplevel (), tem + strlen (GTK_PREFIX), NULL);
g_free (tem);
}
return result;
}
+#endif /* USE_GMODULE */
+
+
+static gboolean
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+ GError **error);
+
static void
gdk_pixbuf_io_init (void)
{
+#ifdef USE_GMODULE
GIOChannel *channel;
gchar *line_buf;
gsize term;
int n_patterns = 0;
GdkPixbufModulePattern *pattern;
GError *error = NULL;
+#endif
+ GdkPixbufModule *builtin_module ;
+
+ /* initialize on separate line to avoid compiler warnings in the
+ * common case of no compiled-in modules.
+ */
+ builtin_module = NULL;
+
+#define load_one_builtin_module(format) \
+ builtin_module = g_new0 (GdkPixbufModule, 1); \
+ builtin_module->module_name = #format; \
+ if (gdk_pixbuf_load_module_unlocked (builtin_module, NULL)) \
+ file_formats = g_slist_prepend (file_formats, builtin_module);\
+ else \
+ g_free (builtin_module)
+
+#ifdef INCLUDE_ani
+ load_one_builtin_module (ani);
+#endif
+#ifdef INCLUDE_png
+ load_one_builtin_module (png);
+#endif
+#ifdef INCLUDE_bmp
+ load_one_builtin_module (bmp);
+#endif
+#ifdef INCLUDE_wbmp
+ load_one_builtin_module (wbmp);
+#endif
+#ifdef INCLUDE_gif
+ load_one_builtin_module (gif);
+#endif
+#ifdef INCLUDE_ico
+ load_one_builtin_module (ico);
+#endif
+#ifdef INCLUDE_jpeg
+ load_one_builtin_module (jpeg);
+#endif
+#ifdef INCLUDE_pnm
+ load_one_builtin_module (pnm);
+#endif
+#ifdef INCLUDE_ras
+ load_one_builtin_module (ras);
+#endif
+#ifdef INCLUDE_tiff
+ load_one_builtin_module (tiff);
+#endif
+#ifdef INCLUDE_xpm
+ load_one_builtin_module (xpm);
+#endif
+#ifdef INCLUDE_xbm
+ load_one_builtin_module (xbm);
+#endif
+#ifdef INCLUDE_tga
+ load_one_builtin_module (tga);
+#endif
+#ifdef INCLUDE_pcx
+ load_one_builtin_module (pcx);
+#endif
+#ifdef INCLUDE_icns
+ load_one_builtin_module (icns);
+#endif
+#ifdef INCLUDE_jasper
+ load_one_builtin_module (jasper);
+#endif
+#ifdef INCLUDE_qtif
+ load_one_builtin_module (qtif);
+#endif
+#ifdef INCLUDE_gdiplus
+ /* We don't bother having the GDI+ loaders individually selectable
+ * for building in or not.
+ */
+ load_one_builtin_module (ico);
+ load_one_builtin_module (wmf);
+ load_one_builtin_module (emf);
+ load_one_builtin_module (bmp);
+ load_one_builtin_module (gif);
+ load_one_builtin_module (jpeg);
+ load_one_builtin_module (tiff);
+#endif
+#ifdef INCLUDE_gdip_png
+ /* Except the gdip-png loader which normally isn't built at all even */
+ load_one_builtin_module (png);
+#endif
+#undef load_one_builtin_module
+
+#ifdef USE_GMODULE
channel = g_io_channel_new_file (filename, "r", &error);
if (!channel) {
- g_warning ("Cannot open pixbuf loader module file '%s': %s",
- filename, error->message);
+ /* Don't bother warning if we have some built-in loaders */
+ if (file_formats == NULL)
+ g_warning ("Cannot open pixbuf loader module file '%s': %s",
+ filename, error->message);
+ g_string_free (tmp_buf, TRUE);
+ g_free (filename);
return;
}
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;
g_string_free (tmp_buf, TRUE);
g_io_channel_unref (channel);
g_free (filename);
+#endif
}
-/* actually load the image handler - gdk_pixbuf_get_module only get a */
-/* reference to the module to load, it doesn't actually load it */
-/* perhaps these actions should be combined in one function */
-static gboolean
-_gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
- GError **error)
-{
- char *path;
- GModule *module;
- gpointer sym;
-
- g_return_val_if_fail (image_module->module == NULL, FALSE);
-
- path = image_module->module_path;
- module = g_module_open (path, G_MODULE_BIND_LAZY);
-
- if (!module) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_FAILED,
- _("Unable to load image-loading module: %s: %s"),
- path, g_module_error ());
- return FALSE;
- }
-
- image_module->module = module;
-
- if (g_module_symbol (module, "fill_vtable", &sym)) {
- GdkPixbufModuleFillVtableFunc func = (GdkPixbufModuleFillVtableFunc) sym;
- (* func) (image_module);
- return TRUE;
- } else {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_FAILED,
- _("Image-loading module %s does not export the proper interface; perhaps it's from a different GTK version?"),
- path);
- return FALSE;
- }
-}
-
-gboolean
-_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
- GError **error)
-{
- gboolean ret;
- gboolean locked = FALSE;
-
- /* be extra careful, maybe the module initializes
- * the thread system
- */
- if (g_threads_got_initialized)
- {
- G_LOCK (init_lock);
- locked = TRUE;
- }
- ret = _gdk_pixbuf_load_module_unlocked (image_module, error);
- if (locked)
- G_UNLOCK (init_lock);
- return ret;
-}
-
-#else /* !USE_GMODULE */
#define module(type) \
- extern void MODULE_ENTRY (type, fill_info) (GdkPixbufFormat *info); \
- extern void MODULE_ENTRY (type, fill_vtable) (GdkPixbufModule *module)
+ extern void _gdk_pixbuf__##type##_fill_info (GdkPixbufFormat *info); \
+ extern void _gdk_pixbuf__##type##_fill_vtable (GdkPixbufModule *module)
module (png);
-module (bmp);
-module (wbmp);
+module (jpeg);
module (gif);
module (ico);
module (ani);
-module (jpeg);
-module (pnm);
module (ras);
-module (tiff);
module (xpm);
+module (tiff);
+module (pnm);
+module (bmp);
+module (wbmp);
module (xbm);
module (tga);
module (pcx);
+module (icns);
+module (jasper);
+module (qtif);
+module (gdip_ico);
+module (gdip_wmf);
+module (gdip_emf);
+module (gdip_bmp);
+module (gdip_gif);
+module (gdip_jpeg);
+module (gdip_png);
+module (gdip_tiff);
+
+#undef module
-gboolean
-_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
- GError **error)
+/* actually load the image handler - gdk_pixbuf_get_module only get a */
+/* reference to the module to load, it doesn't actually load it */
+/* perhaps these actions should be combined in one function */
+static gboolean
+gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
+ GError **error)
{
GdkPixbufModuleFillInfoFunc fill_info = NULL;
GdkPixbufModuleFillVtableFunc fill_vtable = NULL;
-
- image_module->module = (void *) 1;
-
- if (FALSE) {
- /* Ugly hack so we can use else if unconditionally below ;-) */
- }
-
-#ifdef INCLUDE_png
- else if (strcmp (image_module->module_name, "png") == 0) {
- fill_info = MODULE_ENTRY (png, fill_info);
- fill_vtable = MODULE_ENTRY (png, fill_vtable);
+
+ if (image_module->module != NULL)
+ return TRUE;
+
+#define try_module(format,id) \
+ if (fill_info == NULL && \
+ strcmp (image_module->module_name, #format) == 0) { \
+ fill_info = _gdk_pixbuf__##id##_fill_info; \
+ fill_vtable = _gdk_pixbuf__##id##_fill_vtable; \
}
+#ifdef INCLUDE_png
+ try_module (png,png);
#endif
-
-#ifdef INCLUDE_bmp
- else if (strcmp (image_module->module_name, "bmp") == 0) {
- fill_info = MODULE_ENTRY (bmp, fill_info);
- fill_vtable = MODULE_ENTRY (bmp, fill_vtable);
- }
+#ifdef INCLUDE_bmp
+ try_module (bmp,bmp);
#endif
-
#ifdef INCLUDE_wbmp
- else if (strcmp (image_module->module_name, "wbmp") == 0) {
- fill_info = MODULE_ENTRY (wbmp, fill_info);
- fill_vtable = MODULE_ENTRY (wbmp, fill_vtable);
- }
+ try_module (wbmp,wbmp);
#endif
-
#ifdef INCLUDE_gif
- else if (strcmp (image_module->module_name, "gif") == 0) {
- fill_info = MODULE_ENTRY (gif, fill_info);
- fill_vtable = MODULE_ENTRY (gif, fill_vtable);
- }
+ try_module (gif,gif);
#endif
-
#ifdef INCLUDE_ico
- else if (strcmp (image_module->module_name, "ico") == 0) {
- fill_info = MODULE_ENTRY (ico, fill_info);
- fill_vtable = MODULE_ENTRY (ico, fill_vtable);
- }
+ try_module (ico,ico);
#endif
-
#ifdef INCLUDE_ani
- else if (strcmp (image_module->module_name, "ani") == 0) {
- fill_info = MODULE_ENTRY (ani, fill_info);
- fill_vtable = MODULE_ENTRY (ani, fill_vtable);
- }
+ try_module (ani,ani);
#endif
-
#ifdef INCLUDE_jpeg
- else if (strcmp (image_module->module_name, "jpeg") == 0) {
- fill_info = MODULE_ENTRY (jpeg, fill_info);
- fill_vtable = MODULE_ENTRY (jpeg, fill_vtable);
- }
+ try_module (jpeg,jpeg);
#endif
-
#ifdef INCLUDE_pnm
- else if (strcmp (image_module->module_name, "pnm") == 0) {
- fill_info = MODULE_ENTRY (pnm, fill_info);
- fill_vtable = MODULE_ENTRY (pnm, fill_vtable);
- }
+ try_module (pnm,pnm);
#endif
-
#ifdef INCLUDE_ras
- else if (strcmp (image_module->module_name, "ras") == 0) {
- fill_info = MODULE_ENTRY (ras, fill_info);
- fill_vtable = MODULE_ENTRY (ras, fill_vtable);
- }
+ try_module (ras,ras);
#endif
-
#ifdef INCLUDE_tiff
- else if (strcmp (image_module->module_name, "tiff") == 0) {
- fill_info = MODULE_ENTRY (tiff, fill_info);
- fill_vtable = MODULE_ENTRY (tiff, fill_vtable);
- }
+ try_module (tiff,tiff);
#endif
-
#ifdef INCLUDE_xpm
- else if (strcmp (image_module->module_name, "xpm") == 0) {
- fill_info = MODULE_ENTRY (xpm, fill_info);
- fill_vtable = MODULE_ENTRY (xpm, fill_vtable);
- }
+ try_module (xpm,xpm);
#endif
-
#ifdef INCLUDE_xbm
- else if (strcmp (image_module->module_name, "xbm") == 0) {
- fill_info = MODULE_ENTRY (xbm, fill_info);
- fill_vtable = MODULE_ENTRY (xbm, fill_vtable);
- }
+ try_module (xbm,xbm);
#endif
-
#ifdef INCLUDE_tga
- else if (strcmp (image_module->module_name, "tga") == 0) {
- fill_info = MODULE_ENTRY (tga, fill_info);
- fill_vtable = MODULE_ENTRY (tga, fill_vtable);
- }
+ try_module (tga,tga);
#endif
-
#ifdef INCLUDE_pcx
- else if (strcmp (image_module->module_name, "pcx") == 0) {
- fill_info = MODULE_ENTRY (pcx, fill_info);
- fill_vtable = MODULE_ENTRY (pcx, fill_vtable);
- }
+ try_module (pcx,pcx);
+#endif
+#ifdef INCLUDE_icns
+ try_module (icns,icns);
+#endif
+#ifdef INCLUDE_jasper
+ try_module (jasper,jasper);
+#endif
+#ifdef INCLUDE_qtif
+ try_module (qtif,qtif);
#endif
+#ifdef INCLUDE_gdiplus
+ try_module (ico,gdip_ico);
+ try_module (wmf,gdip_wmf);
+ try_module (emf,gdip_emf);
+ try_module (bmp,gdip_bmp);
+ try_module (gif,gdip_gif);
+ try_module (jpeg,gdip_jpeg);
+ try_module (tiff,gdip_tiff);
+#endif
+#ifdef INCLUDE_gdip_png
+ try_module (png,gdip_png);
+#endif
+
+#undef try_module
if (fill_vtable) {
+ image_module->module = (void *) 1;
(* fill_vtable) (image_module);
- image_module->info = g_new0 (GdkPixbufFormat, 1);
- (* fill_info) (image_module->info);
-
+ if (image_module->info == NULL) {
+ image_module->info = g_new0 (GdkPixbufFormat, 1);
+ (* fill_info) (image_module->info);
+ }
return TRUE;
- } else {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
- _("Image type '%s' is not supported"),
- image_module->module_name);
-
- return FALSE;
- }
+ }
+ else
+#ifdef USE_GMODULE
+ {
+ char *path;
+ GModule *module;
+ gpointer sym;
+
+ path = image_module->module_path;
+ module = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+
+ if (!module) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Unable to load image-loading module: %s: %s"),
+ path, g_module_error ());
+ return FALSE;
+ }
+
+ image_module->module = module;
+
+ if (g_module_symbol (module, "fill_vtable", &sym)) {
+ fill_vtable = (GdkPixbufModuleFillVtableFunc) sym;
+ (* fill_vtable) (image_module);
+ return TRUE;
+ } else {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Image-loading module %s does not export the proper interface; perhaps it's from a different GTK version?"),
+ path);
+ return FALSE;
+ }
+ }
+#else
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+ _("Image type '%s' is not supported"),
+ image_module->module_name);
+ return FALSE;
+#endif /* !USE_GMODULE */
}
-static void
-gdk_pixbuf_io_init ()
+
+gboolean
+_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
+ GError **error)
{
- gchar *included_formats[] = {
- "ani", "png", "bmp", "wbmp", "gif",
- "ico", "jpeg", "pnm", "ras", "tiff",
- "xpm", "xbm", "tga", "pcx",
- NULL
- };
- gchar **name;
- GdkPixbufModule *module = NULL;
+ gboolean ret;
+ gboolean locked = FALSE;
- for (name = included_formats; *name; name++) {
- module = g_new0 (GdkPixbufModule, 1);
- module->module_name = *name;
- if (_gdk_pixbuf_load_module (module, NULL))
- file_formats = g_slist_prepend (file_formats, module);
- else
- g_free (module);
+ /* be extra careful, maybe the module initializes
+ * the thread system
+ */
+ if (g_threads_got_initialized) {
+ G_LOCK (init_lock);
+ locked = TRUE;
}
-}
-#endif /* !USE_GMODULE */
+ ret = gdk_pixbuf_load_module_unlocked (image_module, error);
+
+ if (locked)
+ G_UNLOCK (init_lock);
+
+ return ret;
+}
\f
{
GSList *modules;
- gint score, best = 0;
GdkPixbufModule *selected = NULL;
gchar *display_name = NULL;
+#ifdef GDK_PIXBUF_USE_GIO_MIME
+ gchar *mime_type;
+ gchar **mimes;
+ gchar *type;
+ gint j;
+ gboolean uncertain;
+
+ mime_type = g_content_type_guess (NULL, buffer, size, &uncertain);
+ if (uncertain)
+ mime_type = g_content_type_guess (filename, buffer, size, NULL);
+
+ for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
+ GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
+ GdkPixbufFormat *info = module->info;
+
+ if (info->disabled)
+ continue;
+
+ mimes = info->mime_types;
+ for (j = 0; mimes[j] != NULL; j++) {
+ type = g_content_type_from_mime_type (mimes[j]);
+ if (g_ascii_strcasecmp (type, mime_type) == 0) {
+ g_free (type);
+ selected = module;
+ break;
+ }
+ g_free (type);
+ }
+ }
+ g_free (mime_type);
+#else
+ gint score, best = 0;
for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
if (score >= 100)
break;
}
+#endif
+
if (selected != NULL)
return selected;
g_free (display_name);
}
else
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
- _("Unrecognized image file format"));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+ _("Unrecognized image file format"));
return NULL;
FILE *f,
GError **error)
{
- guchar buffer[4096];
+ guchar buffer[LOAD_BUFFER_SIZE];
size_t length;
GdkPixbuf *pixbuf = NULL;
GdkPixbufAnimation *animation = NULL;
GdkPixbuf *pixbuf;
int size;
FILE *f;
- guchar buffer[256];
+ guchar buffer[SNIFF_BUFFER_SIZE];
GdkPixbufModule *image_module;
gchar *display_name;
f = g_fopen (filename, "rb");
if (!f) {
+ gint save_errno = errno;
g_set_error (error,
G_FILE_ERROR,
- g_file_error_from_errno (errno),
+ g_file_error_from_errno (save_errno),
_("Failed to open file '%s': %s"),
display_name,
- g_strerror (errno));
+ g_strerror (save_errno));
g_free (display_name);
return NULL;
}
return NULL;
}
- if (image_module->module == NULL)
- if (!_gdk_pixbuf_load_module (image_module, error)) {
- g_free (display_name);
- fclose (f);
- return NULL;
- }
+ if (!_gdk_pixbuf_load_module (image_module, error)) {
+ g_free (display_name);
+ fclose (f);
+ return NULL;
+ }
fseek (f, 0, SEEK_SET);
pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
}
#endif
-static void
-size_prepared_cb (GdkPixbufLoader *loader,
- int width,
- int height,
- gpointer data)
-{
- struct {
- gint width;
- gint height;
- gboolean preserve_aspect_ratio;
- } *info = data;
-
- g_return_if_fail (width > 0 && height > 0);
-
- if (info->preserve_aspect_ratio &&
- (info->width > 0 || info->height > 0)) {
- if (info->width < 0)
- {
- width = width * (double)info->height/(double)height;
- height = info->height;
- }
- else if (info->height < 0)
- {
- height = height * (double)info->width/(double)width;
- width = info->width;
- }
- else if ((double)height * (double)info->width >
- (double)width * (double)info->height) {
- width = 0.5 + (double)width * (double)info->height / (double)height;
- height = info->height;
- } else {
- height = 0.5 + (double)height * (double)info->width / (double)width;
- width = info->width;
- }
- } else {
- if (info->width > 0)
- width = info->width;
- if (info->height > 0)
- height = info->height;
- }
-
- gdk_pixbuf_loader_set_size (loader, width, height);
-}
/**
* gdk_pixbuf_new_from_file_at_size:
* @height: The height the image should have or -1 to not constrain the height
* @error: Return location for an error
*
- * Creates a new pixbuf by loading an image from a file. The file format is
- * detected automatically. If %NULL is returned, then @error will be set.
- * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
+ * Creates a new pixbuf by loading an image from a file.
+ * The file format is detected automatically. If %NULL is returned, then
+ * @error will be set. Possible errors are in the #GDK_PIXBUF_ERROR and
+ * #G_FILE_ERROR domains.
+ *
* The image will be scaled to fit in the requested size, preserving
- * the image's aspect ratio.
+ * the image's aspect ratio. Note that the returned pixbuf may be smaller
+ * than @width x @height, if the aspect ratio requires it. To load
+ * and image at the requested size, regardless of aspect ratio, use
+ * gdk_pixbuf_new_from_file_at_scale().
*
* Return value: A newly-created pixbuf with a reference count of 1, or
* %NULL if any of several error conditions occurred: the file could not
}
#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
GdkPixbufLoader *loader;
GdkPixbuf *pixbuf;
-
- guchar buffer [4096];
+ guchar buffer[LOAD_BUFFER_SIZE];
int length;
FILE *f;
- struct {
- gint width;
- gint height;
- gboolean preserve_aspect_ratio;
- } info;
+ AtScaleData info;
+ GdkPixbufAnimation *animation;
+ GdkPixbufAnimationIter *iter;
+ gboolean has_frame;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (width > 0 || width == -1, NULL);
f = g_fopen (filename, "rb");
if (!f) {
+ gint save_errno = errno;
gchar *display_name = g_filename_display_name (filename);
g_set_error (error,
G_FILE_ERROR,
- g_file_error_from_errno (errno),
+ g_file_error_from_errno (save_errno),
_("Failed to open file '%s': %s"),
display_name,
- g_strerror (errno));
+ g_strerror (save_errno));
g_free (display_name);
return NULL;
}
info.height = height;
info.preserve_aspect_ratio = preserve_aspect_ratio;
- g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info);
+ g_signal_connect (loader, "size-prepared",
+ G_CALLBACK (at_scale_size_prepared_cb), &info);
- while (!feof (f) && !ferror (f)) {
+ has_frame = FALSE;
+ while (!has_frame && !feof (f) && !ferror (f)) {
length = fread (buffer, 1, sizeof (buffer), f);
if (length > 0)
if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
g_object_unref (loader);
return NULL;
}
+
+ animation = gdk_pixbuf_loader_get_animation (loader);
+ if (animation) {
+ iter = gdk_pixbuf_animation_get_iter (animation, NULL);
+ if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
+ has_frame = TRUE;
+ }
+ g_object_unref (iter);
+ }
}
fclose (f);
- if (!gdk_pixbuf_loader_close (loader, error)) {
+ if (!gdk_pixbuf_loader_close (loader, error) && !has_frame) {
g_object_unref (loader);
return NULL;
}
#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,
gint *height)
{
GdkPixbufLoader *loader;
- guchar buffer [4096];
+ guchar buffer[SNIFF_BUFFER_SIZE];
int length;
FILE *f;
struct {
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);
return NULL;
}
- if (xpm_module->module == NULL) {
- if (!_gdk_pixbuf_load_module (xpm_module, &error)) {
- g_warning ("Error loading XPM image loader: %s", error->message);
- g_error_free (error);
- return NULL;
- }
+ if (!_gdk_pixbuf_load_module (xpm_module, &error)) {
+ g_warning ("Error loading XPM image loader: %s", error->message);
+ g_error_free (error);
+ return NULL;
}
locked = _gdk_pixbuf_lock (xpm_module);
n = fwrite (buf, 1, count, filehandle);
if (n != count) {
+ gint save_errno = errno;
g_set_error (error,
G_FILE_ERROR,
- g_file_error_from_errno (errno),
+ g_file_error_from_errno (save_errno),
_("Error writing to image file: %s"),
- g_strerror (errno));
+ g_strerror (save_errno));
return FALSE;
}
return TRUE;
if (image_module == NULL)
return FALSE;
- if (image_module->module == NULL)
- if (!_gdk_pixbuf_load_module (image_module, error))
- return FALSE;
+ if (!_gdk_pixbuf_load_module (image_module, error))
+ return FALSE;
locked = _gdk_pixbuf_lock (image_module);
buf = g_try_malloc (TMP_FILE_BUF_SIZE);
if (buf == NULL) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Insufficient memory to save image to callback"));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image to callback"));
goto end;
}
goto end;
f = fdopen (fd, "wb+");
if (f == NULL) {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to open temporary file"));
+ gint save_errno = errno;
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (save_errno),
+ _("Failed to open temporary file"));
goto end;
}
break;
}
if (ferror (f)) {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- _("Failed to read from temporary file"));
+ gint save_errno = errno;
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (save_errno),
+ _("Failed to read from temporary file"));
goto end;
}
retval = TRUE;
if (image_module == NULL)
return FALSE;
- if (image_module->module == NULL)
- if (!_gdk_pixbuf_load_module (image_module, error))
- return FALSE;
+ if (!_gdk_pixbuf_load_module (image_module, error))
+ return FALSE;
locked = _gdk_pixbuf_lock (image_module);
* installed. The list of all writable formats can be determined in the
* following way:
*
- * <informalexample><programlisting>
+ * |[
* void add_if_writable (GdkPixbufFormat *data, GSList **list)
* {
* if (gdk_pixbuf_format_is_writable (data))
* *list = g_slist_prepend (*list, data);
* }
- * <!-- -->
- * GSList *formats = gdk_pixbuf_get_formats (<!-- -->);
+ *
+ * GSList *formats = gdk_pixbuf_get_formats ();
* GSList *writable_formats = NULL;
- * g_slist_foreach (formats, add_if_writable, &writable_formats);
+ * g_slist_foreach (formats, add_if_writable, &writable_formats);
* g_slist_free (formats);
- * </programlisting></informalexample>
+ * ]|
*
* If @error is set, %FALSE will be returned. Possible errors include
* those in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
* be specified using the "compression" parameter; it's value is in an
* integer in the range of [0,9].
*
+ * ICC color profiles can also be embedded into PNG images.
+ * The "icc-profile" value should be the complete ICC profile encoded
+ * into base64.
+ *
+ * <informalexample><programlisting>
+ * gchar *contents;
+ * gchar *contents_encode;
+ * gsize length;
+ * g_file_get_contents ("/home/hughsie/.color/icc/L225W.icm", &contents, &length, NULL);
+ * contents_encode = g_base64_encode ((const guchar *) contents, length);
+ * gdk_pixbuf_save (pixbuf, handle, "png", &error,
+ * "icc-profile", contents_encode,
+ * NULL);
+ * </programlisting></informalexample>
+ *
+ * TIFF images recognize a "compression" option which acceps an integer value.
+ * Among the codecs are 1 None, 2 Huffman, 5 LZW, 7 JPEG and 8 Deflate, see
+ * the libtiff documentation and tiff.h for all supported codec values.
+ *
* ICO images can be saved in depth 16, 24, or 32, by using the "depth"
* parameter. When the ICO saver is given "x_hot" and "y_hot" parameters,
* it produces a CUR instead of an ICO.
* @option_values: values for named options
* @error: return location for error, or %NULL
*
- * Saves pixbuf to a file in @type, which is currently "jpeg", "png", "ico" or "bmp".
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png", "tiff", "ico" or "bmp".
* If @error is set, %FALSE will be returned.
* See gdk_pixbuf_save () for more details.
*
f = g_fopen (filename, "wb");
if (f == NULL) {
+ gint save_errno = errno;
gchar *display_name = g_filename_display_name (filename);
g_set_error (error,
G_FILE_ERROR,
- g_file_error_from_errno (errno),
+ g_file_error_from_errno (save_errno),
_("Failed to open '%s' for writing: %s"),
display_name,
- g_strerror (errno));
+ g_strerror (save_errno));
g_free (display_name);
return FALSE;
}
}
if (fclose (f) < 0) {
+ gint save_errno = errno;
gchar *display_name = g_filename_display_name (filename);
g_set_error (error,
G_FILE_ERROR,
- g_file_error_from_errno (errno),
+ g_file_error_from_errno (save_errno),
_("Failed to close '%s' while writing image, all data may not have been saved: %s"),
display_name,
- g_strerror (errno));
+ g_strerror (save_errno));
g_free (display_name);
return FALSE;
}
* @error: return location for error, or %NULL
*
* Saves pixbuf to a callback in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp". If @error is set, %FALSE will be returned. See
+ * "png", "tiff", "ico" or "bmp". If @error is set, %FALSE will be returned. See
* gdk_pixbuf_save_to_callback () for more details.
*
* Return value: whether an error was set
* @Varargs: list of key-value save options
*
* Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp". This is a convenience function that uses
+ * "png", "tiff", "ico" or "bmp". This is a convenience function that uses
* gdk_pixbuf_save_to_callback() to do the real work. Note that the buffer
* is not nul-terminated and may contain embedded nuls.
- * If @error is set, %FALSE will be returned and @string will be set to
+ * If @error is set, %FALSE will be returned and @buffer will be set to
* %NULL. Possible errors include those in the #GDK_PIXBUF_ERROR
* domain.
*
new_max = MAX (sdata->max*2, sdata->len + count);
new_buffer = g_try_realloc (sdata->buffer, new_max);
if (!new_buffer) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Insufficient memory to save image into a buffer"));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image into a buffer"));
return FALSE;
}
sdata->buffer = new_buffer;
* @error: return location for error, or %NULL
*
* Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer() for more details.
+ * "tiff", "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer()
+ * for more details.
*
* Return value: whether an error was set
*
sdata.max = initial_max;
sdata.len = 0;
if (!sdata.buffer) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Insufficient memory to save image into a buffer"));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to save image into a buffer"));
return FALSE;
}
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
gdk_pixbuf_format_get_description (GdkPixbufFormat *format)
{
gchar *domain;
- gchar *description;
+ const gchar *description;
g_return_val_if_fail (format != NULL, NULL);
if (format->domain != NULL)
domain = format->domain;
else
domain = GETTEXT_PACKAGE;
- description = dgettext (domain, format->description);
+ description = g_dgettext (domain, format->description);
return g_strdup (description);
}
* gdk_pixbuf_format_get_license:
* @format: a #GdkPixbufFormat
*
- * Returns information about the license of the image loader
- * for the format. The returned string should be a shorthand for
- * a wellknown license, e.g. "LGPL", "GPL", "QPL", "GPL/QPL",
- * or "other" to indicate some other license.
+ * Returns information about the license of the image loader for the format. The
+ * returned string should be a shorthand for a wellknown license, e.g. "LGPL",
+ * "GPL", "QPL", "GPL/QPL", or "other" to indicate some other license. This
+ * string should be freed with g_free() when it's no longer needed.
*
* Returns: a string describing the license of @format.
*
#define __GDK_PIXBUF_IO_C__
#include "gdk-pixbuf-aliasdef.c"
-
-
-
-