1 /* GdkPixbuf library - PNG image loader
3 * Copyright (C) 1999 Mark Crichton
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Mark Crichton <crichton@gimp.org>
7 * Federico Mena-Quintero <federico@gimp.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
29 #include "gdk-pixbuf-private.h"
30 #include "gdk-pixbuf-io.h"
35 setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
36 gboolean *fatal_error_occurred,
37 png_uint_32* width_p, png_uint_32* height_p,
40 png_uint_32 width, height;
41 int bit_depth, color_type, interlace_type, compression_type, filter_type;
44 /* Get the image info */
46 png_get_IHDR (png_read_ptr, png_info_ptr,
54 /* set_expand() basically needs to be called unless
55 we are already in RGB/RGBA mode
57 if (color_type == PNG_COLOR_TYPE_PALETTE &&
60 /* Convert indexed images to RGB */
61 png_set_expand (png_read_ptr);
63 } else if (color_type == PNG_COLOR_TYPE_GRAY &&
66 /* Convert grayscale to RGB */
67 png_set_expand (png_read_ptr);
69 } else if (png_get_valid (png_read_ptr,
70 png_info_ptr, PNG_INFO_tRNS)) {
72 /* If we have transparency header, convert it to alpha
74 png_set_expand(png_read_ptr);
76 } else if (bit_depth < 8) {
78 /* If we have < 8 scale it up to 8 */
79 png_set_expand(png_read_ptr);
82 /* Conceivably, png_set_packing() is a better idea;
83 * God only knows how libpng works
87 /* If we are 16-bit, convert to 8-bit */
88 if (bit_depth == 16) {
89 png_set_strip_16(png_read_ptr);
92 /* If gray scale, convert to RGB */
93 if (color_type == PNG_COLOR_TYPE_GRAY ||
94 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
95 png_set_gray_to_rgb(png_read_ptr);
98 /* If interlaced, handle that */
99 if (interlace_type != PNG_INTERLACE_NONE) {
100 png_set_interlace_handling(png_read_ptr);
103 /* Update the info the reflect our transformations */
104 png_read_update_info(png_read_ptr, png_info_ptr);
106 png_get_IHDR (png_read_ptr, png_info_ptr,
116 *color_type_p = color_type;
118 #ifndef G_DISABLE_CHECKS
119 /* Check that the new info is what we want */
121 if (bit_depth != 8) {
122 g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
123 *fatal_error_occurred = TRUE;
127 if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
128 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
129 g_warning("Transformed PNG not RGB or RGBA.");
130 *fatal_error_occurred = TRUE;
134 channels = png_get_channels(png_read_ptr, png_info_ptr);
135 if ( ! (channels == 3 || channels == 4) ) {
136 g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
137 *fatal_error_occurred = TRUE;
144 png_simple_error_callback(png_structp png_save_ptr,
145 png_const_charp error_msg)
149 error = png_get_error_ptr(png_save_ptr);
151 /* I don't trust libpng to call the error callback only once,
152 * so check for already-set error
154 if (error && *error == NULL) {
157 GDK_PIXBUF_ERROR_FAILED,
158 _("Fatal error in PNG image file: %s"),
164 png_simple_warning_callback(png_structp png_save_ptr,
165 png_const_charp warning_msg)
167 /* Don't print anything; we should not be dumping junk to
168 * stderr, since that may be bad for some apps. If it's
169 * important enough to display, we need to add a GError
170 * **warning return location wherever we have an error return
175 /* Destroy notification function for the pixbuf */
177 free_buffer (guchar *pixels, gpointer data)
182 /* Shared library entry point */
184 gdk_pixbuf__png_image_load (FILE *f, GError **error)
187 png_infop info_ptr, end_info;
188 gboolean failed = FALSE;
194 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
196 png_simple_error_callback,
197 png_simple_warning_callback);
201 info_ptr = png_create_info_struct (png_ptr);
203 png_destroy_read_struct (&png_ptr, NULL, NULL);
207 end_info = png_create_info_struct (png_ptr);
209 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
213 if (setjmp (png_ptr->jmpbuf)) {
214 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
218 png_init_io (png_ptr, f);
219 png_read_info (png_ptr, info_ptr);
221 setup_png_transformations(png_ptr, info_ptr, &failed, &w, &h, &ctype);
224 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
228 if (ctype & PNG_COLOR_MASK_ALPHA)
233 pixels = malloc (w * h * bpp);
235 /* Check error NULL, normally this would be broken,
236 * but libpng makes me want to code defensively.
238 if (error && *error == NULL) {
241 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
242 _("Insufficient memory to load PNG file"));
245 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
249 rows = g_new (png_bytep, h);
251 for (i = 0; i < h; i++)
252 rows[i] = pixels + i * w * bpp;
254 png_read_image (png_ptr, rows);
255 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
258 if (ctype & PNG_COLOR_MASK_ALPHA)
259 return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
263 return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
268 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
269 just allow you to change the error reporting. */
270 static void png_error_callback (png_structp png_read_ptr,
271 png_const_charp error_msg);
273 static void png_warning_callback(png_structp png_read_ptr,
274 png_const_charp warning_msg);
276 /* Called at the start of the progressive load */
277 static void png_info_callback (png_structp png_read_ptr,
278 png_infop png_info_ptr);
280 /* Called for each row; note that you will get duplicate row numbers
281 for interlaced PNGs */
282 static void png_row_callback (png_structp png_read_ptr,
287 /* Called after reading the entire image */
288 static void png_end_callback (png_structp png_read_ptr,
289 png_infop png_info_ptr);
291 typedef struct _LoadContext LoadContext;
293 struct _LoadContext {
294 png_structp png_read_ptr;
295 png_infop png_info_ptr;
297 ModulePreparedNotifyFunc prepare_func;
298 ModuleUpdatedNotifyFunc update_func;
299 gpointer notify_user_data;
303 /* row number of first row seen, or -1 if none yet seen */
305 gint first_row_seen_in_chunk;
307 /* pass number for the first row seen */
309 gint first_pass_seen_in_chunk;
311 /* row number of last row seen */
312 gint last_row_seen_in_chunk;
314 gint last_pass_seen_in_chunk;
316 /* highest row number seen */
317 gint max_row_seen_in_chunk;
319 guint fatal_error_occurred : 1;
325 gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func,
326 ModuleUpdatedNotifyFunc update_func,
327 ModuleFrameDoneNotifyFunc frame_done_func,
328 ModuleAnimationDoneNotifyFunc anim_done_func,
334 lc = g_new0(LoadContext, 1);
336 lc->fatal_error_occurred = FALSE;
338 lc->prepare_func = prepare_func;
339 lc->update_func = update_func;
340 lc->notify_user_data = user_data;
342 lc->first_row_seen_in_chunk = -1;
343 lc->last_row_seen_in_chunk = -1;
344 lc->first_pass_seen_in_chunk = -1;
345 lc->last_pass_seen_in_chunk = -1;
346 lc->max_row_seen_in_chunk = -1;
349 /* Create the main PNG context struct */
352 lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
353 lc, /* error/warning callback data */
355 png_warning_callback);
357 if (lc->png_read_ptr == NULL) {
359 /* error callback should have set the error */
363 if (setjmp (lc->png_read_ptr->jmpbuf)) {
364 if (lc->png_info_ptr)
365 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
367 /* error callback should have set the error */
371 /* Create the auxiliary context struct */
373 lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
375 if (lc->png_info_ptr == NULL) {
376 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
378 /* error callback should have set the error */
382 png_set_progressive_read_fn(lc->png_read_ptr,
383 lc, /* callback data */
389 /* We don't want to keep modifying error after returning here,
390 * it may no longer be valid.
398 gdk_pixbuf__png_image_stop_load (gpointer context)
400 LoadContext* lc = context;
402 g_return_if_fail(lc != NULL);
404 gdk_pixbuf_unref(lc->pixbuf);
406 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
411 gdk_pixbuf__png_image_load_increment(gpointer context, guchar *buf, guint size,
414 LoadContext* lc = context;
416 g_return_val_if_fail(lc != NULL, FALSE);
419 lc->first_row_seen_in_chunk = -1;
420 lc->last_row_seen_in_chunk = -1;
421 lc->first_pass_seen_in_chunk = -1;
422 lc->last_pass_seen_in_chunk = -1;
423 lc->max_row_seen_in_chunk = -1;
426 /* Invokes our callbacks as needed */
427 if (setjmp (lc->png_read_ptr->jmpbuf)) {
431 png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
434 if (lc->fatal_error_occurred) {
438 if (lc->first_row_seen_in_chunk >= 0) {
439 /* We saw at least one row */
440 gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
442 g_assert(pass_diff >= 0);
444 if (pass_diff == 0) {
445 /* start and end row were in the same pass */
446 (lc->update_func)(lc->pixbuf, 0,
447 lc->first_row_seen_in_chunk,
449 (lc->last_row_seen_in_chunk -
450 lc->first_row_seen_in_chunk) + 1,
451 lc->notify_user_data);
452 } else if (pass_diff == 1) {
453 /* We have from the first row seen to
454 the end of the image (max row
455 seen), then from the top of the
456 image to the last row seen */
457 /* first row to end */
458 (lc->update_func)(lc->pixbuf, 0,
459 lc->first_row_seen_in_chunk,
461 (lc->max_row_seen_in_chunk -
462 lc->first_row_seen_in_chunk) + 1,
463 lc->notify_user_data);
464 /* top to last row */
465 (lc->update_func)(lc->pixbuf,
468 lc->last_row_seen_in_chunk + 1,
469 lc->notify_user_data);
471 /* We made at least one entire pass, so update the
473 (lc->update_func)(lc->pixbuf,
476 lc->max_row_seen_in_chunk + 1,
477 lc->notify_user_data);
487 /* Called at the start of the progressive load, once we have image info */
489 png_info_callback (png_structp png_read_ptr,
490 png_infop png_info_ptr)
493 png_uint_32 width, height;
495 gboolean have_alpha = FALSE;
496 gboolean failed = FALSE;
498 lc = png_get_progressive_ptr(png_read_ptr);
500 if (lc->fatal_error_occurred)
503 setup_png_transformations(lc->png_read_ptr,
506 &width, &height, &color_type);
509 lc->fatal_error_occurred = TRUE;
513 /* If we have alpha, set a flag */
514 if (color_type & PNG_COLOR_MASK_ALPHA)
517 lc->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
519 if (lc->pixbuf == NULL) {
520 /* Failed to allocate memory */
521 lc->fatal_error_occurred = TRUE;
522 if (lc->error && *lc->error == NULL) {
523 g_set_error (lc->error,
525 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
526 _("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
532 /* Notify the client that we are ready to go */
534 if (lc->prepare_func)
535 (* lc->prepare_func) (lc->pixbuf, lc->notify_user_data);
540 /* Called for each row; note that you will get duplicate row numbers
541 for interlaced PNGs */
543 png_row_callback (png_structp png_read_ptr,
549 guchar* old_row = NULL;
551 lc = png_get_progressive_ptr(png_read_ptr);
553 if (lc->fatal_error_occurred)
556 if (lc->first_row_seen_in_chunk < 0) {
557 lc->first_row_seen_in_chunk = row_num;
558 lc->first_pass_seen_in_chunk = pass_num;
561 lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
562 lc->last_row_seen_in_chunk = row_num;
563 lc->last_pass_seen_in_chunk = pass_num;
565 old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);
567 png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
570 /* Called after reading the entire image */
572 png_end_callback (png_structp png_read_ptr,
573 png_infop png_info_ptr)
577 lc = png_get_progressive_ptr(png_read_ptr);
579 if (lc->fatal_error_occurred)
584 png_error_callback(png_structp png_read_ptr,
585 png_const_charp error_msg)
589 lc = png_get_error_ptr(png_read_ptr);
591 lc->fatal_error_occurred = TRUE;
593 /* I don't trust libpng to call the error callback only once,
594 * so check for already-set error
596 if (lc->error && *lc->error == NULL) {
597 g_set_error (lc->error,
599 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
600 _("Fatal error reading PNG image file: %s"),
606 png_warning_callback(png_structp png_read_ptr,
607 png_const_charp warning_msg)
611 lc = png_get_error_ptr(png_read_ptr);
613 /* Don't print anything; we should not be dumping junk to
614 * stderr, since that may be bad for some apps. If it's
615 * important enough to display, we need to add a GError
616 * **warning return location wherever we have an error return
625 gdk_pixbuf__png_image_save (FILE *f,
636 png_bytep row_ptr, data = NULL;
643 g_warning ("Bad option name '%s' passed to PNG saver",
647 gchar **kiter = keys;
648 gchar **viter = values;
659 bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
660 w = gdk_pixbuf_get_width (pixbuf);
661 h = gdk_pixbuf_get_height (pixbuf);
662 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
663 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
664 pixels = gdk_pixbuf_get_pixels (pixbuf);
666 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
668 png_simple_error_callback,
669 png_simple_warning_callback);
671 g_return_val_if_fail (png_ptr != NULL, FALSE);
673 info_ptr = png_create_info_struct (png_ptr);
674 if (info_ptr == NULL) {
675 png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
678 if (setjmp (png_ptr->jmpbuf)) {
679 png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
682 png_init_io (png_ptr, f);
684 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
685 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
686 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
687 #ifdef WORDS_BIGENDIAN
688 png_set_swap_alpha (png_ptr);
690 png_set_bgr (png_ptr);
693 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
694 PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
695 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
696 data = malloc (w * 3 * sizeof (char));
699 /* Check error NULL, normally this would be broken,
700 * but libpng makes me want to code defensively.
702 if (error && *error == NULL) {
705 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
706 _("Insufficient memory to save PNG file"));
708 png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
716 png_set_sBIT (png_ptr, info_ptr, &sig_bit);
717 png_write_info (png_ptr, info_ptr);
718 png_set_shift (png_ptr, &sig_bit);
719 png_set_packing (png_ptr);
722 for (y = 0; y < h; y++) {
724 row_ptr = (png_bytep)ptr;
726 for (j = 0, x = 0; x < w; x++)
727 memcpy (&(data[x*3]), &(ptr[x*3]), 3);
729 row_ptr = (png_bytep)data;
731 png_write_rows (png_ptr, &row_ptr, 1);
738 png_write_end (png_ptr, info_ptr);
739 png_destroy_write_struct (&png_ptr, (png_infopp) NULL);