summaryrefslogtreecommitdiff
path: root/gdk-pixbuf/io-png.c
diff options
context:
space:
mode:
authorHavoc Pennington <hp@pobox.com>1999-10-27 18:55:00 +0000
committerHavoc Pennington <hp@src.gnome.org>1999-10-27 18:55:00 +0000
commit54f017171fefee078ad36a4d2a6c67622c8f57de (patch)
tree0bb7951e213bc386a7bb7cff841c729e402eba84 /gdk-pixbuf/io-png.c
parent2ec115ea801002eb68b7881433a17c5810605a80 (diff)
downloadgtk+-54f017171fefee078ad36a4d2a6c67622c8f57de.tar.gz
Check properly whether the XPM module has already been loaded
1999-10-27 Havoc Pennington <hp@pobox.com> * src/gdk-pixbuf-io.c (gdk_pixbuf_new_from_xpm_data): Check properly whether the XPM module has already been loaded (gdk_pixbuf_new_from_file): Check properly if loader module was already loaded (was checking if load symbol was present in order to decide whether to re-load; should check module != NULL, then load != NULL) (image_handler_load): Check in present working directory, makes it easier to debug for now (file_formats): This array initializer was seriously on crack, was assigning a function pointer to a GModule* * src/testpixbuf.c (main): Change type of pixbuf_loader to GdkPixbufLoader* * src/io-png.c: Progressive loading stuff compiles, untested.
Diffstat (limited to 'gdk-pixbuf/io-png.c')
-rw-r--r--gdk-pixbuf/io-png.c296
1 files changed, 294 insertions, 2 deletions
diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c
index a60adfbdc4..fed2e7e806 100644
--- a/gdk-pixbuf/io-png.c
+++ b/gdk-pixbuf/io-png.c
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <png.h>
#include "gdk-pixbuf.h"
+#include "gdk-pixbuf-io.h"
@@ -135,8 +136,299 @@ image_load (FILE *f)
free_buffer, NULL);
}
+/* These avoid the setjmp()/longjmp() crap in libpng */
+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);
+
+/* Called at the start of the progressive load */
+static void png_info_callback (png_structp png_read_ptr,
+ png_infop png_info_ptr);
+
+/* Called for each row; note that you will get duplicate row numbers
+ for interlaced PNGs */
+static void png_row_callback (png_structp png_read_ptr,
+ png_bytep new_row,
+ png_uint_32 row_num,
+ int pass_num);
+
+/* Called after reading the entire image */
+static void png_end_callback (png_structp png_read_ptr,
+ png_infop png_info_ptr);
+
+typedef struct _LoadContext LoadContext;
+
+struct _LoadContext {
+ png_structp png_read_ptr;
+ png_infop png_info_ptr;
+
+ ModulePreparedNotifyFunc notify_func;
+ gpointer notify_user_data;
+
+ GdkPixbuf* pixbuf;
+
+ guint fatal_error_occurred : 1;
+
+};
+
+gpointer
+image_begin_load (ModulePreparedNotifyFunc func, gpointer user_data)
+{
+ LoadContext* lc;
+
+ lc = g_new0(LoadContext, 1);
+
+ lc->fatal_error_occurred = FALSE;
+
+ lc->notify_func = func;
+ lc->notify_user_data = user_data;
+
+ /* Create the main PNG context struct */
+
+ lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ lc, /* error/warning callback data */
+ png_error_callback,
+ png_warning_callback);
+
+ if (lc->png_read_ptr == NULL) {
+ g_free(lc);
+ return NULL;
+ }
+
+ /* Create the two auxiliary context structs */
+
+ lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
+
+ if (lc->png_info_ptr == NULL) {
+ png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
+ g_free(lc);
+ return NULL;
+ }
+
+ png_set_progressive_read_fn(lc->png_read_ptr,
+ lc, /* callback data */
+ png_info_callback,
+ png_row_callback,
+ png_end_callback);
+
+
+ return lc;
+}
+
+void
+image_stop_load (gpointer context)
+{
+ LoadContext* lc = context;
+
+ g_return_if_fail(lc != NULL);
+
+ gdk_pixbuf_unref(lc->pixbuf);
+
+ png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
+ g_free(lc);
+}
+
gboolean
-image_load_by_data (void *data, size_t count)
+image_load_increment(gpointer context, guchar *buf, guint size)
+{
+ LoadContext* lc = context;
+
+ g_return_val_if_fail(lc != NULL, FALSE);
+
+ /* Invokes our callbacks as needed */
+ png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
+
+ if (lc->fatal_error_occurred)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* Called at the start of the progressive load, once we have image info */
+static void
+png_info_callback (png_structp png_read_ptr,
+ png_infop png_info_ptr)
+{
+ LoadContext* lc;
+ png_uint_32 width, height;
+ int bit_depth, color_type, filter_type,
+ compression_type, interlace_type, channels;
+ gboolean have_alpha = FALSE;
+
+ lc = png_get_progressive_ptr(png_read_ptr);
+
+ if (lc->fatal_error_occurred)
+ return;
+
+ /* Get the image info */
+
+ png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
+ &width, &height,
+ &bit_depth,
+ &color_type,
+ &interlace_type,
+ &compression_type,
+ &filter_type);
+
+ /* set_expand() basically needs to be called unless
+ we are already in RGB/RGBA mode
+ */
+ if (color_type == PNG_COLOR_TYPE_PALETTE &&
+ bit_depth <= 8) {
+
+ /* Convert indexed images to RGB */
+ png_set_expand (lc->png_read_ptr);
+
+ } else if (color_type == PNG_COLOR_TYPE_GRAY &&
+ bit_depth < 8) {
+
+ /* Convert grayscale to RGB */
+ png_set_expand (lc->png_read_ptr);
+
+ } else if (png_get_valid (lc->png_read_ptr,
+ lc->png_info_ptr, PNG_INFO_tRNS)) {
+
+ /* If we have transparency header, convert it to alpha
+ channel */
+ png_set_expand(lc->png_read_ptr);
+
+ } else if (bit_depth < 8) {
+
+ /* If we have < 8 scale it up to 8 */
+ png_set_expand(lc->png_read_ptr);
+
+
+ /* Conceivably, png_set_packing() is a better idea;
+ * God only knows how libpng works
+ */
+ }
+
+ /* If we are 16-bit, convert to 8-bit */
+ if (bit_depth == 16) {
+ png_set_strip_16(lc->png_read_ptr);
+ }
+
+ /* If gray scale, convert to RGB */
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(lc->png_read_ptr);
+ }
+
+ /* If we have alpha, set a flag */
+ if (color_type & PNG_COLOR_MASK_ALPHA)
+ have_alpha = TRUE;
+
+ /* Update the info the reflect our transformations */
+ png_read_update_info(lc->png_read_ptr, lc->png_info_ptr);
+
+ png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
+ &width, &height,
+ &bit_depth,
+ &color_type,
+ &interlace_type,
+ &compression_type,
+ &filter_type);
+
+#ifndef G_DISABLE_CHECKS
+ /* Check that the new info is what we want */
+
+ if (bit_depth != 8) {
+ g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
+ lc->fatal_error_occurred = TRUE;
+ return;
+ }
+
+ if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
+ g_warning("Transformed PNG not RGB or RGBA.");
+ lc->fatal_error_occurred = TRUE;
+ return;
+ }
+
+ channels = png_get_channels(lc->png_read_ptr, lc->png_info_ptr);
+ if ( ! (channels == 3 || channels == 4) ) {
+ g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
+ lc->fatal_error_occurred = TRUE;
+ return;
+ }
+#endif
+
+ lc->pixbuf = gdk_pixbuf_new(have_alpha, width, height);
+
+ if (lc->pixbuf == NULL) {
+ /* Failed to allocate memory */
+ lc->fatal_error_occurred = TRUE;
+ return;
+ }
+
+ /* Notify the client that we are ready to go */
+
+ if (lc->notify_func)
+ (* lc->notify_func) (lc->pixbuf, lc->notify_user_data);
+
+ return;
+}
+
+/* Called for each row; note that you will get duplicate row numbers
+ for interlaced PNGs */
+static void
+png_row_callback (png_structp png_read_ptr,
+ png_bytep new_row,
+ png_uint_32 row_num,
+ int pass_num)
+{
+ LoadContext* lc;
+ guchar* old_row = NULL;
+
+ lc = png_get_progressive_ptr(png_read_ptr);
+
+ if (lc->fatal_error_occurred)
+ return;
+
+ old_row = lc->pixbuf->art_pixbuf->pixels + (row_num * lc->pixbuf->art_pixbuf->rowstride);
+
+ png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
+}
+
+/* Called after reading the entire image */
+static void
+png_end_callback (png_structp png_read_ptr,
+ png_infop png_info_ptr)
{
- return TRUE;
+ LoadContext* lc;
+
+ lc = png_get_progressive_ptr(png_read_ptr);
+
+ if (lc->fatal_error_occurred)
+ return;
+
+ /* Doesn't do anything for now */
}
+
+
+static void
+png_error_callback(png_structp png_read_ptr,
+ png_const_charp error_msg)
+{
+ LoadContext* lc;
+
+ lc = png_get_error_ptr(png_read_ptr);
+
+ lc->fatal_error_occurred = TRUE;
+
+ fprintf(stderr, "Fatal error loading PNG: %s\n", error_msg);
+}
+
+static void
+png_warning_callback(png_structp png_read_ptr,
+ png_const_charp warning_msg)
+{
+ LoadContext* lc;
+
+ lc = png_get_error_ptr(png_read_ptr);
+
+ fprintf(stderr, "Warning loading PNG: %s\n", warning_msg);
+}
+