From 54f017171fefee078ad36a4d2a6c67622c8f57de Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 27 Oct 1999 18:55:00 +0000 Subject: [PATCH] Check properly whether the XPM module has already been loaded 1999-10-27 Havoc Pennington * 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. --- demos/testpixbuf.c | 2 +- gdk-pixbuf/gdk-pixbuf-io.c | 54 ++++--- gdk-pixbuf/io-png.c | 298 ++++++++++++++++++++++++++++++++++++- 3 files changed, 332 insertions(+), 22 deletions(-) diff --git a/demos/testpixbuf.c b/demos/testpixbuf.c index 20dc5f79a6..fc6d0aa397 100644 --- a/demos/testpixbuf.c +++ b/demos/testpixbuf.c @@ -409,7 +409,7 @@ main (int argc, char **argv) int found_valid = FALSE; GdkPixbuf *pixbuf; - GtkObject *pixbuf_loader; + GdkPixbufLoader *pixbuf_loader; FILE *file; gint val; guchar buf; diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c index 236fdaeb23..9c41402cb5 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.c +++ b/gdk-pixbuf/gdk-pixbuf-io.c @@ -136,17 +136,17 @@ pixbuf_check_ppm (guchar *buffer, int size) #endif ModuleType file_formats [] = { - { "png", pixbuf_check_png, NULL, NULL, NULL, NULL, NULL, NULL }, - { "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL }, - { "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL }, - { "gif", pixbuf_check_gif, NULL, NULL, NULL, NULL, NULL, NULL }, + { "png", NULL, pixbuf_check_png, NULL, NULL, NULL, NULL, NULL }, + { "jpeg", NULL, pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL }, + { "tiff", NULL, pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL }, + { "gif", NULL, pixbuf_check_gif, NULL, NULL, NULL, NULL, NULL }, #define XPM_FILE_FORMAT_INDEX 4 - { "xpm", pixbuf_check_xpm, NULL, NULL, NULL, NULL, NULL, NULL }, + { "xpm", NULL, pixbuf_check_xpm, NULL, NULL, NULL, NULL, NULL }, #if 0 - { "bmp", pixbuf_check_bmp, NULL, NULL, NULL, NULL, NULL, NULL }, - { "ppm", pixbuf_check_ppm, NULL, NULL, NULL, NULL, NULL, NULL }, + { "bmp", NULL, pixbuf_check_bmp, NULL, NULL, NULL, NULL, NULL }, + { "ppm", NULL, pixbuf_check_ppm, NULL, NULL, NULL, NULL, NULL }, #endif - { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; static void @@ -161,14 +161,29 @@ image_handler_load (ModuleType *image_module) module_name = g_strconcat ("pixbuf-", image_module->module_name, NULL); path = g_module_build_path (PIXBUF_LIBDIR, module_name); - g_free (module_name); - + module = g_module_open (path, G_MODULE_BIND_LAZY); - g_free (path); if (!module) { - g_warning ("Unable to load module: %s", path); - return; - } + /* Debug feature, check in present working directory */ + g_free(path); + path = g_module_build_path("", module_name); + module = g_module_open(path, G_MODULE_BIND_LAZY); + + if (!module) { + g_warning ("Unable to load module: %s: %s", path, g_module_error()); + g_free (module_name); + g_free(path); + return; + } else { + printf("loaded module `%s'\n", module_name); + } + g_free(path); + } else { + printf("loaded module `%s'\n", path); + g_free (path); + } + + g_free (module_name); image_module->module = module; @@ -224,10 +239,10 @@ gdk_pixbuf_new_from_file (const char *filename) image_module = gdk_pixbuf_get_module (buffer, size); if (image_module){ - if (!image_module->load) + if (image_module->module == NULL) image_handler_load (image_module); - if (!image_module->load) { + if (image_module->load == NULL) { fclose (f); return NULL; } @@ -254,13 +269,16 @@ gdk_pixbuf_new_from_xpm_data (const gchar **data) GdkPixbuf *(* load_xpm_data) (const gchar **data); GdkPixbuf *pixbuf; - if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) { + if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) { image_handler_load(&file_formats[XPM_FILE_FORMAT_INDEX]); } - if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) { + if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) { g_warning("Can't find gdk-pixbuf module for parsing inline XPM data"); return NULL; + } else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) { + g_warning("gdk-pixbuf XPM module lacks XPM data capability"); + return NULL; } else { load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data; } 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 #include #include "gdk-pixbuf.h" +#include "gdk-pixbuf-io.h" @@ -135,8 +136,299 @@ image_load (FILE *f) free_buffer, NULL); } -gboolean -image_load_by_data (void *data, size_t count) +/* 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) { - return TRUE; + 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_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) +{ + 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); +} +