* Boston, MA 02111-1307, USA.
*/
-#include <config.h>
+#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
*/
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."));
+ g_set_error_literal (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,
/* 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."));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Transformed PNG has zero width or height."));
return FALSE;
}
if (bit_depth != 8) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Bits per channel of transformed PNG is not 8."));
+ g_set_error_literal (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_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Transformed PNG not RGB or RGBA."));
+ g_set_error_literal (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_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
- _("Transformed PNG has unsupported number of channels, must be 3 or 4."));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Transformed PNG has unsupported number of channels, must be 3 or 4."));
return FALSE;
}
return TRUE;
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 {
+ gboolean is_ascii = TRUE;
+ int i;
+
+ /* Avoid loading iconv if the text is plain ASCII */
+ for (i = 0; i < text_ptr.text_length; i++)
+ if (text_ptr.text[i] & 0x80) {
+ is_ascii = FALSE;
+ break;
+ }
+
+ if (is_ascii) {
*value = g_strdup (text_ptr.text);
+ } else {
+ *value = g_convert (text_ptr.text, -1,
+ "UTF-8", "ISO-8859-1",
+ NULL, NULL, NULL);
}
+
if (*value) {
*key = g_strconcat ("tEXt::", text_ptr.key, NULL);
return TRUE;
}
if (setjmp (png_ptr->jmpbuf)) {
- if (rows)
- g_free (rows);
+ g_free (rows);
if (pixbuf)
g_object_unref (pixbuf);
if (!pixbuf) {
if (error && *error == NULL) {
- g_set_error (error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Insufficient memory to load PNG file"));
+ g_set_error_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Insufficient memory to load PNG file"));
}
static void png_error_callback (png_structp png_read_ptr,
png_const_charp error_msg);
-static void png_warning_callback(png_structp png_read_ptr,
- png_const_charp warning_msg);
+static void png_warning_callback (png_structp png_read_ptr,
+ png_const_charp warning_msg);
/* Called at the start of the progressive load */
static void png_info_callback (png_structp png_read_ptr,
png_structp png_read_ptr;
png_infop png_info_ptr;
- ModulePreparedNotifyFunc prepare_func;
- ModuleUpdatedNotifyFunc update_func;
+ GdkPixbufModuleSizeFunc size_func;
+ GdkPixbufModulePreparedFunc prepare_func;
+ GdkPixbufModuleUpdatedFunc update_func;
gpointer notify_user_data;
GdkPixbuf* pixbuf;
};
static gpointer
-gdk_pixbuf__png_image_begin_load (ModuleSizeFunc size_func,
- ModulePreparedNotifyFunc prepare_func,
- ModuleUpdatedNotifyFunc update_func,
+gdk_pixbuf__png_image_begin_load (GdkPixbufModuleSizeFunc size_func,
+ GdkPixbufModulePreparedFunc prepare_func,
+ GdkPixbufModuleUpdatedFunc update_func,
gpointer user_data,
GError **error)
{
lc->fatal_error_occurred = FALSE;
+ lc->size_func = size_func;
lc->prepare_func = prepare_func;
lc->update_func = update_func;
lc->notify_user_data = user_data;
lc->error = NULL;
return FALSE;
} else {
- if (lc->first_row_seen_in_chunk >= 0) {
+ if (lc->first_row_seen_in_chunk >= 0 && lc->update_func) {
/* We saw at least one row */
gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
if (color_type & PNG_COLOR_MASK_ALPHA)
have_alpha = TRUE;
- lc->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
+ if (lc->size_func) {
+ gint w = width;
+ gint h = height;
+ (* lc->size_func) (&w, &h, lc->notify_user_data);
+
+ if (w == 0 || h == 0) {
+ lc->fatal_error_occurred = TRUE;
+ if (lc->error && *lc->error == NULL) {
+ g_set_error_literal (lc->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Transformed PNG has zero width or height."));
+ }
+ return;
+ }
+ }
+
+ lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
if (lc->pixbuf == NULL) {
/* Failed to allocate memory */
if (lc->fatal_error_occurred)
return;
- if (row_num < 0 || row_num >= lc->pixbuf->height) {
+ if (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"));
+ g_set_error_literal (lc->error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+ _("Fatal error reading PNG image file"));
}
return;
}
}
static void
-png_warning_callback(png_structp png_read_ptr,
- png_const_charp warning_msg)
+png_warning_callback (png_structp png_read_ptr,
+ png_const_charp warning_msg)
{
LoadContext* lc;
/* Save */
-static gboolean
-gdk_pixbuf__png_image_save (FILE *f,
- GdkPixbuf *pixbuf,
- gchar **keys,
- gchar **values,
- GError **error)
+typedef struct {
+ GdkPixbufSaveFunc save_func;
+ gpointer user_data;
+ GError **error;
+} SaveToFunctionIoPtr;
+
+static void
+png_save_to_callback_write_func (png_structp png_ptr,
+ png_bytep data,
+ png_size_t length)
+{
+ SaveToFunctionIoPtr *ioptr = png_get_io_ptr (png_ptr);
+
+ if (!ioptr->save_func ((gchar *)data, length, ioptr->error, ioptr->user_data)) {
+ /* If save_func has already set an error, which it
+ should have done, this won't overwrite it. */
+ png_error (png_ptr, "write function failed");
+ }
+}
+
+static void
+png_save_to_callback_flush_func (png_structp png_ptr)
+{
+ ;
+}
+
+static gboolean real_save_png (GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error,
+ gboolean to_callback,
+ FILE *f,
+ GdkPixbufSaveFunc save_func,
+ gpointer user_data)
{
png_structp png_ptr;
png_infop info_ptr;
int has_alpha;
int bpc;
int num_keys;
+ int compression = -1;
gboolean success = TRUE;
+ SaveToFunctionIoPtr to_callback_ioptr;
num_keys = 0;
if (keys && *keys) {
- gchar **kiter;
- gchar *key;
- int len;
+ gchar **kiter = keys;
+ gchar **viter = values;
+
+ while (*kiter) {
+ if (strncmp (*kiter, "tEXt::", 6) == 0) {
+ gchar *key = *kiter + 6;
+ int len = strlen (key);
+ if (len <= 1 || len > 79) {
+ g_set_error_literal (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_literal (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("Keys for PNG text chunks must be ASCII characters."));
+ return FALSE;
+ }
+ }
+ num_keys++;
+ } else if (strcmp (*kiter, "compression") == 0) {
+ char *endptr = NULL;
+ compression = strtol (*viter, &endptr, 10);
- 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) {
+ if (endptr == *viter) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_BAD_OPTION,
- _("Keys for PNG text chunks must be ASCII characters."));
+ _("PNG compression level must be a value between 0 and 9; value '%s' could not be parsed."),
+ *viter);
return FALSE;
}
+ if (compression < 0 || compression > 9) {
+ /* This is a user-visible error;
+ * lets people skip the range-checking
+ * in their app.
+ */
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_BAD_OPTION,
+ _("PNG compression level must be a value between 0 and 9; value '%d' is not allowed."),
+ compression);
+ return FALSE;
+ }
+ } else {
+ g_warning ("Unrecognized parameter (%s) passed to PNG saver.", *kiter);
}
- num_keys++;
+
+ ++kiter;
+ ++viter;
}
}
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);
+ _("Value for PNG text chunk %s cannot 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);
error,
png_simple_error_callback,
png_simple_warning_callback);
-
- g_return_val_if_fail (png_ptr != NULL, FALSE);
+ if (png_ptr == NULL) {
+ success = FALSE;
+ goto cleanup;
+ }
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
}
- png_init_io (png_ptr, f);
+ if (to_callback) {
+ to_callback_ioptr.save_func = save_func;
+ to_callback_ioptr.user_data = user_data;
+ to_callback_ioptr.error = error;
+ png_set_write_fn (png_ptr, &to_callback_ioptr,
+ png_save_to_callback_write_func,
+ png_save_to_callback_flush_func);
+ } else {
+ png_init_io (png_ptr, f);
+ }
+
+ if (compression >= 0)
+ png_set_compression_level (png_ptr, compression);
if (has_alpha) {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
png_write_end (png_ptr, info_ptr);
cleanup:
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
if (num_keys > 0) {
for (i = 0; i < num_keys; i++)
return success;
}
+static gboolean
+gdk_pixbuf__png_image_save (FILE *f,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_png (pixbuf, keys, values, error,
+ FALSE, f, NULL, NULL);
+}
+
+static gboolean
+gdk_pixbuf__png_image_save_to_callback (GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return real_save_png (pixbuf, keys, values, error,
+ TRUE, NULL, save_func, user_data);
+}
+
+#ifndef INCLUDE_png
+#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
+#else
+#define MODULE_ENTRY(function) void _gdk_pixbuf__png_ ## function
+#endif
+MODULE_ENTRY (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;
+ module->save_to_callback = gdk_pixbuf__png_image_save_to_callback;
+}
-void
-gdk_pixbuf__png_fill_vtable (GdkPixbufModule *module)
+MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
{
- 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;
+ 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 | GDK_PIXBUF_FORMAT_THREADSAFE;
+ info->license = "LGPL";
}