1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - PNG image loader
4 * Copyright (C) 1999 Mark Crichton
5 * Copyright (C) 1999 The Free Software Foundation
7 * Authors: Mark Crichton <crichton@gimp.org>
8 * Federico Mena-Quintero <federico@gimp.org>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
30 #include "gdk-pixbuf-private.h"
31 #include "gdk-pixbuf-io.h"
36 setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
38 png_uint_32* width_p, png_uint_32* height_p,
41 png_uint_32 width, height;
42 int bit_depth, color_type, interlace_type, compression_type, filter_type;
45 /* Get the image info */
47 /* Must check bit depth, since png_get_IHDR generates an
50 bit_depth = png_get_bit_depth (png_read_ptr, png_info_ptr);
51 if (bit_depth < 1 || bit_depth > 16) {
54 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
55 _("Bits per channel of PNG image is invalid."));
58 png_get_IHDR (png_read_ptr, png_info_ptr,
66 /* set_expand() basically needs to be called unless
67 we are already in RGB/RGBA mode
69 if (color_type == PNG_COLOR_TYPE_PALETTE &&
72 /* Convert indexed images to RGB */
73 png_set_expand (png_read_ptr);
75 } else if (color_type == PNG_COLOR_TYPE_GRAY &&
78 /* Convert grayscale to RGB */
79 png_set_expand (png_read_ptr);
81 } else if (png_get_valid (png_read_ptr,
82 png_info_ptr, PNG_INFO_tRNS)) {
84 /* If we have transparency header, convert it to alpha
86 png_set_expand(png_read_ptr);
88 } else if (bit_depth < 8) {
90 /* If we have < 8 scale it up to 8 */
91 png_set_expand(png_read_ptr);
94 /* Conceivably, png_set_packing() is a better idea;
95 * God only knows how libpng works
99 /* If we are 16-bit, convert to 8-bit */
100 if (bit_depth == 16) {
101 png_set_strip_16(png_read_ptr);
104 /* If gray scale, convert to RGB */
105 if (color_type == PNG_COLOR_TYPE_GRAY ||
106 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
107 png_set_gray_to_rgb(png_read_ptr);
110 /* If interlaced, handle that */
111 if (interlace_type != PNG_INTERLACE_NONE) {
112 png_set_interlace_handling(png_read_ptr);
115 /* Update the info the reflect our transformations */
116 png_read_update_info(png_read_ptr, png_info_ptr);
118 png_get_IHDR (png_read_ptr, png_info_ptr,
128 *color_type_p = color_type;
130 /* Check that the new info is what we want */
132 if (width == 0 || height == 0) {
135 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
136 _("Transformed PNG has zero width or height."));
140 if (bit_depth != 8) {
143 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
144 _("Bits per channel of transformed PNG is not 8."));
148 if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
149 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
152 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
153 _("Transformed PNG not RGB or RGBA."));
157 channels = png_get_channels(png_read_ptr, png_info_ptr);
158 if ( ! (channels == 3 || channels == 4) ) {
161 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
162 _("Transformed PNG has unsupported number of channels, must be 3 or 4."));
169 png_simple_error_callback(png_structp png_save_ptr,
170 png_const_charp error_msg)
174 error = png_get_error_ptr(png_save_ptr);
176 /* I don't trust libpng to call the error callback only once,
177 * so check for already-set error
179 if (error && *error == NULL) {
182 GDK_PIXBUF_ERROR_FAILED,
183 _("Fatal error in PNG image file: %s"),
187 longjmp (png_save_ptr->jmpbuf, 1);
191 png_simple_warning_callback(png_structp png_save_ptr,
192 png_const_charp warning_msg)
194 /* Don't print anything; we should not be dumping junk to
195 * stderr, since that may be bad for some apps. If it's
196 * important enough to display, we need to add a GError
197 * **warning return location wherever we have an error return
203 png_text_to_pixbuf_option (png_text text_ptr,
207 if (text_ptr.text_length > 0) {
208 *value = g_convert (text_ptr.text, -1,
209 "UTF-8", "ISO-8859-1",
213 *value = g_strdup (text_ptr.text);
216 *key = g_strconcat ("tEXt::", text_ptr.key, NULL);
219 g_warning ("Couldn't convert text chunk value to UTF-8.");
226 png_malloc_callback (png_structp o, png_size_t size)
228 return g_try_malloc (size);
232 png_free_callback (png_structp o, png_voidp x)
237 /* Shared library entry point */
239 gdk_pixbuf__png_image_load (FILE *f, GError **error)
241 GdkPixbuf * volatile pixbuf = NULL;
247 png_bytepp volatile rows = NULL;
252 #ifdef PNG_USER_MEM_SUPPORTED
253 png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
255 png_simple_error_callback,
256 png_simple_warning_callback,
261 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
263 png_simple_error_callback,
264 png_simple_warning_callback);
269 info_ptr = png_create_info_struct (png_ptr);
271 png_destroy_read_struct (&png_ptr, NULL, NULL);
275 if (setjmp (png_ptr->jmpbuf)) {
280 g_object_unref (pixbuf);
282 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
286 png_init_io (png_ptr, f);
287 png_read_info (png_ptr, info_ptr);
289 if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) {
290 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
294 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h);
297 if (error && *error == NULL) {
300 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
301 _("Insufficient memory to load PNG file"));
305 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
309 rows = g_new (png_bytep, h);
311 for (i = 0; i < h; i++)
312 rows[i] = pixbuf->pixels + i * pixbuf->rowstride;
314 png_read_image (png_ptr, rows);
315 png_read_end (png_ptr, info_ptr);
317 if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
318 for (i = 0; i < num_texts; i++) {
319 png_text_to_pixbuf_option (text_ptr[i], &key, &value);
320 gdk_pixbuf_set_option (pixbuf, key, value);
327 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
332 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
333 just allow you to change the error reporting. */
334 static void png_error_callback (png_structp png_read_ptr,
335 png_const_charp error_msg);
337 static void png_warning_callback(png_structp png_read_ptr,
338 png_const_charp warning_msg);
340 /* Called at the start of the progressive load */
341 static void png_info_callback (png_structp png_read_ptr,
342 png_infop png_info_ptr);
344 /* Called for each row; note that you will get duplicate row numbers
345 for interlaced PNGs */
346 static void png_row_callback (png_structp png_read_ptr,
351 /* Called after reading the entire image */
352 static void png_end_callback (png_structp png_read_ptr,
353 png_infop png_info_ptr);
355 typedef struct _LoadContext LoadContext;
357 struct _LoadContext {
358 png_structp png_read_ptr;
359 png_infop png_info_ptr;
361 GdkPixbufModulePreparedFunc prepare_func;
362 GdkPixbufModuleUpdatedFunc update_func;
363 gpointer notify_user_data;
367 /* row number of first row seen, or -1 if none yet seen */
369 gint first_row_seen_in_chunk;
371 /* pass number for the first row seen */
373 gint first_pass_seen_in_chunk;
375 /* row number of last row seen */
376 gint last_row_seen_in_chunk;
378 gint last_pass_seen_in_chunk;
380 /* highest row number seen */
381 gint max_row_seen_in_chunk;
383 guint fatal_error_occurred : 1;
389 gdk_pixbuf__png_image_begin_load (GdkPixbufModuleSizeFunc size_func,
390 GdkPixbufModulePreparedFunc prepare_func,
391 GdkPixbufModuleUpdatedFunc update_func,
397 lc = g_new0(LoadContext, 1);
399 lc->fatal_error_occurred = FALSE;
401 lc->prepare_func = prepare_func;
402 lc->update_func = update_func;
403 lc->notify_user_data = user_data;
405 lc->first_row_seen_in_chunk = -1;
406 lc->last_row_seen_in_chunk = -1;
407 lc->first_pass_seen_in_chunk = -1;
408 lc->last_pass_seen_in_chunk = -1;
409 lc->max_row_seen_in_chunk = -1;
412 /* Create the main PNG context struct */
414 #ifdef PNG_USER_MEM_SUPPORTED
415 lc->png_read_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
416 lc, /* error/warning callback data */
418 png_warning_callback,
423 lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
424 lc, /* error/warning callback data */
426 png_warning_callback);
428 if (lc->png_read_ptr == NULL) {
430 /* error callback should have set the error */
434 if (setjmp (lc->png_read_ptr->jmpbuf)) {
435 if (lc->png_info_ptr)
436 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
438 /* error callback should have set the error */
442 /* Create the auxiliary context struct */
444 lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
446 if (lc->png_info_ptr == NULL) {
447 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
449 /* error callback should have set the error */
453 png_set_progressive_read_fn(lc->png_read_ptr,
454 lc, /* callback data */
460 /* We don't want to keep modifying error after returning here,
461 * it may no longer be valid.
469 gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
471 LoadContext* lc = context;
473 g_return_val_if_fail(lc != NULL, TRUE);
475 /* FIXME this thing needs to report errors if
476 * we have unused image data
480 g_object_unref (lc->pixbuf);
482 png_destroy_read_struct(&lc->png_read_ptr, &lc->png_info_ptr, NULL);
489 gdk_pixbuf__png_image_load_increment(gpointer context,
490 const guchar *buf, guint size,
493 LoadContext* lc = context;
495 g_return_val_if_fail(lc != NULL, FALSE);
498 lc->first_row_seen_in_chunk = -1;
499 lc->last_row_seen_in_chunk = -1;
500 lc->first_pass_seen_in_chunk = -1;
501 lc->last_pass_seen_in_chunk = -1;
502 lc->max_row_seen_in_chunk = -1;
505 /* Invokes our callbacks as needed */
506 if (setjmp (lc->png_read_ptr->jmpbuf)) {
510 png_process_data(lc->png_read_ptr, lc->png_info_ptr,
511 (guchar*) buf, size);
514 if (lc->fatal_error_occurred) {
518 if (lc->first_row_seen_in_chunk >= 0) {
519 /* We saw at least one row */
520 gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
522 g_assert(pass_diff >= 0);
524 if (pass_diff == 0) {
525 /* start and end row were in the same pass */
526 (lc->update_func)(lc->pixbuf, 0,
527 lc->first_row_seen_in_chunk,
529 (lc->last_row_seen_in_chunk -
530 lc->first_row_seen_in_chunk) + 1,
531 lc->notify_user_data);
532 } else if (pass_diff == 1) {
533 /* We have from the first row seen to
534 the end of the image (max row
535 seen), then from the top of the
536 image to the last row seen */
537 /* first row to end */
538 (lc->update_func)(lc->pixbuf, 0,
539 lc->first_row_seen_in_chunk,
541 (lc->max_row_seen_in_chunk -
542 lc->first_row_seen_in_chunk) + 1,
543 lc->notify_user_data);
544 /* top to last row */
545 (lc->update_func)(lc->pixbuf,
548 lc->last_row_seen_in_chunk + 1,
549 lc->notify_user_data);
551 /* We made at least one entire pass, so update the
553 (lc->update_func)(lc->pixbuf,
556 lc->max_row_seen_in_chunk + 1,
557 lc->notify_user_data);
567 /* Called at the start of the progressive load, once we have image info */
569 png_info_callback (png_structp png_read_ptr,
570 png_infop png_info_ptr)
573 png_uint_32 width, height;
574 png_textp png_text_ptr;
577 gboolean have_alpha = FALSE;
579 lc = png_get_progressive_ptr(png_read_ptr);
581 if (lc->fatal_error_occurred)
584 if (!setup_png_transformations(lc->png_read_ptr,
587 &width, &height, &color_type)) {
588 lc->fatal_error_occurred = TRUE;
592 /* If we have alpha, set a flag */
593 if (color_type & PNG_COLOR_MASK_ALPHA)
596 lc->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
598 if (lc->pixbuf == NULL) {
599 /* Failed to allocate memory */
600 lc->fatal_error_occurred = TRUE;
601 if (lc->error && *lc->error == NULL) {
602 g_set_error (lc->error,
604 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
605 _("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
611 /* Extract text chunks and attach them as pixbuf options */
613 if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
614 for (i = 0; i < num_texts; i++) {
617 if (png_text_to_pixbuf_option (png_text_ptr[i],
619 gdk_pixbuf_set_option (lc->pixbuf, key, value);
626 /* Notify the client that we are ready to go */
628 if (lc->prepare_func)
629 (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
634 /* Called for each row; note that you will get duplicate row numbers
635 for interlaced PNGs */
637 png_row_callback (png_structp png_read_ptr,
643 guchar* old_row = NULL;
645 lc = png_get_progressive_ptr(png_read_ptr);
647 if (lc->fatal_error_occurred)
650 if (row_num < 0 || row_num >= lc->pixbuf->height) {
651 lc->fatal_error_occurred = TRUE;
652 if (lc->error && *lc->error == NULL) {
653 g_set_error (lc->error,
655 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
656 _("Fatal error reading PNG image file"));
661 if (lc->first_row_seen_in_chunk < 0) {
662 lc->first_row_seen_in_chunk = row_num;
663 lc->first_pass_seen_in_chunk = pass_num;
666 lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
667 lc->last_row_seen_in_chunk = row_num;
668 lc->last_pass_seen_in_chunk = pass_num;
670 old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);
672 png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
675 /* Called after reading the entire image */
677 png_end_callback (png_structp png_read_ptr,
678 png_infop png_info_ptr)
682 lc = png_get_progressive_ptr(png_read_ptr);
684 if (lc->fatal_error_occurred)
689 png_error_callback(png_structp png_read_ptr,
690 png_const_charp error_msg)
694 lc = png_get_error_ptr(png_read_ptr);
696 lc->fatal_error_occurred = TRUE;
698 /* I don't trust libpng to call the error callback only once,
699 * so check for already-set error
701 if (lc->error && *lc->error == NULL) {
702 g_set_error (lc->error,
704 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
705 _("Fatal error reading PNG image file: %s"),
709 longjmp (png_read_ptr->jmpbuf, 1);
713 png_warning_callback(png_structp png_read_ptr,
714 png_const_charp warning_msg)
718 lc = png_get_error_ptr(png_read_ptr);
720 /* Don't print anything; we should not be dumping junk to
721 * stderr, since that may be bad for some apps. If it's
722 * important enough to display, we need to add a GError
723 * **warning return location wherever we have an error return
732 gdk_pixbuf__png_image_save (FILE *f,
740 png_textp text_ptr = NULL;
751 gboolean success = TRUE;
760 for (kiter = keys; *kiter; kiter++) {
761 if (strncmp (*kiter, "tEXt::", 6) != 0) {
762 g_warning ("Bad option name '%s' passed to PNG saver", *kiter);
767 if (len <= 1 || len > 79) {
770 GDK_PIXBUF_ERROR_BAD_OPTION,
771 _("Keys for PNG text chunks must have at least 1 and at most 79 characters."));
774 for (i = 0; i < len; i++) {
775 if ((guchar) key[i] > 127) {
778 GDK_PIXBUF_ERROR_BAD_OPTION,
779 _("Keys for PNG text chunks must be ASCII characters."));
788 text_ptr = g_new0 (png_text, num_keys);
789 for (i = 0; i < num_keys; i++) {
790 text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
791 text_ptr[i].key = keys[i] + 6;
792 text_ptr[i].text = g_convert (values[i], -1,
793 "ISO-8859-1", "UTF-8",
794 NULL, &text_ptr[i].text_length,
797 #ifdef PNG_iTXt_SUPPORTED
798 if (!text_ptr[i].text) {
799 text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE;
800 text_ptr[i].text = g_strdup (values[i]);
801 text_ptr[i].text_length = 0;
802 text_ptr[i].itxt_length = strlen (text_ptr[i].text);
803 text_ptr[i].lang = NULL;
804 text_ptr[i].lang_key = NULL;
808 if (!text_ptr[i].text) {
811 GDK_PIXBUF_ERROR_BAD_OPTION,
812 _("Value for PNG text chunk %s can not be converted to ISO-8859-1 encoding."), keys[i] + 6);
814 for (i = 0; i < num_keys; i++)
815 g_free (text_ptr[i].text);
822 bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
823 w = gdk_pixbuf_get_width (pixbuf);
824 h = gdk_pixbuf_get_height (pixbuf);
825 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
826 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
827 pixels = gdk_pixbuf_get_pixels (pixbuf);
829 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
831 png_simple_error_callback,
832 png_simple_warning_callback);
834 g_return_val_if_fail (png_ptr != NULL, FALSE);
836 info_ptr = png_create_info_struct (png_ptr);
837 if (info_ptr == NULL) {
841 if (setjmp (png_ptr->jmpbuf)) {
847 png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
850 png_init_io (png_ptr, f);
853 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
854 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
855 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
857 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
858 PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
859 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
865 png_set_sBIT (png_ptr, info_ptr, &sig_bit);
866 png_write_info (png_ptr, info_ptr);
867 png_set_shift (png_ptr, &sig_bit);
868 png_set_packing (png_ptr);
871 for (y = 0; y < h; y++) {
872 row_ptr = (png_bytep)ptr;
873 png_write_rows (png_ptr, &row_ptr, 1);
877 png_write_end (png_ptr, info_ptr);
880 png_destroy_write_struct (&png_ptr, &info_ptr);
883 for (i = 0; i < num_keys; i++)
884 g_free (text_ptr[i].text);
892 MODULE_ENTRY (png, fill_vtable) (GdkPixbufModule *module)
894 module->load = gdk_pixbuf__png_image_load;
895 module->begin_load = gdk_pixbuf__png_image_begin_load;
896 module->stop_load = gdk_pixbuf__png_image_stop_load;
897 module->load_increment = gdk_pixbuf__png_image_load_increment;
898 module->save = gdk_pixbuf__png_image_save;
902 MODULE_ENTRY (png, fill_info) (GdkPixbufFormat *info)
904 static GdkPixbufModulePattern signature[] = {
905 { "\x89PNG\r\n\x1a\x0a", NULL, 100 },
908 static gchar * mime_types[] = {
912 static gchar * extensions[] = {
918 info->signature = signature;
919 info->description = N_("The PNG image format");
920 info->mime_types = mime_types;
921 info->extensions = extensions;
922 info->flags = GDK_PIXBUF_FORMAT_WRITABLE;