gtk2/gdk-pixbuf/gdk-pixbuf-io.c
Manish Singh 4bd1fa5ff3 removed unnecessary G_OBJECT() cast checks.
Sun Feb  8 01:07:20 2004  Manish Singh  <yosh@gimp.org>

        * gdk-pixbuf-io.c: removed unnecessary G_OBJECT() cast checks.

        * io-jpeg.c: make the return type for to_callback_empty_output_buffer
        "boolean", which should be defined by the jpeg headers. The right
        return type for this function depends on how the jpeg library
        was built (one wonders what happens if different compilers are used).
2004-02-08 09:13:18 +00:00

1793 lines
46 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- mode: C; c-file-style: "linux" -*- */
/* GdkPixbuf library - Main loading interface.
*
* Copyright (C) 1999 The Free Software Foundation
*
* Authors: Miguel de Icaza <miguel@gnu.org>
* Federico Mena-Quintero <federico@gimp.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 "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 "gdk-pixbuf-private.h"
#include "gdk-pixbuf-io.h"
#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
#undef STRICT
#endif
static gint
format_check (GdkPixbufModule *module, guchar *buffer, int size)
{
int j;
gchar m;
GdkPixbufModulePattern *pattern;
for (pattern = module->info->signature; pattern->prefix; pattern++) {
for (j = 0; j < size && pattern->prefix[j] != 0; j++) {
m = pattern->mask ? pattern->mask[j] : ' ';
if (m == ' ') {
if (buffer[j] != pattern->prefix[j])
break;
}
else if (m == '!') {
if (buffer[j] == pattern->prefix[j])
break;
}
else if (m == 'z') {
if (buffer[j] != 0)
break;
}
else if (m == 'n') {
if (buffer[j] == 0)
break;
}
}
if (pattern->prefix[j] == 0)
return pattern->relevance;
}
return 0;
}
static GSList *file_formats = NULL;
static void gdk_pixbuf_io_init ();
static GSList *
get_file_formats ()
{
if (file_formats == NULL)
gdk_pixbuf_io_init ();
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');
}
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;
}
static void
gdk_pixbuf_io_init ()
{
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;
channel = g_io_channel_new_file (filename, "r", &error);
if (!channel) {
g_warning ("Can not 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 != '#') {
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] = 0;
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] = 0;
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);
}
#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_libdir (void)
{
static char *libdir = NULL;
if (libdir == NULL)
libdir = g_win32_get_package_installation_subdirectory
(GETTEXT_PACKAGE, dll_name, "lib\\gtk-2.0\\" GTK_BINARY_VERSION "\\loaders");
return libdir;
}
#undef PIXBUF_LIBDIR
#define PIXBUF_LIBDIR get_libdir ()
#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 */
gboolean
_gdk_pixbuf_load_module (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;
}
}
#else
#define module(type) \
extern void MODULE_ENTRY (type, fill_info) (GdkPixbufFormat *info); \
extern void MODULE_ENTRY (type, fill_vtable) (GdkPixbufModule *module)
module (png);
module (bmp);
module (wbmp);
module (gif);
module (ico);
module (ani);
module (jpeg);
module (pnm);
module (ras);
module (tiff);
module (xpm);
module (xbm);
module (tga);
module (pcx);
gboolean
_gdk_pixbuf_load_module (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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#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);
}
#endif
if (fill_vtable) {
(* fill_vtable) (image_module);
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;
}
}
static void
gdk_pixbuf_io_init ()
{
gchar *included_formats[] = {
"ani", "png", "bmp", "wbmp", "gif",
"ico", "jpeg", "pnm", "ras", "tiff",
"xpm", "xbm", "tga", "pcx",
NULL
};
gchar **name;
GdkPixbufModule *module = NULL;
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);
}
}
#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 (!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;
for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
score = format_check (module, buffer, size);
if (score > best) {
best = score;
selected = module;
}
if (score >= 100)
break;
}
if (selected != NULL)
return selected;
if (filename)
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("Couldn't recognize the image file format for file '%s'"),
filename);
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[4096];
size_t length;
GdkPixbuf *pixbuf = NULL;
GdkPixbufAnimation *animation = NULL;
gpointer context;
if (module->load != NULL)
return (* module->load) (f, error);
if (module->begin_load != NULL) {
context = module->begin_load (NULL, prepared_notify, NULL, &pixbuf, error);
if (!context)
return NULL;
while (!feof (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);
return NULL;
}
}
if (!module->stop_load (context, error)) {
if (pixbuf != NULL)
g_object_unref (pixbuf);
return NULL;
}
return pixbuf;
}
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);
return pixbuf;
}
}
return NULL;
}
/**
* gdk_pixbuf_new_from_file:
* @filename: Name of file to load.
* @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 [128];
GdkPixbufModule *image_module;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
f = fopen (filename, "rb");
if (!f) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to open file '%s': %s"),
filename, g_strerror (errno));
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"),
filename);
fclose (f);
return NULL;
}
image_module = _gdk_pixbuf_get_module (buffer, size, filename, error);
if (image_module == NULL) {
fclose (f);
return NULL;
}
if (image_module->module == NULL)
if (!_gdk_pixbuf_load_module (image_module, error)) {
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 invariastable/gdk-pixbuf/nt 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"),
filename);
} 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"),
filename, old);
g_free (old);
}
return pixbuf;
}
static void
size_prepared_cb (GdkPixbufLoader *loader,
int width,
int height,
gpointer data)
{
struct {
int width;
int height;
} *info = data;
g_return_if_fail (width > 0 && height > 0);
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;
}
gdk_pixbuf_loader_set_size (loader, width, height);
}
/**
* gdk_pixbuf_new_from_file_at_size:
* @filename: Name of file to load.
* @width: The width the image should have
* @height: The height the image should have
* @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 its 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)
{
GdkPixbufLoader *loader;
GdkPixbuf *pixbuf;
guchar buffer [4096];
int length;
FILE *f;
struct {
gint width;
gint height;
} info;
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (width > 0 && height > 0, NULL);
f = fopen (filename, "rb");
if (!f) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to open file '%s': %s"),
filename, g_strerror (errno));
return NULL;
}
loader = gdk_pixbuf_loader_new ();
info.width = width;
info.height = height;
g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info);
while (!feof (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;
}
}
fclose (f);
if (!gdk_pixbuf_loader_close (loader, error)) {
g_object_unref (loader);
return NULL;
}
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
if (!pixbuf) {
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"),
filename);
return NULL;
}
g_object_ref (pixbuf);
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 [4096];
int length;
FILE *f;
struct {
GdkPixbufFormat *format;
gint width;
gint height;
} info;
g_return_val_if_fail (filename != NULL, NULL);
f = 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)) {
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 = _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;
}
}
if (xpm_module->load_xpm_data == NULL) {
g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
return NULL;
} else
load_xpm_data = xpm_module->load_xpm_data;
pixbuf = (* load_xpm_data) (data);
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) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Error writing to image file: %s"),
g_strerror (errno));
return FALSE;
}
return TRUE;
}
static gboolean
gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
FILE *filehandle,
const char *type,
gchar **keys,
gchar **values,
GError **error)
{
GdkPixbufModule *image_module = NULL;
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;
if (image_module->save) {
/* save normally */
return (* image_module->save) (filehandle, pixbuf,
keys, values,
error);
}
else if (image_module->save_to_callback) {
/* save with simple callback */
return (* 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);
return FALSE;
}
}
#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;
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) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to open temporary file"));
goto end;
}
if (!(* image_module->save) (f, pixbuf, keys, values, error))
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)) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to read from temporary file"));
goto end;
}
retval = TRUE;
end:
/* cleanup and return retval */
if (f)
fclose (f);
if (filename) {
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)
{
GdkPixbufModule *image_module = NULL;
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;
if (image_module->save_to_callback) {
/* save normally */
return (* image_module->save_to_callback) (save_func, user_data,
pixbuf, keys, values,
error);
}
else if (image_module->save) {
/* use a temporary file */
return 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);
return FALSE;
}
}
/**
* 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" and
* "ico" 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:
*
* <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 *writable_formats = NULL;
* g_slist_foreach (formats, add_if_writable, &amp;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.
*
* The variable argument list should be %NULL-terminated; if not empty,
* it should contain pairs of strings that modify the save
* parameters. For example:
* <informalexample><programlisting>
* gdk_pixbuf_save (pixbuf, handle, "jpeg", &amp;error,
* "quality", "100", NULL);
* </programlisting></informalexample>
*
* Currently only few parameters exist. JPEG images can be saved with a
* "quality" parameter; its value should be in the range [0,100].
* Text chunks can be attached to PNG images by specifying parameters of
* the form "tEXt::key", where key is an ASCII string of length 1-79.
* The values are UTF-8 encoded strings.
* 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;
}
/**
* 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" or "ico".
* 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 = fopen (filename, "wb");
if (f == NULL) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to open '%s' for writing: %s"),
filename, g_strerror (errno));
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) {
g_set_error (error,
G_FILE_ERROR,
g_file_error_from_errno (errno),
_("Failed to close '%s' while writing image, all data may not have been saved: %s"),
filename, g_strerror (errno));
return FALSE;
}
return TRUE;
}
/**
* 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" or "ico". 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" or "ico". 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",
* "png" or "ico". 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;
}
/**
* 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;
}
GdkPixbufFormat *
_gdk_pixbuf_get_format (GdkPixbufModule *module)
{
g_return_val_if_fail (module != NULL, NULL);
return module->info;
}
/**
* gdk_pixbuf_get_formats:
*
* Obtains the available information about the image formats supported
* by GdkPixbuf.
*
* Returns: A list of #GdkPixbufFormat<!-- -->s describing the supported
* image formats. The list should be freed when it is no longer needed,
* but the structures themselves are owned by #GdkPixbuf and should not be
* freed.
*
* Since: 2.2
*/
GSList *
gdk_pixbuf_get_formats (void)
{
GSList *result = NULL;
GSList *modules;
for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
GdkPixbufFormat *info = _gdk_pixbuf_get_format (module);
result = g_slist_prepend (result, info);
}
return result;
}