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) {
52 g_set_error_literal (error,
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) {
133 g_set_error_literal (error,
135 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
136 _("Transformed PNG has zero width or height."));
140 if (bit_depth != 8) {
141 g_set_error_literal (error,
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) ) {
150 g_set_error_literal (error,
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) ) {
159 g_set_error_literal (error,
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 gboolean is_ascii = TRUE;
210 /* Avoid loading iconv if the text is plain ASCII */
211 for (i = 0; i < text_ptr.text_length; i++)
212 if (text_ptr.text[i] & 0x80) {
218 *value = g_strdup (text_ptr.text);
220 *value = g_convert (text_ptr.text, -1,
221 "UTF-8", "ISO-8859-1",
226 *key = g_strconcat ("tEXt::", text_ptr.key, NULL);
229 g_warning ("Couldn't convert text chunk value to UTF-8.");
236 png_malloc_callback (png_structp o, png_size_t size)
238 return g_try_malloc (size);
242 png_free_callback (png_structp o, png_voidp x)
247 /* Shared library entry point */
249 gdk_pixbuf__png_image_load (FILE *f, GError **error)
251 GdkPixbuf * volatile pixbuf = NULL;
257 png_bytepp volatile rows = NULL;
261 gchar *icc_profile_base64;
262 const gchar *icc_profile_title;
263 const gchar *icc_profile;
264 png_uint_32 icc_profile_size;
266 gint compression_type;
268 #ifdef PNG_USER_MEM_SUPPORTED
269 png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
271 png_simple_error_callback,
272 png_simple_warning_callback,
277 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
279 png_simple_error_callback,
280 png_simple_warning_callback);
285 info_ptr = png_create_info_struct (png_ptr);
287 png_destroy_read_struct (&png_ptr, NULL, NULL);
291 if (setjmp (png_ptr->jmpbuf)) {
295 g_object_unref (pixbuf);
297 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
301 png_init_io (png_ptr, f);
302 png_read_info (png_ptr, info_ptr);
304 if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) {
305 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
309 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h);
312 if (error && *error == NULL) {
313 g_set_error_literal (error,
315 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
316 _("Insufficient memory to load PNG file"));
320 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
324 rows = g_new (png_bytep, h);
326 for (i = 0; i < h; i++)
327 rows[i] = pixbuf->pixels + i * pixbuf->rowstride;
329 png_read_image (png_ptr, rows);
330 png_read_end (png_ptr, info_ptr);
332 if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
333 for (i = 0; i < num_texts; i++) {
334 png_text_to_pixbuf_option (text_ptr[i], &key, &value);
335 gdk_pixbuf_set_option (pixbuf, key, value);
341 #if defined(PNG_cHRM_SUPPORTED)
342 /* Extract embedded ICC profile */
343 retval = png_get_iCCP (png_ptr, info_ptr,
344 (png_charpp) &icc_profile_title, &compression_type,
345 (png_charpp) &icc_profile, (png_uint_32*) &icc_profile_size);
347 icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size);
348 gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64);
349 g_free (icc_profile_base64);
354 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
359 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
360 just allow you to change the error reporting. */
361 static void png_error_callback (png_structp png_read_ptr,
362 png_const_charp error_msg);
364 static void png_warning_callback (png_structp png_read_ptr,
365 png_const_charp warning_msg);
367 /* Called at the start of the progressive load */
368 static void png_info_callback (png_structp png_read_ptr,
369 png_infop png_info_ptr);
371 /* Called for each row; note that you will get duplicate row numbers
372 for interlaced PNGs */
373 static void png_row_callback (png_structp png_read_ptr,
378 /* Called after reading the entire image */
379 static void png_end_callback (png_structp png_read_ptr,
380 png_infop png_info_ptr);
382 typedef struct _LoadContext LoadContext;
384 struct _LoadContext {
385 png_structp png_read_ptr;
386 png_infop png_info_ptr;
388 GdkPixbufModuleSizeFunc size_func;
389 GdkPixbufModulePreparedFunc prepare_func;
390 GdkPixbufModuleUpdatedFunc update_func;
391 gpointer notify_user_data;
395 /* row number of first row seen, or -1 if none yet seen */
397 gint first_row_seen_in_chunk;
399 /* pass number for the first row seen */
401 gint first_pass_seen_in_chunk;
403 /* row number of last row seen */
404 gint last_row_seen_in_chunk;
406 gint last_pass_seen_in_chunk;
408 /* highest row number seen */
409 gint max_row_seen_in_chunk;
411 guint fatal_error_occurred : 1;
417 gdk_pixbuf__png_image_begin_load (GdkPixbufModuleSizeFunc size_func,
418 GdkPixbufModulePreparedFunc prepare_func,
419 GdkPixbufModuleUpdatedFunc update_func,
425 lc = g_new0(LoadContext, 1);
427 lc->fatal_error_occurred = FALSE;
429 lc->size_func = size_func;
430 lc->prepare_func = prepare_func;
431 lc->update_func = update_func;
432 lc->notify_user_data = user_data;
434 lc->first_row_seen_in_chunk = -1;
435 lc->last_row_seen_in_chunk = -1;
436 lc->first_pass_seen_in_chunk = -1;
437 lc->last_pass_seen_in_chunk = -1;
438 lc->max_row_seen_in_chunk = -1;
441 /* Create the main PNG context struct */
443 #ifdef PNG_USER_MEM_SUPPORTED
444 lc->png_read_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
445 lc, /* error/warning callback data */
447 png_warning_callback,
452 lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
453 lc, /* error/warning callback data */
455 png_warning_callback);
457 if (lc->png_read_ptr == NULL) {
459 /* error callback should have set the error */
463 if (setjmp (lc->png_read_ptr->jmpbuf)) {
464 if (lc->png_info_ptr)
465 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
467 /* error callback should have set the error */
471 /* Create the auxiliary context struct */
473 lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
475 if (lc->png_info_ptr == NULL) {
476 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
478 /* error callback should have set the error */
482 png_set_progressive_read_fn(lc->png_read_ptr,
483 lc, /* callback data */
489 /* We don't want to keep modifying error after returning here,
490 * it may no longer be valid.
498 gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
500 LoadContext* lc = context;
502 g_return_val_if_fail(lc != NULL, TRUE);
504 /* FIXME this thing needs to report errors if
505 * we have unused image data
509 g_object_unref (lc->pixbuf);
511 png_destroy_read_struct(&lc->png_read_ptr, &lc->png_info_ptr, NULL);
518 gdk_pixbuf__png_image_load_increment(gpointer context,
519 const guchar *buf, guint size,
522 LoadContext* lc = context;
524 g_return_val_if_fail(lc != NULL, FALSE);
527 lc->first_row_seen_in_chunk = -1;
528 lc->last_row_seen_in_chunk = -1;
529 lc->first_pass_seen_in_chunk = -1;
530 lc->last_pass_seen_in_chunk = -1;
531 lc->max_row_seen_in_chunk = -1;
534 /* Invokes our callbacks as needed */
535 if (setjmp (lc->png_read_ptr->jmpbuf)) {
539 png_process_data(lc->png_read_ptr, lc->png_info_ptr,
540 (guchar*) buf, size);
543 if (lc->fatal_error_occurred) {
547 if (lc->first_row_seen_in_chunk >= 0 && lc->update_func) {
548 /* We saw at least one row */
549 gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
551 g_assert(pass_diff >= 0);
553 if (pass_diff == 0) {
554 /* start and end row were in the same pass */
555 (lc->update_func)(lc->pixbuf, 0,
556 lc->first_row_seen_in_chunk,
558 (lc->last_row_seen_in_chunk -
559 lc->first_row_seen_in_chunk) + 1,
560 lc->notify_user_data);
561 } else if (pass_diff == 1) {
562 /* We have from the first row seen to
563 the end of the image (max row
564 seen), then from the top of the
565 image to the last row seen */
566 /* first row to end */
567 (lc->update_func)(lc->pixbuf, 0,
568 lc->first_row_seen_in_chunk,
570 (lc->max_row_seen_in_chunk -
571 lc->first_row_seen_in_chunk) + 1,
572 lc->notify_user_data);
573 /* top to last row */
574 (lc->update_func)(lc->pixbuf,
577 lc->last_row_seen_in_chunk + 1,
578 lc->notify_user_data);
580 /* We made at least one entire pass, so update the
582 (lc->update_func)(lc->pixbuf,
585 lc->max_row_seen_in_chunk + 1,
586 lc->notify_user_data);
596 /* Called at the start of the progressive load, once we have image info */
598 png_info_callback (png_structp png_read_ptr,
599 png_infop png_info_ptr)
602 png_uint_32 width, height;
603 png_textp png_text_ptr;
606 gboolean have_alpha = FALSE;
607 gchar *icc_profile_base64;
608 const gchar *icc_profile_title;
609 const gchar *icc_profile;
610 png_uint_32 icc_profile_size;
612 gint compression_type;
614 lc = png_get_progressive_ptr(png_read_ptr);
616 if (lc->fatal_error_occurred)
619 if (!setup_png_transformations(lc->png_read_ptr,
622 &width, &height, &color_type)) {
623 lc->fatal_error_occurred = TRUE;
627 /* If we have alpha, set a flag */
628 if (color_type & PNG_COLOR_MASK_ALPHA)
634 (* lc->size_func) (&w, &h, lc->notify_user_data);
636 if (w == 0 || h == 0) {
637 lc->fatal_error_occurred = TRUE;
638 if (lc->error && *lc->error == NULL) {
639 g_set_error_literal (lc->error,
641 GDK_PIXBUF_ERROR_FAILED,
642 _("Transformed PNG has zero width or height."));
648 lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
650 if (lc->pixbuf == NULL) {
651 /* Failed to allocate memory */
652 lc->fatal_error_occurred = TRUE;
653 if (lc->error && *lc->error == NULL) {
654 g_set_error (lc->error,
656 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
657 _("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
663 /* Extract text chunks and attach them as pixbuf options */
665 if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
666 for (i = 0; i < num_texts; i++) {
669 if (png_text_to_pixbuf_option (png_text_ptr[i],
671 gdk_pixbuf_set_option (lc->pixbuf, key, value);
678 #if defined(PNG_cHRM_SUPPORTED)
679 /* Extract embedded ICC profile */
680 retval = png_get_iCCP (png_read_ptr, png_info_ptr,
681 (png_charpp) &icc_profile_title, &compression_type,
682 (png_charpp) &icc_profile, &icc_profile_size);
684 icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size);
685 gdk_pixbuf_set_option (lc->pixbuf, "icc-profile", icc_profile_base64);
686 g_free (icc_profile_base64);
690 /* Notify the client that we are ready to go */
692 if (lc->prepare_func)
693 (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
698 /* Called for each row; note that you will get duplicate row numbers
699 for interlaced PNGs */
701 png_row_callback (png_structp png_read_ptr,
707 guchar* old_row = NULL;
709 lc = png_get_progressive_ptr(png_read_ptr);
711 if (lc->fatal_error_occurred)
714 if (row_num >= lc->pixbuf->height) {
715 lc->fatal_error_occurred = TRUE;
716 if (lc->error && *lc->error == NULL) {
717 g_set_error_literal (lc->error,
719 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
720 _("Fatal error reading PNG image file"));
725 if (lc->first_row_seen_in_chunk < 0) {
726 lc->first_row_seen_in_chunk = row_num;
727 lc->first_pass_seen_in_chunk = pass_num;
730 lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
731 lc->last_row_seen_in_chunk = row_num;
732 lc->last_pass_seen_in_chunk = pass_num;
734 old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);
736 png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
739 /* Called after reading the entire image */
741 png_end_callback (png_structp png_read_ptr,
742 png_infop png_info_ptr)
746 lc = png_get_progressive_ptr(png_read_ptr);
748 if (lc->fatal_error_occurred)
753 png_error_callback(png_structp png_read_ptr,
754 png_const_charp error_msg)
758 lc = png_get_error_ptr(png_read_ptr);
760 lc->fatal_error_occurred = TRUE;
762 /* I don't trust libpng to call the error callback only once,
763 * so check for already-set error
765 if (lc->error && *lc->error == NULL) {
766 g_set_error (lc->error,
768 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
769 _("Fatal error reading PNG image file: %s"),
773 longjmp (png_read_ptr->jmpbuf, 1);
777 png_warning_callback (png_structp png_read_ptr,
778 png_const_charp warning_msg)
782 lc = png_get_error_ptr(png_read_ptr);
784 /* Don't print anything; we should not be dumping junk to
785 * stderr, since that may be bad for some apps. If it's
786 * important enough to display, we need to add a GError
787 * **warning return location wherever we have an error return
796 GdkPixbufSaveFunc save_func;
799 } SaveToFunctionIoPtr;
802 png_save_to_callback_write_func (png_structp png_ptr,
806 SaveToFunctionIoPtr *ioptr = png_get_io_ptr (png_ptr);
808 if (!ioptr->save_func ((gchar *)data, length, ioptr->error, ioptr->user_data)) {
809 /* If save_func has already set an error, which it
810 should have done, this won't overwrite it. */
811 png_error (png_ptr, "write function failed");
816 png_save_to_callback_flush_func (png_structp png_ptr)
821 static gboolean real_save_png (GdkPixbuf *pixbuf,
825 gboolean to_callback,
827 GdkPixbufSaveFunc save_func,
830 png_structp png_ptr = NULL;
832 png_textp text_ptr = NULL;
843 int compression = -1;
844 gboolean success = TRUE;
845 guchar *icc_profile = NULL;
846 gsize icc_profile_size = 0;
847 SaveToFunctionIoPtr to_callback_ioptr;
852 gchar **kiter = keys;
853 gchar **viter = values;
856 if (strncmp (*kiter, "tEXt::", 6) == 0) {
857 gchar *key = *kiter + 6;
858 int len = strlen (key);
859 if (len <= 1 || len > 79) {
860 g_set_error_literal (error,
862 GDK_PIXBUF_ERROR_BAD_OPTION,
863 _("Keys for PNG text chunks must have at least 1 and at most 79 characters."));
867 for (i = 0; i < len; i++) {
868 if ((guchar) key[i] > 127) {
869 g_set_error_literal (error,
871 GDK_PIXBUF_ERROR_BAD_OPTION,
872 _("Keys for PNG text chunks must be ASCII characters."));
878 } else if (strcmp (*kiter, "icc-profile") == 0) {
879 /* decode from base64 */
880 icc_profile = g_base64_decode (*viter, &icc_profile_size);
881 if (icc_profile_size < 127) {
882 /* This is a user-visible error */
885 GDK_PIXBUF_ERROR_BAD_OPTION,
886 _("Color profile has invalid length %d."),
887 (gint)icc_profile_size);
891 } else if (strcmp (*kiter, "compression") == 0) {
893 compression = strtol (*viter, &endptr, 10);
895 if (endptr == *viter) {
898 GDK_PIXBUF_ERROR_BAD_OPTION,
899 _("PNG compression level must be a value between 0 and 9; value '%s' could not be parsed."),
904 if (compression < 0 || compression > 9) {
905 /* This is a user-visible error;
906 * lets people skip the range-checking
911 GDK_PIXBUF_ERROR_BAD_OPTION,
912 _("PNG compression level must be a value between 0 and 9; value '%d' is not allowed."),
918 g_warning ("Unrecognized parameter (%s) passed to PNG saver.", *kiter);
927 text_ptr = g_new0 (png_text, num_keys);
928 for (i = 0; i < num_keys; i++) {
929 text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
930 text_ptr[i].key = keys[i] + 6;
931 text_ptr[i].text = g_convert (values[i], -1,
932 "ISO-8859-1", "UTF-8",
933 NULL, &text_ptr[i].text_length,
936 #ifdef PNG_iTXt_SUPPORTED
937 if (!text_ptr[i].text) {
938 text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE;
939 text_ptr[i].text = g_strdup (values[i]);
940 text_ptr[i].text_length = 0;
941 text_ptr[i].itxt_length = strlen (text_ptr[i].text);
942 text_ptr[i].lang = NULL;
943 text_ptr[i].lang_key = NULL;
947 if (!text_ptr[i].text) {
950 GDK_PIXBUF_ERROR_BAD_OPTION,
951 _("Value for PNG text chunk %s cannot be converted to ISO-8859-1 encoding."), keys[i] + 6);
953 for (i = 0; i < num_keys; i++)
954 g_free (text_ptr[i].text);
961 bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
962 w = gdk_pixbuf_get_width (pixbuf);
963 h = gdk_pixbuf_get_height (pixbuf);
964 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
965 has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
966 pixels = gdk_pixbuf_get_pixels (pixbuf);
968 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
970 png_simple_error_callback,
971 png_simple_warning_callback);
972 if (png_ptr == NULL) {
977 info_ptr = png_create_info_struct (png_ptr);
978 if (info_ptr == NULL) {
982 if (setjmp (png_ptr->jmpbuf)) {
988 png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
992 to_callback_ioptr.save_func = save_func;
993 to_callback_ioptr.user_data = user_data;
994 to_callback_ioptr.error = error;
995 png_set_write_fn (png_ptr, &to_callback_ioptr,
996 png_save_to_callback_write_func,
997 png_save_to_callback_flush_func);
999 png_init_io (png_ptr, f);
1002 if (compression >= 0)
1003 png_set_compression_level (png_ptr, compression);
1005 #if defined(PNG_iCCP_SUPPORTED)
1006 /* the proper ICC profile title is encoded in the profile */
1007 if (icc_profile != NULL) {
1008 png_set_iCCP (png_ptr, info_ptr,
1009 "ICC profile", PNG_COMPRESSION_TYPE_BASE,
1010 (gchar*) icc_profile, icc_profile_size);
1015 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
1016 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
1017 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1019 png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
1020 PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1021 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1024 sig_bit.green = bpc;
1026 sig_bit.alpha = bpc;
1027 png_set_sBIT (png_ptr, info_ptr, &sig_bit);
1028 png_write_info (png_ptr, info_ptr);
1029 png_set_shift (png_ptr, &sig_bit);
1030 png_set_packing (png_ptr);
1033 for (y = 0; y < h; y++) {
1034 row_ptr = (png_bytep)ptr;
1035 png_write_rows (png_ptr, &row_ptr, 1);
1039 png_write_end (png_ptr, info_ptr);
1042 if (png_ptr != NULL)
1043 png_destroy_write_struct (&png_ptr, &info_ptr);
1045 g_free (icc_profile);
1047 if (text_ptr != NULL) {
1048 for (i = 0; i < num_keys; i++)
1049 g_free (text_ptr[i].text);
1057 gdk_pixbuf__png_image_save (FILE *f,
1063 return real_save_png (pixbuf, keys, values, error,
1064 FALSE, f, NULL, NULL);
1068 gdk_pixbuf__png_image_save_to_callback (GdkPixbufSaveFunc save_func,
1075 return real_save_png (pixbuf, keys, values, error,
1076 TRUE, NULL, save_func, user_data);
1080 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1082 #define MODULE_ENTRY(function) void _gdk_pixbuf__png_ ## function
1085 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1087 module->load = gdk_pixbuf__png_image_load;
1088 module->begin_load = gdk_pixbuf__png_image_begin_load;
1089 module->stop_load = gdk_pixbuf__png_image_stop_load;
1090 module->load_increment = gdk_pixbuf__png_image_load_increment;
1091 module->save = gdk_pixbuf__png_image_save;
1092 module->save_to_callback = gdk_pixbuf__png_image_save_to_callback;
1095 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1097 static GdkPixbufModulePattern signature[] = {
1098 { "\x89PNG\r\n\x1a\x0a", NULL, 100 },
1101 static gchar * mime_types[] = {
1105 static gchar * extensions[] = {
1111 info->signature = signature;
1112 info->description = N_("The PNG image format");
1113 info->mime_types = mime_types;
1114 info->extensions = extensions;
1115 info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1116 info->license = "LGPL";