+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/* GdkPixbuf library - PNG image loader
*
* Copyright (C) 1999 Mark Crichton
\f
-static void
+static gboolean
setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
- gboolean *fatal_error_occurred,
+ GError **error,
png_uint_32* width_p, png_uint_32* height_p,
int* color_type_p)
{
/* Get the image info */
+ /* Must check bit depth, since png_get_IHDR generates an
+ FPE on bit_depth 0.
+ */
+ bit_depth = png_get_bit_depth (png_read_ptr, png_info_ptr);
+ if (bit_depth < 1 || bit_depth > 16) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Bits per channel of PNG image is invalid."));
+ return FALSE;
+ }
png_get_IHDR (png_read_ptr, png_info_ptr,
&width, &height,
&bit_depth,
*height_p = height;
*color_type_p = color_type;
-#ifndef G_DISABLE_CHECKS
/* Check that the new info is what we want */
+ if (width == 0 || height == 0) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Transformed PNG has zero width or height."));
+ return FALSE;
+ }
+
if (bit_depth != 8) {
- g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
- *fatal_error_occurred = TRUE;
- return;
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Bits per channel of transformed PNG is not 8."));
+ return FALSE;
}
if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
- g_warning("Transformed PNG not RGB or RGBA.");
- *fatal_error_occurred = TRUE;
- return;
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Transformed PNG not RGB or RGBA."));
+ return FALSE;
}
channels = png_get_channels(png_read_ptr, png_info_ptr);
if ( ! (channels == 3 || channels == 4) ) {
- g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
- *fatal_error_occurred = TRUE;
- return;
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Transformed PNG has unsupported number of channels, must be 3 or 4."));
+ return FALSE;
}
-#endif
+ return TRUE;
}
-/* Destroy notification function for the pixbuf */
static void
-free_buffer (guchar *pixels, gpointer data)
+png_simple_error_callback(png_structp png_save_ptr,
+ png_const_charp error_msg)
{
- free (pixels);
+ GError **error;
+
+ error = png_get_error_ptr(png_save_ptr);
+
+ /* I don't trust libpng to call the error callback only once,
+ * so check for already-set error
+ */
+ if (error && *error == NULL) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Fatal error in PNG image file: %s"),
+ error_msg);
+ }
+
+ longjmp (png_save_ptr->jmpbuf, 1);
+}
+
+static void
+png_simple_warning_callback(png_structp png_save_ptr,
+ png_const_charp warning_msg)
+{
+ /* Don't print anything; we should not be dumping junk to
+ * stderr, since that may be bad for some apps. If it's
+ * important enough to display, we need to add a GError
+ * **warning return location wherever we have an error return
+ * location.
+ */
+}
+
+static gboolean
+png_text_to_pixbuf_option (png_text text_ptr,
+ gchar **key,
+ gchar **value)
+{
+ if (text_ptr.text_length > 0) {
+ *value = g_convert (text_ptr.text, -1,
+ "UTF-8", "ISO-8859-1",
+ NULL, NULL, NULL);
+ }
+ else {
+ *value = g_strdup (text_ptr.text);
+ }
+ if (*value) {
+ *key = g_strconcat ("tEXt::", text_ptr.key, NULL);
+ return TRUE;
+ } else {
+ g_warning ("Couldn't convert text chunk value to UTF-8.");
+ *key = NULL;
+ return FALSE;
+ }
+}
+
+static png_voidp
+png_malloc_callback (png_structp o, png_size_t size)
+{
+ return g_try_malloc (size);
+}
+
+static void
+png_free_callback (png_structp o, png_voidp x)
+{
+ g_free (x);
}
/* Shared library entry point */
-GdkPixbuf *
-gdk_pixbuf__png_image_load (FILE *f)
+static GdkPixbuf *
+gdk_pixbuf__png_image_load (FILE *f, GError **error)
{
+ GdkPixbuf * volatile pixbuf = NULL;
png_structp png_ptr;
- png_infop info_ptr, end_info;
- gboolean failed = FALSE;
- gint i, ctype, bpp;
+ png_infop info_ptr;
+ png_textp text_ptr;
+ gint i, ctype;
png_uint_32 w, h;
- png_bytepp rows;
- guchar *pixels;
-
- png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_bytepp volatile rows = NULL;
+ gint num_texts;
+ gchar *key;
+ gchar *value;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+ png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
+ error,
+ png_simple_error_callback,
+ png_simple_warning_callback,
+ NULL,
+ png_malloc_callback,
+ png_free_callback);
+#else
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+ error,
+ png_simple_error_callback,
+ png_simple_warning_callback);
+#endif
if (!png_ptr)
return NULL;
return NULL;
}
- end_info = png_create_info_struct (png_ptr);
- if (!end_info) {
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- return NULL;
- }
-
if (setjmp (png_ptr->jmpbuf)) {
- png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ if (rows)
+ g_free (rows);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
png_init_io (png_ptr, f);
png_read_info (png_ptr, info_ptr);
- setup_png_transformations(png_ptr, info_ptr, &failed, &w, &h, &ctype);
-
- if (failed) {
- png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) {
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
- if (ctype & PNG_COLOR_MASK_ALPHA)
- bpp = 4;
- else
- bpp = 3;
-
- pixels = malloc (w * h * bpp);
- if (!pixels) {
- png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h);
+
+ if (!pixbuf) {
+ if (error && *error == NULL) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to load PNG file"));
+ }
+
+
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
rows = g_new (png_bytep, h);
for (i = 0; i < h; i++)
- rows[i] = pixels + i * w * bpp;
+ rows[i] = pixbuf->pixels + i * pixbuf->rowstride;
png_read_image (png_ptr, rows);
- png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
+ png_read_end (png_ptr, info_ptr);
+
+ if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
+ for (i = 0; i < num_texts; i++) {
+ png_text_to_pixbuf_option (text_ptr[i], &key, &value);
+ gdk_pixbuf_set_option (pixbuf, key, value);
+ g_free (key);
+ g_free (value);
+ }
+ }
+
g_free (rows);
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- if (ctype & PNG_COLOR_MASK_ALPHA)
- return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
- w, h, w * 4,
- free_buffer, NULL);
- else
- return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
- w, h, w * 3,
- free_buffer, NULL);
+ return pixbuf;
}
/* I wish these avoided the setjmp()/longjmp() crap in libpng instead
png_structp png_read_ptr;
png_infop png_info_ptr;
- ModulePreparedNotifyFunc prepare_func;
- ModuleUpdatedNotifyFunc update_func;
+ GdkPixbufModulePreparedFunc prepare_func;
+ GdkPixbufModuleUpdatedFunc update_func;
gpointer notify_user_data;
GdkPixbuf* pixbuf;
guint fatal_error_occurred : 1;
+ GError **error;
};
-gpointer
-gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func,
- ModuleUpdatedNotifyFunc update_func,
- ModuleFrameDoneNotifyFunc frame_done_func,
- ModuleAnimationDoneNotifyFunc anim_done_func,
- gpointer user_data)
+static gpointer
+gdk_pixbuf__png_image_begin_load (GdkPixbufModuleSizeFunc size_func,
+ GdkPixbufModulePreparedFunc prepare_func,
+ GdkPixbufModuleUpdatedFunc update_func,
+ gpointer user_data,
+ GError **error)
{
LoadContext* lc;
lc->first_pass_seen_in_chunk = -1;
lc->last_pass_seen_in_chunk = -1;
lc->max_row_seen_in_chunk = -1;
+ lc->error = error;
/* Create the main PNG context struct */
-
+#ifdef PNG_USER_MEM_SUPPORTED
+ lc->png_read_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
+ lc, /* error/warning callback data */
+ png_error_callback,
+ png_warning_callback,
+ NULL,
+ png_malloc_callback,
+ png_free_callback);
+#else
lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
lc, /* error/warning callback data */
png_error_callback,
png_warning_callback);
-
+#endif
if (lc->png_read_ptr == NULL) {
g_free(lc);
+ /* error callback should have set the error */
return NULL;
}
-
+
if (setjmp (lc->png_read_ptr->jmpbuf)) {
if (lc->png_info_ptr)
png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
g_free(lc);
+ /* error callback should have set the error */
return NULL;
}
if (lc->png_info_ptr == NULL) {
png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
g_free(lc);
+ /* error callback should have set the error */
return NULL;
}
png_end_callback);
+ /* We don't want to keep modifying error after returning here,
+ * it may no longer be valid.
+ */
+ lc->error = NULL;
+
return lc;
}
-void
-gdk_pixbuf__png_image_stop_load (gpointer context)
+static gboolean
+gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
{
LoadContext* lc = context;
- g_return_if_fail(lc != NULL);
+ g_return_val_if_fail(lc != NULL, TRUE);
- gdk_pixbuf_unref(lc->pixbuf);
+ /* FIXME this thing needs to report errors if
+ * we have unused image data
+ */
+
+ if (lc->pixbuf)
+ g_object_unref (lc->pixbuf);
- png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
+ png_destroy_read_struct(&lc->png_read_ptr, &lc->png_info_ptr, NULL);
g_free(lc);
+
+ return TRUE;
}
-gboolean
-gdk_pixbuf__png_image_load_increment(gpointer context, guchar *buf, guint size)
+static gboolean
+gdk_pixbuf__png_image_load_increment(gpointer context,
+ const guchar *buf, guint size,
+ GError **error)
{
LoadContext* lc = context;
lc->first_pass_seen_in_chunk = -1;
lc->last_pass_seen_in_chunk = -1;
lc->max_row_seen_in_chunk = -1;
+ lc->error = error;
/* Invokes our callbacks as needed */
if (setjmp (lc->png_read_ptr->jmpbuf)) {
+ lc->error = NULL;
return FALSE;
} else {
- png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
+ png_process_data(lc->png_read_ptr, lc->png_info_ptr,
+ (guchar*) buf, size);
}
- if (lc->fatal_error_occurred)
+ if (lc->fatal_error_occurred) {
+ lc->error = NULL;
return FALSE;
- else {
+ } else {
if (lc->first_row_seen_in_chunk >= 0) {
/* We saw at least one row */
gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
lc->notify_user_data);
}
}
+
+ lc->error = NULL;
return TRUE;
}
{
LoadContext* lc;
png_uint_32 width, height;
+ png_textp png_text_ptr;
+ int i, num_texts;
int color_type;
gboolean have_alpha = FALSE;
- gboolean failed = FALSE;
lc = png_get_progressive_ptr(png_read_ptr);
if (lc->fatal_error_occurred)
return;
-
- setup_png_transformations(lc->png_read_ptr,
- lc->png_info_ptr,
- &failed,
- &width, &height, &color_type);
-
- if (failed) {
+ if (!setup_png_transformations(lc->png_read_ptr,
+ lc->png_info_ptr,
+ lc->error,
+ &width, &height, &color_type)) {
lc->fatal_error_occurred = TRUE;
return;
}
if (lc->pixbuf == NULL) {
/* Failed to allocate memory */
lc->fatal_error_occurred = TRUE;
+ if (lc->error && *lc->error == NULL) {
+ g_set_error (lc->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
+ width, height);
+ }
return;
}
+
+ /* Extract text chunks and attach them as pixbuf options */
+ if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
+ for (i = 0; i < num_texts; i++) {
+ gchar *key, *value;
+
+ if (png_text_to_pixbuf_option (png_text_ptr[i],
+ &key, &value)) {
+ gdk_pixbuf_set_option (lc->pixbuf, key, value);
+ g_free (key);
+ g_free (value);
+ }
+ }
+ }
+
/* Notify the client that we are ready to go */
if (lc->prepare_func)
- (* lc->prepare_func) (lc->pixbuf, lc->notify_user_data);
+ (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
return;
}
if (lc->fatal_error_occurred)
return;
+ if (row_num < 0 || row_num >= lc->pixbuf->height) {
+ lc->fatal_error_occurred = TRUE;
+ if (lc->error && *lc->error == NULL) {
+ g_set_error (lc->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Fatal error reading PNG image file"));
+ }
+ return;
+ }
+
if (lc->first_row_seen_in_chunk < 0) {
lc->first_row_seen_in_chunk = row_num;
lc->first_pass_seen_in_chunk = pass_num;
lc = png_get_error_ptr(png_read_ptr);
lc->fatal_error_occurred = TRUE;
-
- fprintf(stderr, "Fatal error loading PNG: %s\n", error_msg);
+
+ /* I don't trust libpng to call the error callback only once,
+ * so check for already-set error
+ */
+ if (lc->error && *lc->error == NULL) {
+ g_set_error (lc->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Fatal error reading PNG image file: %s"),
+ error_msg);
+ }
+
+ longjmp (png_read_ptr->jmpbuf, 1);
}
static void
LoadContext* lc;
lc = png_get_error_ptr(png_read_ptr);
-
- fprintf(stderr, "Warning loading PNG: %s\n", warning_msg);
+
+ /* Don't print anything; we should not be dumping junk to
+ * stderr, since that may be bad for some apps. If it's
+ * important enough to display, we need to add a GError
+ * **warning return location wherever we have an error return
+ * location.
+ */
}
/* Save */
-gboolean
+
+static gboolean
gdk_pixbuf__png_image_save (FILE *f,
GdkPixbuf *pixbuf,
gchar **keys,
gchar **values,
GError **error)
{
- /* FIXME error handling is broken */
-
png_structp png_ptr;
png_infop info_ptr;
+ png_textp text_ptr = NULL;
guchar *ptr;
guchar *pixels;
- int x, y, j;
- png_bytep row_ptr, data = NULL;
+ int y;
+ int i;
+ png_bytep row_ptr;
png_color_8 sig_bit;
int w, h, rowstride;
int has_alpha;
int bpc;
+ int num_keys;
+ gboolean success = TRUE;
+
+ num_keys = 0;
if (keys && *keys) {
- g_warning ("Bad option name '%s' passed to PNG saver",
- *keys);
- return FALSE;
-#if 0
- gchar **kiter = keys;
- gchar **viter = values;
-
-
- while (*kiter) {
-
- ++kiter;
- ++viter;
+ gchar **kiter;
+ gchar *key;
+ int len;
+
+ for (kiter = keys; *kiter; kiter++) {
+ if (strncmp (*kiter, "tEXt::", 6) != 0) {
+ g_warning ("Bad option name '%s' passed to PNG saver", *kiter);
+ return FALSE;
+ }
+ key = *kiter + 6;
+ len = strlen (key);
+ if (len <= 1 || len > 79) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Keys for PNG text chunks must have at least 1 and at most 79 characters."));
+ return FALSE;
+ }
+ for (i = 0; i < len; i++) {
+ if ((guchar) key[i] > 127) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Keys for PNG text chunks must be ASCII characters."));
+ return FALSE;
+ }
+ }
+ num_keys++;
}
+ }
+
+ if (num_keys > 0) {
+ text_ptr = g_new0 (png_text, num_keys);
+ for (i = 0; i < num_keys; i++) {
+ text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
+ text_ptr[i].key = keys[i] + 6;
+ text_ptr[i].text = g_convert (values[i], -1,
+ "ISO-8859-1", "UTF-8",
+ NULL, &text_ptr[i].text_length,
+ NULL);
+
+#ifdef PNG_iTXt_SUPPORTED
+ if (!text_ptr[i].text) {
+ text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE;
+ text_ptr[i].text = g_strdup (values[i]);
+ text_ptr[i].text_length = 0;
+ text_ptr[i].itxt_length = strlen (text_ptr[i].text);
+ text_ptr[i].lang = NULL;
+ text_ptr[i].lang_key = NULL;
+ }
#endif
+
+ if (!text_ptr[i].text) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Value for PNG text chunk %s can not be converted to ISO-8859-1 encoding."), keys[i] + 6);
+ num_keys = i;
+ for (i = 0; i < num_keys; i++)
+ g_free (text_ptr[i].text);
+ g_free (text_ptr);
+ return FALSE;
+ }
+ }
}
-
+
bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
w = gdk_pixbuf_get_width (pixbuf);
h = gdk_pixbuf_get_height (pixbuf);
pixels = gdk_pixbuf_get_pixels (pixbuf);
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
+ error,
+ png_simple_error_callback,
+ png_simple_warning_callback);
g_return_val_if_fail (png_ptr != NULL, FALSE);
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
- return FALSE;
+ success = FALSE;
+ goto cleanup;
}
if (setjmp (png_ptr->jmpbuf)) {
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
- return FALSE;
+ success = FALSE;
+ goto cleanup;
+ }
+
+ if (num_keys > 0) {
+ png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
}
+
png_init_io (png_ptr, f);
+
if (has_alpha) {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
- PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-#ifdef WORDS_BIGENDIAN
- png_set_swap_alpha (png_ptr);
-#else
- png_set_bgr (png_ptr);
-#endif
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
} else {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
- PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
- data = malloc (w * 3 * sizeof (char));
+ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
}
sig_bit.red = bpc;
sig_bit.green = bpc;
ptr = pixels;
for (y = 0; y < h; y++) {
- if (has_alpha)
- row_ptr = (png_bytep)ptr;
- else {
- for (j = 0, x = 0; x < w; x++)
- memcpy (&(data[x*3]), &(ptr[x*3]), 3);
-
- row_ptr = (png_bytep)data;
- }
+ row_ptr = (png_bytep)ptr;
png_write_rows (png_ptr, &row_ptr, 1);
ptr += rowstride;
}
- if (data)
- free (data);
-
png_write_end (png_ptr, info_ptr);
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
- return TRUE;
-}
+cleanup:
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+
+ if (num_keys > 0) {
+ for (i = 0; i < num_keys; i++)
+ g_free (text_ptr[i].text);
+ g_free (text_ptr);
+ }
+ return success;
+}
+void
+MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
+{
+ module->load = gdk_pixbuf__png_image_load;
+ module->begin_load = gdk_pixbuf__png_image_begin_load;
+ module->stop_load = gdk_pixbuf__png_image_stop_load;
+ module->load_increment = gdk_pixbuf__png_image_load_increment;
+ module->save = gdk_pixbuf__png_image_save;
+}
+void
+MODULE_ENTRY (png, fill_info) (GdkPixbufFormat *info)
+{
+ static GdkPixbufModulePattern signature[] = {
+ { "\x89PNG\r\n\x1a\x0a", NULL, 100 },
+ { NULL, NULL, 0 }
+ };
+ static gchar * mime_types[] = {
+ "image/png",
+ NULL
+ };
+ static gchar * extensions[] = {
+ "png",
+ NULL
+ };
+
+ info->name = "png";
+ info->signature = signature;
+ info->description = N_("The PNG image format");
+ info->mime_types = mime_types;
+ info->extensions = extensions;
+ info->flags = GDK_PIXBUF_FORMAT_WRITABLE;
+}