Commit cb918cdb authored by Matthias Clasen's avatar Matthias Clasen Committed by Matthias Clasen

Changes to make gdk-pixbuf threadsafe (#157310, #157306, Colin Walters):

2004-11-12  Matthias Clasen  <mclasen@redhat.com>

	Changes to make gdk-pixbuf threadsafe  (#157310, #157306,
	Colin Walters):

	* gdk-pixbuf-io.h (enum GdkPixbufFormatFlags): Add
	GDK_PIXBUF_FORMAT_THREADSAFE to indicate that an image loader
	is threadsafe.

	* gdk-pixbuf-io.c (get_file_formats, _gdk_pixbuf_load_module):
	Use a lock to make initialization of global data structures
	threadsafe.
	* gdk-pixbuf-private.h:
	* gdk-pixbuf-io.c (_gdk_pixbuf_lock, _gdk_pixbuf_unlock):
	Auxiliary functions which use another lock to protect
	threadunsafe image loaders.

	* gdk-pixbuf-io.c (gdk_pixbuf_real_save):
	(save_to_callback_with_tmp_file):
	(gdk_pixbuf_real_save_to_callback):
	(gdk_pixbuf_new_from_xpm_data):
	(_gdk_pixbuf_generic_image_load):
	* gdk-pixbuf-animation.c (gdk_pixbuf_animation_new_from_file):
	* gdk-pixbuf-loader.c (gdk_pixbuf_loader_load_module):
	(gdk_pixbuf_loader_close):
	(gdk_pixbuf_loader_finalize):
	Use _gdk_pixbuf_lock() and _gdk_pixbuf_unlock().

	* io-ani.c, io-bmp.c, io-gif.c, io-ico.c:
	* io-jpeg.c, io-pcx.c, io-png.c, io-pnm.c:
	* io-ras.c, io-tga.c, io-wbmp.c, io-xbm.c:
	* io-xpm.c: Mark as threadsafe.

	* io-tiff.c: Remove pointless locking, mark as
	threadunsafe.
parent 26cbda1b
2004-11-12 Matthias Clasen <mclasen@redhat.com>
* gdk-pixbuf/tmpl/module_interface.sgml: Document
GDK_PIXBUF_FORMAT_THREADSAFE.
2004-11-09 Matthias Clasen <mclasen@redhat.com>
* gtk/gtk-sections.txt: Add gtk_action_get_accel_path.
......
......@@ -193,6 +193,9 @@ operations.
@GDK_PIXBUF_FORMAT_WRITABLE: the module can write out images in the format.
@GDK_PIXBUF_FORMAT_SCALABLE: the image format is scalable
@GDK_PIXBUF_FORMAT_THREADSAFE: the module is threadsafe. If this flag is not
set, &gdk-pixbuf; will use a lock to prevent multiple threads from using
this module at the same time. (Since 2.6)
@Since: 2.2
<!-- ##### STRUCT GdkPixbufModulePattern ##### -->
......
2004-11-12 Matthias Clasen <mclasen@redhat.com>
Changes to make gdk-pixbuf threadsafe (#157310, #157306,
Colin Walters):
* gdk-pixbuf-io.h (enum GdkPixbufFormatFlags): Add
GDK_PIXBUF_FORMAT_THREADSAFE to indicate that an image loader
is threadsafe.
* gdk-pixbuf-io.c (get_file_formats, _gdk_pixbuf_load_module):
Use a lock to make initialization of global data structures
threadsafe.
* gdk-pixbuf-private.h:
* gdk-pixbuf-io.c (_gdk_pixbuf_lock, _gdk_pixbuf_unlock):
Auxiliary functions which use another lock to protect
threadunsafe image loaders.
* gdk-pixbuf-io.c (gdk_pixbuf_real_save):
(save_to_callback_with_tmp_file):
(gdk_pixbuf_real_save_to_callback):
(gdk_pixbuf_new_from_xpm_data):
(_gdk_pixbuf_generic_image_load):
* gdk-pixbuf-animation.c (gdk_pixbuf_animation_new_from_file):
* gdk-pixbuf-loader.c (gdk_pixbuf_loader_load_module):
(gdk_pixbuf_loader_close):
(gdk_pixbuf_loader_finalize):
Use _gdk_pixbuf_lock() and _gdk_pixbuf_unlock().
* io-ani.c, io-bmp.c, io-gif.c, io-ico.c:
* io-jpeg.c, io-pcx.c, io-png.c, io-pnm.c:
* io-ras.c, io-tga.c, io-wbmp.c, io-xbm.c:
* io-xpm.c: Mark as threadsafe.
* io-tiff.c: Remove pointless locking, mark as
threadunsafe.
2004-11-10 Matthias Clasen <mclasen@redhat.com>
* gdk-pixbuf-animation.c:
......
......@@ -181,6 +181,8 @@ gdk_pixbuf_animation_new_from_file (const char *filename,
return NULL;
}
_gdk_pixbuf_lock (image_module);
if (image_module->load_animation == NULL) {
GdkPixbuf *pixbuf;
......@@ -208,7 +210,8 @@ gdk_pixbuf_animation_new_from_file (const char *filename,
if (pixbuf == NULL) {
g_free (display_name);
return NULL;
animation = NULL;
goto out_unlock;
}
animation = gdk_pixbuf_non_anim_new (pixbuf);
......@@ -241,6 +244,8 @@ gdk_pixbuf_animation_new_from_file (const char *filename,
g_free (display_name);
out_unlock:
_gdk_pixbuf_unlock (image_module);
return animation;
}
......
......@@ -76,6 +76,25 @@ format_check (GdkPixbufModule *module, guchar *buffer, int size)
return 0;
}
G_LOCK_DEFINE_STATIC (init_lock);
G_LOCK_DEFINE_STATIC (threadunsafe_loader_lock);
void
_gdk_pixbuf_lock (GdkPixbufModule *image_module)
{
if (!(image_module->info->flags & GDK_PIXBUF_FORMAT_THREADSAFE)) {
G_LOCK (threadunsafe_loader_lock);
}
}
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 ();
......@@ -83,8 +102,10 @@ static void gdk_pixbuf_io_init ();
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;
}
......@@ -396,9 +417,9 @@ gdk_pixbuf_io_init (void)
/* 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)
static gboolean
_gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
GError **error)
{
char *path;
GModule *module;
......@@ -433,6 +454,19 @@ _gdk_pixbuf_load_module (GdkPixbufModule *image_module,
return FALSE;
}
}
gboolean
_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
GError **error)
{
gboolean ret;
G_LOCK (init_lock);
ret = _gdk_pixbuf_load_module_unlocked (image_module, error);
G_UNLOCK (init_lock);
return ret;
}
#else
#define module(type) \
......@@ -593,11 +627,11 @@ gdk_pixbuf_io_init ()
};
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))
if (_gdk_pixbuf_load_module_unlocked (module, NULL))
file_formats = g_slist_prepend (file_formats, module);
else
g_free (module);
......@@ -703,37 +737,37 @@ _gdk_pixbuf_generic_image_load (GdkPixbufModule *module,
GdkPixbufAnimation *animation = NULL;
gpointer context;
if (module->load != NULL)
return (* module->load) (f, error);
if (module->begin_load != NULL) {
_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)
return NULL;
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)
if (pixbuf != NULL) {
g_object_unref (pixbuf);
return NULL;
pixbuf = NULL;
}
goto out;
}
}
if (!module->stop_load (context, error)) {
if (pixbuf != NULL)
if (pixbuf != NULL) {
g_object_unref (pixbuf);
return NULL;
pixbuf = NULL;
}
}
return pixbuf;
}
if (module->load_animation != NULL) {
} else if (module->load_animation != NULL) {
animation = (* module->load_animation) (f, error);
if (animation != NULL) {
pixbuf = gdk_pixbuf_animation_get_static_image (animation);
......@@ -741,12 +775,12 @@ _gdk_pixbuf_generic_image_load (GdkPixbufModule *module,
g_object_ref (pixbuf);
g_object_unref (animation);
return pixbuf;
}
}
return NULL;
out:
_gdk_pixbuf_unlock (module);
return pixbuf;
}
/**
......@@ -1134,14 +1168,18 @@ gdk_pixbuf_new_from_xpm_data (const char **data)
return NULL;
}
}
_gdk_pixbuf_lock (xpm_module);
if (xpm_module->load_xpm_data == NULL) {
g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
return NULL;
} else
pixbuf = NULL;
} else {
load_xpm_data = xpm_module->load_xpm_data;
pixbuf = (* load_xpm_data) (data);
}
pixbuf = (* load_xpm_data) (data);
_gdk_pixbuf_unlock (xpm_module);
return pixbuf;
}
......@@ -1210,39 +1248,43 @@ gdk_pixbuf_real_save (GdkPixbuf *pixbuf,
gchar **values,
GError **error)
{
GdkPixbufModule *image_module = NULL;
gboolean ret;
GdkPixbufModule *image_module = NULL;
image_module = _gdk_pixbuf_get_named_module (type, error);
image_module = _gdk_pixbuf_get_named_module (type, error);
if (image_module == NULL)
return FALSE;
if (image_module == NULL)
return FALSE;
if (image_module->module == NULL)
if (!_gdk_pixbuf_load_module (image_module, error))
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,
_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 */
return (* image_module->save_to_callback) (save_to_file_callback,
} 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);
return FALSE;
}
} 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;
}
_gdk_pixbuf_unlock (image_module);
return ret;
}
#define TMP_FILE_BUF_SIZE 4096
......@@ -1283,8 +1325,13 @@ save_to_callback_with_tmp_file (GdkPixbufModule *image_module,
_("Failed to open temporary file"));
goto end;
}
if (!(* image_module->save) (f, pixbuf, keys, values, error))
_gdk_pixbuf_lock (image_module);
retval = (image_module->save) (f, pixbuf, keys, values, error);
_gdk_pixbuf_unlock (image_module);
if (!retval)
goto end;
rewind (f);
for (;;) {
n = fread (buf, 1, TMP_FILE_BUF_SIZE, f);
......@@ -1326,39 +1373,43 @@ gdk_pixbuf_real_save_to_callback (GdkPixbuf *pixbuf,
gchar **values,
GError **error)
{
GdkPixbufModule *image_module = NULL;
gboolean ret;
GdkPixbufModule *image_module = NULL;
image_module = _gdk_pixbuf_get_named_module (type, error);
image_module = _gdk_pixbuf_get_named_module (type, error);
if (image_module == NULL)
return FALSE;
if (image_module == NULL)
return FALSE;
if (image_module->module == NULL)
if (!_gdk_pixbuf_load_module (image_module, error))
return FALSE;
if (image_module->module == NULL)
if (!_gdk_pixbuf_load_module (image_module, error))
return FALSE;
_gdk_pixbuf_lock (image_module);
if (image_module->save_to_callback) {
/* save normally */
return (* image_module->save_to_callback) (save_func, user_data,
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 */
return save_to_callback_with_tmp_file (image_module, pixbuf,
} 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);
return FALSE;
}
} 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;
}
_gdk_pixbuf_unlock (image_module);
return ret;
}
......
......@@ -144,7 +144,8 @@ gboolean gdk_pixbuf_set_option (GdkPixbuf *pixbuf,
typedef enum /*< skip >*/
{
GDK_PIXBUF_FORMAT_WRITABLE = 1 << 0,
GDK_PIXBUF_FORMAT_SCALABLE = 1 << 1
GDK_PIXBUF_FORMAT_SCALABLE = 1 << 1,
GDK_PIXBUF_FORMAT_THREADSAFE = 1 << 2
} GdkPixbufFormatFlags;
struct _GdkPixbufFormat {
......
......@@ -50,7 +50,6 @@ static void gdk_pixbuf_loader_finalize (GObject *loader);
static gpointer parent_class = NULL;
static guint pixbuf_loader_signals[LAST_SIGNAL] = { 0 };
/* Internal data */
#define LOADER_HEADER_SIZE 128
......@@ -59,6 +58,7 @@ typedef struct
{
GdkPixbufAnimation *animation;
gboolean closed;
gboolean holds_threadlock;
guchar header_buf[LOADER_HEADER_SIZE];
gint header_buf_offset;
GdkPixbufModule *image_module;
......@@ -225,8 +225,12 @@ gdk_pixbuf_loader_finalize (GObject *object)
loader = GDK_PIXBUF_LOADER (object);
priv = loader->priv;
if (!priv->closed)
if (!priv->closed) {
g_warning ("GdkPixbufLoader finalized without calling gdk_pixbuf_loader_close() - this is not allowed. You must explicitly end the data stream to the loader before dropping the last reference.");
if (priv->holds_threadlock) {
_gdk_pixbuf_unlock (priv->image_module);
}
}
if (priv->animation)
g_object_unref (priv->animation);
......@@ -382,6 +386,11 @@ gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader,
return 0;
}
if (!priv->holds_threadlock) {
_gdk_pixbuf_lock (priv->image_module);
priv->holds_threadlock = TRUE;
}
priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_size_func,
gdk_pixbuf_loader_prepare,
gdk_pixbuf_loader_update,
......@@ -736,6 +745,11 @@ gdk_pixbuf_loader_close (GdkPixbufLoader *loader,
}
priv->closed = TRUE;
if (priv->image_module) {
g_assert (priv->holds_threadlock);
_gdk_pixbuf_unlock (priv->image_module);
priv->holds_threadlock = FALSE;
}
if (priv->needs_scale)
{
......
......@@ -79,6 +79,9 @@ struct _GdkPixbufClass {
#ifdef GDK_PIXBUF_ENABLE_BACKEND
void _gdk_pixbuf_lock (GdkPixbufModule *image_module);
void _gdk_pixbuf_unlock (GdkPixbufModule *image_module);
GdkPixbufModule *_gdk_pixbuf_get_module (guchar *buffer, guint size,
const gchar *filename,
GError **error);
......
......@@ -676,7 +676,7 @@ MODULE_ENTRY (ani, fill_info) (GdkPixbufFormat *info)
info->description = N_("The ANI image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......
......@@ -1132,7 +1132,7 @@ MODULE_ENTRY (bmp, fill_info) (GdkPixbufFormat *info)
info->description = N_("The BMP image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -1643,6 +1643,6 @@ MODULE_ENTRY (gif, fill_info) (GdkPixbufFormat *info)
info->description = N_("The GIF image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -1210,7 +1210,7 @@ MODULE_ENTRY (ico, fill_info) (GdkPixbufFormat *info)
info->description = N_("The ICO image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......
......@@ -1071,6 +1071,6 @@ MODULE_ENTRY (jpeg, fill_info) (GdkPixbufFormat *info)
info->description = N_("The JPEG image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -758,6 +758,6 @@ MODULE_ENTRY (pcx, fill_info) (GdkPixbufFormat *info)
info->description = N_("The PCX image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -994,6 +994,6 @@ MODULE_ENTRY (png, fill_info) (GdkPixbufFormat *info)
info->description = N_("The PNG image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -1082,6 +1082,6 @@ MODULE_ENTRY (pnm, fill_info) (GdkPixbufFormat *info)
info->description = N_("The PNM/PBM/PGM/PPM image format family");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -543,7 +543,7 @@ MODULE_ENTRY (ras, fill_info) (GdkPixbufFormat *info)
info->description = N_("The Sun raster image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -995,6 +995,6 @@ MODULE_ENTRY (tga, fill_info) (GdkPixbufFormat *info)
info->description = N_("The Targa image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -62,10 +62,6 @@ struct _TiffContext
/* There's no user data for the error handlers, so we just have to
* put a big-ass lock on the whole TIFF loader
*/
G_LOCK_DEFINE_STATIC (tiff_loader);
static char *global_error = NULL;
static TIFFErrorHandler orig_error_handler = NULL;
static TIFFErrorHandler orig_warning_handler = NULL;
......@@ -242,10 +238,8 @@ tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
return NULL;
}
G_UNLOCK (tiff_loader);
if (context)
(* context->prepare_func) (pixbuf, NULL, context->user_data);
G_LOCK (tiff_loader);
#if TIFFLIB_VERSION >= 20031226
if (tifflibversion(&major, &minor, &revision) && major == 3 &&
......@@ -330,10 +324,8 @@ tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
_TIFFfree (rast);
}
G_UNLOCK (tiff_loader);
if (context)
(* context->update_func) (pixbuf, 0, 0, width, height, context->user_data);
G_LOCK (tiff_loader);
return pixbuf;
}
......@@ -351,8 +343,6 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
g_return_val_if_fail (f != NULL, NULL);
G_LOCK (tiff_loader);
tiff_push_handlers ();
fd = fileno (f);
......@@ -371,7 +361,6 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
_("Failed to open TIFF image"));
tiff_pop_handlers ();
G_UNLOCK (tiff_loader);
return NULL;
}
......@@ -386,8 +375,6 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error)
tiff_pop_handlers ();
G_UNLOCK (tiff_loader);
return pixbuf;
}
......@@ -504,8 +491,6 @@ gdk_pixbuf__tiff_image_stop_load (gpointer data,
g_return_val_if_fail (data != NULL, FALSE);
G_LOCK (tiff_loader);
tiff_push_handlers ();
tiff = TIFFClientOpen ("libtiff-pixbuf", "r", data,
......@@ -546,8 +531,6 @@ gdk_pixbuf__tiff_image_stop_load (gpointer data,
tiff_pop_handlers ();
G_UNLOCK (tiff_loader);
return retval;
}
......@@ -627,6 +610,7 @@ MODULE_ENTRY (tiff, fill_info) (GdkPixbufFormat *info)
info->description = N_("The TIFF image format");
info->mime_types = mime_types;
info->extensions = extensions;
/* not threadsafe, due the the error handler handling */
info->flags = 0;
info->license = "LGPL";
}
......@@ -368,6 +368,6 @@ MODULE_ENTRY (wbmp, fill_info) (GdkPixbufFormat *info)
info->description = N_("The WBMP image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -476,6 +476,6 @@ MODULE_ENTRY (xbm, fill_info) (GdkPixbufFormat *info)
info->description = N_("The XBM image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
......@@ -1559,6 +1559,6 @@ MODULE_ENTRY (xpm, fill_info) (GdkPixbufFormat *info)
info->description = N_("The XPM image format");
info->mime_types = mime_types;
info->extensions = extensions;
info->flags = 0;
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment