1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - PNG image loader
3  *
4  * Copyright (C) 1999 Mark Crichton
5  * Copyright (C) 1999 The Free Software Foundation
6  *
7  * Authors: Mark Crichton <crichton@gimp.org>
8  *          Federico Mena-Quintero <federico@gimp.org>
9  *
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.
14  *
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.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <png.h>
29 #include <math.h>
30 #include <glib-object.h>
31 #include <glib/gi18n-lib.h>
32 
33 #include "gdk-pixbuf-core.h"
34 #include "gdk-pixbuf-io.h"
35 #include "fallback-c89.c"
36 
37 /* Helper macros to convert between density units */
38 #define DPI_TO_DPM(value) ((int) round ((value) * 1000 / 25.4))
39 #define DPM_TO_DPI(value) ((int) round ((value) * 25.4 / 1000))
40 
41 #define DEFAULT_FILL_COLOR 0x979899ff
42 
43 static gboolean
setup_png_transformations(png_structp png_read_ptr,png_infop png_info_ptr,GError ** error,png_uint_32 * width_p,png_uint_32 * height_p,int * color_type_p)44 setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
45                           GError **error,
46                           png_uint_32* width_p, png_uint_32* height_p,
47                           int* color_type_p)
48 {
49         png_uint_32 width, height;
50         int bit_depth, color_type, interlace_type, compression_type, filter_type;
51         int channels;
52 
53         /* Get the image info */
54 
55         /* Must check bit depth, since png_get_IHDR generates an
56            FPE on bit_depth 0.
57         */
58         bit_depth = png_get_bit_depth (png_read_ptr, png_info_ptr);
59         if (bit_depth < 1 || bit_depth > 16) {
60                 g_set_error_literal (error,
61                                      GDK_PIXBUF_ERROR,
62                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
63                                      _("Bits per channel of PNG image is invalid."));
64                 return FALSE;
65         }
66         png_get_IHDR (png_read_ptr, png_info_ptr,
67                       &width, &height,
68                       &bit_depth,
69                       &color_type,
70                       &interlace_type,
71                       &compression_type,
72                       &filter_type);
73 
74         /* set_expand() basically needs to be called unless
75            we are already in RGB/RGBA mode
76         */
77         if (color_type == PNG_COLOR_TYPE_PALETTE &&
78             bit_depth <= 8) {
79 
80                 /* Convert indexed images to RGB */
81                 png_set_expand (png_read_ptr);
82 
83         } else if (color_type == PNG_COLOR_TYPE_GRAY &&
84                    bit_depth < 8) {
85 
86                 /* Convert grayscale to RGB */
87                 png_set_expand (png_read_ptr);
88 
89         } else if (png_get_valid (png_read_ptr,
90                                   png_info_ptr, PNG_INFO_tRNS)) {
91 
92                 /* If we have transparency header, convert it to alpha
93                    channel */
94                 png_set_expand(png_read_ptr);
95 
96         } else if (bit_depth < 8) {
97 
98                 /* If we have < 8 scale it up to 8 */
99                 png_set_expand(png_read_ptr);
100 
101 
102                 /* Conceivably, png_set_packing() is a better idea;
103                  * God only knows how libpng works
104                  */
105         }
106 
107         /* If we are 16-bit, convert to 8-bit */
108         if (bit_depth == 16) {
109                 png_set_strip_16(png_read_ptr);
110         }
111 
112         /* If gray scale, convert to RGB */
113         if (color_type == PNG_COLOR_TYPE_GRAY ||
114             color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
115                 png_set_gray_to_rgb(png_read_ptr);
116         }
117 
118         /* If interlaced, handle that */
119         if (interlace_type != PNG_INTERLACE_NONE) {
120                 png_set_interlace_handling(png_read_ptr);
121         }
122 
123         /* Update the info the reflect our transformations */
124         png_read_update_info(png_read_ptr, png_info_ptr);
125 
126         png_get_IHDR (png_read_ptr, png_info_ptr,
127                       &width, &height,
128                       &bit_depth,
129                       &color_type,
130                       &interlace_type,
131                       &compression_type,
132                       &filter_type);
133 
134         *width_p = width;
135         *height_p = height;
136         *color_type_p = color_type;
137 
138         /* Check that the new info is what we want */
139 
140         if (width == 0 || height == 0) {
141                 g_set_error_literal (error,
142                                      GDK_PIXBUF_ERROR,
143                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
144                                      _("Transformed PNG has zero width or height."));
145                 return FALSE;
146         }
147 
148         if (bit_depth != 8) {
149                 g_set_error_literal (error,
150                                      GDK_PIXBUF_ERROR,
151                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
152                                      _("Bits per channel of transformed PNG is not 8."));
153                 return FALSE;
154         }
155 
156         if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
157                 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
158                 g_set_error_literal (error,
159                                      GDK_PIXBUF_ERROR,
160                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
161                                      _("Transformed PNG not RGB or RGBA."));
162                 return FALSE;
163         }
164 
165         channels = png_get_channels(png_read_ptr, png_info_ptr);
166         if ( ! (channels == 3 || channels == 4) ) {
167                 g_set_error_literal (error,
168                                      GDK_PIXBUF_ERROR,
169                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
170                                      _("Transformed PNG has unsupported number of channels, must be 3 or 4."));
171                 return FALSE;
172         }
173         return TRUE;
174 }
175 
176 static void
png_simple_error_callback(png_structp png_save_ptr,png_const_charp error_msg)177 png_simple_error_callback(png_structp png_save_ptr,
178                           png_const_charp error_msg)
179 {
180         GError **error;
181 
182         error = png_get_error_ptr(png_save_ptr);
183 
184         /* I don't trust libpng to call the error callback only once,
185          * so check for already-set error
186          */
187         if (error && *error == NULL) {
188                 g_set_error (error,
189                              GDK_PIXBUF_ERROR,
190                              GDK_PIXBUF_ERROR_FAILED,
191                              _("Fatal error in PNG image file: %s"),
192                              error_msg);
193         }
194 
195         longjmp (png_jmpbuf(png_save_ptr), 1);
196 }
197 
198 static void
png_simple_warning_callback(png_structp png_save_ptr,png_const_charp warning_msg)199 png_simple_warning_callback(png_structp png_save_ptr,
200                             png_const_charp warning_msg)
201 {
202         /* Don't print anything; we should not be dumping junk to
203          * stderr, since that may be bad for some apps. If it's
204          * important enough to display, we need to add a GError
205          * **warning return location wherever we have an error return
206          * location.
207          */
208 }
209 
210 static gboolean
png_text_to_pixbuf_option(png_text text_ptr,gchar ** key,gchar ** value)211 png_text_to_pixbuf_option (png_text   text_ptr,
212                            gchar    **key,
213                            gchar    **value)
214 {
215         gboolean is_ascii = TRUE;
216         int i;
217 
218         /* Avoid loading iconv if the text is plain ASCII */
219         for (i = 0; i < text_ptr.text_length; i++)
220                 if (text_ptr.text[i] & 0x80) {
221                         is_ascii = FALSE;
222                         break;
223                 }
224 
225         if (is_ascii) {
226                 *value = g_strdup (text_ptr.text);
227         } else {
228                 *value = g_convert (text_ptr.text, -1,
229                                      "UTF-8", "ISO-8859-1",
230                                      NULL, NULL, NULL);
231         }
232 
233         if (*value) {
234                 *key = g_strconcat ("tEXt::", text_ptr.key, NULL);
235                 return TRUE;
236         } else {
237                 g_warning ("Couldn't convert text chunk value to UTF-8.");
238                 *key = NULL;
239                 return FALSE;
240         }
241 }
242 
243 static png_voidp
png_malloc_callback(png_structp o,png_size_t size)244 png_malloc_callback (png_structp o, png_size_t size)
245 {
246         return g_try_malloc (size);
247 }
248 
249 static void
png_free_callback(png_structp o,png_voidp x)250 png_free_callback (png_structp o, png_voidp x)
251 {
252         g_free (x);
253 }
254 
255 /* Shared library entry point */
256 static GdkPixbuf *
gdk_pixbuf__png_image_load(FILE * f,GError ** error)257 gdk_pixbuf__png_image_load (FILE *f, GError **error)
258 {
259         GdkPixbuf * volatile pixbuf = NULL;
260         gint rowstride;
261 	png_structp png_ptr;
262 	png_infop info_ptr;
263         png_textp text_ptr;
264 	gint i, ctype;
265 	png_uint_32 w, h;
266 	png_bytepp volatile rows = NULL;
267         gint    num_texts;
268         gchar *key;
269         gchar *value;
270         gchar *icc_profile_base64;
271         const gchar *icc_profile_title;
272         const gchar *icc_profile;
273         png_uint_32 icc_profile_size;
274         png_uint_32 x_resolution;
275         png_uint_32 y_resolution;
276         int unit_type;
277         gchar *density_str;
278         guint32 retval;
279         gint compression_type;
280         gpointer ptr;
281 
282 #ifdef PNG_USER_MEM_SUPPORTED
283 	png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
284                                             error,
285                                             png_simple_error_callback,
286                                             png_simple_warning_callback,
287                                             NULL,
288                                             png_malloc_callback,
289                                             png_free_callback);
290 #else
291 	png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
292                                           error,
293                                           png_simple_error_callback,
294                                           png_simple_warning_callback);
295 #endif
296 	if (!png_ptr)
297 		return NULL;
298 
299 	info_ptr = png_create_info_struct (png_ptr);
300 	if (!info_ptr) {
301 		png_destroy_read_struct (&png_ptr, NULL, NULL);
302 		return NULL;
303 	}
304 
305 	if (setjmp (png_jmpbuf(png_ptr))) {
306 	    	g_free (rows);
307 
308 		if (pixbuf)
309 			g_object_unref (pixbuf);
310 
311 		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
312 		return NULL;
313 	}
314 
315 	png_init_io (png_ptr, f);
316 	png_read_info (png_ptr, info_ptr);
317 
318         if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) {
319                 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
320                 return NULL;
321         }
322 
323         pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h);
324 
325 	if (!pixbuf) {
326                 g_set_error_literal (error,
327                                      GDK_PIXBUF_ERROR,
328                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
329                                      _("Insufficient memory to load PNG file"));
330 
331 		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
332 		return NULL;
333 	}
334 
335         rowstride = gdk_pixbuf_get_rowstride (pixbuf);
336 
337         gdk_pixbuf_fill (pixbuf, DEFAULT_FILL_COLOR);
338 
339 	rows = g_new (png_bytep, h);
340 
341         for (i = 0, ptr = gdk_pixbuf_get_pixels (pixbuf); i < h; i++, ptr = (guchar *) ptr + rowstride)
342 		rows[i] = ptr;
343 
344 	png_read_image (png_ptr, rows);
345         png_read_end (png_ptr, info_ptr);
346 
347         if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
348                 for (i = 0; i < num_texts; i++) {
349                         png_text_to_pixbuf_option (text_ptr[i], &key, &value);
350                         gdk_pixbuf_set_option (pixbuf, key, value);
351                         g_free (key);
352                         g_free (value);
353                 }
354         }
355 
356 #if defined(PNG_cHRM_SUPPORTED)
357         /* Extract embedded ICC profile */
358         retval = png_get_iCCP (png_ptr, info_ptr,
359                                (png_charpp) &icc_profile_title, &compression_type,
360                                (png_bytepp) &icc_profile, (png_uint_32*) &icc_profile_size);
361         if (retval != 0) {
362                 icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size);
363                 gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64);
364                 g_free (icc_profile_base64);
365         }
366 #endif
367 
368 #ifdef PNG_pHYs_SUPPORTED
369         retval = png_get_pHYs (png_ptr, info_ptr, &x_resolution, &y_resolution, &unit_type);
370         if (retval != 0 && unit_type == PNG_RESOLUTION_METER) {
371                 density_str = g_strdup_printf ("%d", DPM_TO_DPI (x_resolution));
372                 gdk_pixbuf_set_option (pixbuf, "x-dpi", density_str);
373                 g_free (density_str);
374                 density_str = g_strdup_printf ("%d", DPM_TO_DPI (y_resolution));
375                 gdk_pixbuf_set_option (pixbuf, "y-dpi", density_str);
376                 g_free (density_str);
377         }
378 #endif
379 
380 	g_free (rows);
381 	png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
382 
383         return pixbuf;
384 }
385 
386 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
387    just allow you to change the error reporting. */
388 static void png_error_callback  (png_structp png_read_ptr,
389                                  png_const_charp error_msg);
390 
391 static void png_warning_callback (png_structp png_read_ptr,
392                                   png_const_charp warning_msg);
393 
394 /* Called at the start of the progressive load */
395 static void png_info_callback   (png_structp png_read_ptr,
396                                  png_infop   png_info_ptr);
397 
398 /* Called for each row; note that you will get duplicate row numbers
399    for interlaced PNGs */
400 static void png_row_callback   (png_structp png_read_ptr,
401                                 png_bytep   new_row,
402                                 png_uint_32 row_num,
403                                 int pass_num);
404 
405 /* Called after reading the entire image */
406 static void png_end_callback   (png_structp png_read_ptr,
407                                 png_infop   png_info_ptr);
408 
409 typedef struct _LoadContext LoadContext;
410 
411 struct _LoadContext {
412         png_structp png_read_ptr;
413         png_infop   png_info_ptr;
414 
415         GdkPixbufModuleSizeFunc size_func;
416         GdkPixbufModulePreparedFunc prepare_func;
417         GdkPixbufModuleUpdatedFunc update_func;
418         gpointer notify_user_data;
419 
420         GdkPixbuf* pixbuf;
421 
422         /* row number of first row seen, or -1 if none yet seen */
423 
424         gint first_row_seen_in_chunk;
425 
426         /* pass number for the first row seen */
427 
428         gint first_pass_seen_in_chunk;
429 
430         /* row number of last row seen */
431         gint last_row_seen_in_chunk;
432 
433         gint last_pass_seen_in_chunk;
434 
435         /* highest row number seen */
436         gint max_row_seen_in_chunk;
437 
438         guint fatal_error_occurred : 1;
439 
440         GError **error;
441 };
442 
443 static gpointer
gdk_pixbuf__png_image_begin_load(GdkPixbufModuleSizeFunc size_func,GdkPixbufModulePreparedFunc prepare_func,GdkPixbufModuleUpdatedFunc update_func,gpointer user_data,GError ** error)444 gdk_pixbuf__png_image_begin_load (GdkPixbufModuleSizeFunc size_func,
445                                   GdkPixbufModulePreparedFunc prepare_func,
446 				  GdkPixbufModuleUpdatedFunc update_func,
447 				  gpointer user_data,
448                                   GError **error)
449 {
450         LoadContext* lc;
451 
452         lc = g_new0(LoadContext, 1);
453 
454         lc->fatal_error_occurred = FALSE;
455 
456         lc->size_func = size_func;
457         lc->prepare_func = prepare_func;
458         lc->update_func = update_func;
459         lc->notify_user_data = user_data;
460 
461         lc->first_row_seen_in_chunk = -1;
462         lc->last_row_seen_in_chunk = -1;
463         lc->first_pass_seen_in_chunk = -1;
464         lc->last_pass_seen_in_chunk = -1;
465         lc->max_row_seen_in_chunk = -1;
466         lc->error = error;
467 
468         /* Create the main PNG context struct */
469 
470 #ifdef PNG_USER_MEM_SUPPORTED
471         lc->png_read_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING,
472                                                      lc, /* error/warning callback data */
473                                                      png_error_callback,
474                                                      png_warning_callback,
475                                                      NULL,
476                                                      png_malloc_callback,
477                                                      png_free_callback);
478 #else
479         lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
480                                                   lc, /* error/warning callback data */
481                                                   png_error_callback,
482                                                   png_warning_callback);
483 #endif
484         if (lc->png_read_ptr == NULL) {
485                 g_free(lc);
486 
487                 /* A failure here isn't supposed to call the error
488                  * callback, but it doesn't hurt to be careful.
489                  */
490                 if (error && *error == NULL) {
491                         g_set_error_literal (error,
492                                              GDK_PIXBUF_ERROR,
493                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
494                                              _("Couldn’t allocate memory for loading PNG"));
495                 }
496 
497                 return NULL;
498         }
499 
500         /* Create the auxiliary context struct */
501 
502         lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
503 
504         if (lc->png_info_ptr == NULL) {
505                 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
506                 g_free(lc);
507 
508                 /* A failure here isn't supposed to call the error
509                  * callback, but it doesn't hurt to be careful.
510                  */
511                 if (error && *error == NULL) {
512                         g_set_error_literal (error,
513                                              GDK_PIXBUF_ERROR,
514                                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
515                                              _("Couldn’t allocate memory for loading PNG"));
516                 }
517 
518                 return NULL;
519         }
520 
521         if (setjmp (png_jmpbuf(lc->png_read_ptr))) {
522                 png_destroy_read_struct(&lc->png_read_ptr, &lc->png_info_ptr, NULL);
523                 g_free(lc);
524                 /* error callback should have set the error */
525                 return NULL;
526         }
527 
528         png_set_progressive_read_fn(lc->png_read_ptr,
529                                     lc, /* callback data */
530                                     png_info_callback,
531                                     png_row_callback,
532                                     png_end_callback);
533 
534 
535         /* We don't want to keep modifying error after returning here,
536          * it may no longer be valid.
537          */
538         lc->error = NULL;
539 
540         return lc;
541 }
542 
543 static gboolean
gdk_pixbuf__png_image_stop_load(gpointer context,GError ** error)544 gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
545 {
546         LoadContext* lc = context;
547         gboolean retval = TRUE;
548 
549         g_return_val_if_fail(lc != NULL, TRUE);
550 
551         /* FIXME this thing needs to report errors if
552          * we have unused image data
553          */
554 
555         if (lc->pixbuf)
556                 g_object_unref (lc->pixbuf);
557         else {
558                 g_set_error_literal (error, GDK_PIXBUF_ERROR,
559                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
560                                      _("Premature end-of-file encountered"));
561                 retval = FALSE;
562 	}
563 
564         png_destroy_read_struct(&lc->png_read_ptr, &lc->png_info_ptr, NULL);
565         g_free(lc);
566 
567         return retval;
568 }
569 
570 static gboolean
gdk_pixbuf__png_image_load_increment(gpointer context,const guchar * buf,guint size,GError ** error)571 gdk_pixbuf__png_image_load_increment(gpointer context,
572                                      const guchar *buf, guint size,
573                                      GError **error)
574 {
575         LoadContext* lc = context;
576 
577         g_return_val_if_fail(lc != NULL, FALSE);
578 
579         /* reset */
580         lc->first_row_seen_in_chunk = -1;
581         lc->last_row_seen_in_chunk = -1;
582         lc->first_pass_seen_in_chunk = -1;
583         lc->last_pass_seen_in_chunk = -1;
584         lc->max_row_seen_in_chunk = -1;
585         lc->error = error;
586 
587         /* Invokes our callbacks as needed */
588 	if (setjmp (png_jmpbuf(lc->png_read_ptr))) {
589                 lc->error = NULL;
590 		return FALSE;
591 	} else {
592 		png_process_data(lc->png_read_ptr, lc->png_info_ptr,
593                                  (guchar*) buf, size);
594 	}
595 
596         if (lc->fatal_error_occurred) {
597                 lc->error = NULL;
598                 return FALSE;
599         } else {
600                 if (lc->first_row_seen_in_chunk >= 0 && lc->update_func) {
601                         gint width = gdk_pixbuf_get_width (lc->pixbuf);
602 
603                         /* We saw at least one row */
604                         gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
605 
606                         g_assert(pass_diff >= 0);
607 
608                         if (pass_diff == 0) {
609                                 /* start and end row were in the same pass */
610                                 (lc->update_func)(lc->pixbuf, 0,
611                                                   lc->first_row_seen_in_chunk,
612                                                   width,
613                                                   (lc->last_row_seen_in_chunk -
614                                                    lc->first_row_seen_in_chunk) + 1,
615 						  lc->notify_user_data);
616                         } else if (pass_diff == 1) {
617                                 /* We have from the first row seen to
618                                    the end of the image (max row
619                                    seen), then from the top of the
620                                    image to the last row seen */
621                                 /* first row to end */
622                                 (lc->update_func)(lc->pixbuf, 0,
623                                                   lc->first_row_seen_in_chunk,
624                                                   width,
625                                                   (lc->max_row_seen_in_chunk -
626                                                    lc->first_row_seen_in_chunk) + 1,
627 						  lc->notify_user_data);
628                                 /* top to last row */
629                                 (lc->update_func)(lc->pixbuf,
630                                                   0, 0,
631                                                   width,
632                                                   lc->last_row_seen_in_chunk + 1,
633 						  lc->notify_user_data);
634                         } else {
635                                 /* We made at least one entire pass, so update the
636                                    whole image */
637                                 (lc->update_func)(lc->pixbuf,
638                                                   0, 0,
639                                                   width,
640                                                   lc->max_row_seen_in_chunk + 1,
641 						  lc->notify_user_data);
642                         }
643                 }
644 
645                 lc->error = NULL;
646 
647                 return TRUE;
648         }
649 }
650 
651 /* Called at the start of the progressive load, once we have image info */
652 static void
png_info_callback(png_structp png_read_ptr,png_infop png_info_ptr)653 png_info_callback   (png_structp png_read_ptr,
654                      png_infop   png_info_ptr)
655 {
656         LoadContext* lc;
657         png_uint_32 width, height;
658         png_textp png_text_ptr;
659         int i, num_texts;
660         int color_type;
661         gboolean have_alpha = FALSE;
662         gchar *icc_profile_base64;
663         const gchar *icc_profile_title;
664         const gchar *icc_profile;
665         png_uint_32 icc_profile_size;
666         png_uint_32 x_resolution;
667         png_uint_32 y_resolution;
668         int unit_type;
669         gchar *density_str;
670         guint32 retval;
671         gint compression_type;
672 
673         lc = png_get_progressive_ptr(png_read_ptr);
674 
675         if (lc->fatal_error_occurred)
676                 return;
677 
678         if (!setup_png_transformations(lc->png_read_ptr,
679                                        lc->png_info_ptr,
680                                        lc->error,
681                                        &width, &height, &color_type)) {
682                 lc->fatal_error_occurred = TRUE;
683                 return;
684         }
685 
686         /* If we have alpha, set a flag */
687         if (color_type & PNG_COLOR_MASK_ALPHA)
688                 have_alpha = TRUE;
689 
690         if (lc->size_func) {
691                 gint w = width;
692                 gint h = height;
693                 (* lc->size_func) (&w, &h, lc->notify_user_data);
694 
695                 if (w == 0 || h == 0) {
696                         lc->fatal_error_occurred = TRUE;
697                         g_set_error_literal (lc->error,
698                                              GDK_PIXBUF_ERROR,
699                                              GDK_PIXBUF_ERROR_FAILED,
700                                              _("Transformed PNG has zero width or height."));
701                         return;
702                 }
703         }
704 
705         lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
706 
707         if (lc->pixbuf == NULL) {
708                 /* Failed to allocate memory */
709                 lc->fatal_error_occurred = TRUE;
710                 g_set_error (lc->error,
711                              GDK_PIXBUF_ERROR,
712                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
713                              _("Insufficient memory to store a %lu by %lu image; try exiting some applications to reduce memory usage"),
714                              (gulong) width, (gulong) height);
715                 return;
716         }
717 
718         gdk_pixbuf_fill (lc->pixbuf, DEFAULT_FILL_COLOR);
719 
720         /* Extract text chunks and attach them as pixbuf options */
721 
722         if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
723                 for (i = 0; i < num_texts; i++) {
724                         gchar *key, *value;
725 
726                         if (png_text_to_pixbuf_option (png_text_ptr[i],
727                                                        &key, &value)) {
728                                 gdk_pixbuf_set_option (lc->pixbuf, key, value);
729                                 g_free (key);
730                                 g_free (value);
731                         }
732                 }
733         }
734 
735 #if defined(PNG_cHRM_SUPPORTED)
736         /* Extract embedded ICC profile */
737         retval = png_get_iCCP (png_read_ptr, png_info_ptr,
738                                (png_charpp) &icc_profile_title, &compression_type,
739                                (png_bytepp) &icc_profile, &icc_profile_size);
740         if (retval != 0) {
741                 icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size);
742                 gdk_pixbuf_set_option (lc->pixbuf, "icc-profile", icc_profile_base64);
743                 g_free (icc_profile_base64);
744         }
745 #endif
746 
747 #ifdef PNG_pHYs_SUPPORTED
748         retval = png_get_pHYs (png_read_ptr, png_info_ptr, &x_resolution, &y_resolution, &unit_type);
749         if (retval != 0 && unit_type == PNG_RESOLUTION_METER) {
750                 density_str = g_strdup_printf ("%d", DPM_TO_DPI (x_resolution));
751                 gdk_pixbuf_set_option (lc->pixbuf, "x-dpi", density_str);
752                 g_free (density_str);
753                 density_str = g_strdup_printf ("%d", DPM_TO_DPI (y_resolution));
754                 gdk_pixbuf_set_option (lc->pixbuf, "y-dpi", density_str);
755                 g_free (density_str);
756         }
757 #endif
758 
759         /* Notify the client that we are ready to go */
760 
761         if (lc->prepare_func)
762                 (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
763 
764         return;
765 }
766 
767 /* Called for each row; note that you will get duplicate row numbers
768    for interlaced PNGs */
769 static void
png_row_callback(png_structp png_read_ptr,png_bytep new_row,png_uint_32 row_num,int pass_num)770 png_row_callback   (png_structp png_read_ptr,
771                     png_bytep   new_row,
772                     png_uint_32 row_num,
773                     int pass_num)
774 {
775         LoadContext* lc;
776         guchar* old_row = NULL;
777         gsize rowstride;
778 
779         lc = png_get_progressive_ptr(png_read_ptr);
780 
781         if (lc->fatal_error_occurred)
782                 return;
783 
784         if (row_num >= gdk_pixbuf_get_height (lc->pixbuf)) {
785                 lc->fatal_error_occurred = TRUE;
786                 g_set_error_literal (lc->error,
787                                      GDK_PIXBUF_ERROR,
788                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
789                                      _("Fatal error reading PNG image file"));
790                 return;
791         }
792 
793         if (lc->first_row_seen_in_chunk < 0) {
794                 lc->first_row_seen_in_chunk = row_num;
795                 lc->first_pass_seen_in_chunk = pass_num;
796         }
797 
798         lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
799         lc->last_row_seen_in_chunk = row_num;
800         lc->last_pass_seen_in_chunk = pass_num;
801 
802         rowstride = gdk_pixbuf_get_rowstride (lc->pixbuf);
803         old_row = gdk_pixbuf_get_pixels (lc->pixbuf) + (row_num * rowstride);
804 
805         png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
806 }
807 
808 /* Called after reading the entire image */
809 static void
png_end_callback(png_structp png_read_ptr,png_infop png_info_ptr)810 png_end_callback   (png_structp png_read_ptr,
811                     png_infop   png_info_ptr)
812 {
813         LoadContext* lc;
814 
815         lc = png_get_progressive_ptr(png_read_ptr);
816 
817         if (lc->fatal_error_occurred)
818                 return;
819 }
820 
821 static void
png_error_callback(png_structp png_read_ptr,png_const_charp error_msg)822 png_error_callback(png_structp png_read_ptr,
823                    png_const_charp error_msg)
824 {
825         LoadContext* lc;
826 
827         lc = png_get_error_ptr(png_read_ptr);
828 
829         lc->fatal_error_occurred = TRUE;
830 
831         /* I don't trust libpng to call the error callback only once,
832          * so check for already-set error
833          */
834         if (lc->error && *lc->error == NULL) {
835                 g_set_error (lc->error,
836                              GDK_PIXBUF_ERROR,
837                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
838                              _("Fatal error reading PNG image file: %s"),
839                              error_msg);
840         }
841 
842         longjmp (png_jmpbuf(png_read_ptr), 1);
843 }
844 
845 static void
png_warning_callback(png_structp png_read_ptr,png_const_charp warning_msg)846 png_warning_callback (png_structp png_read_ptr,
847                       png_const_charp warning_msg)
848 {
849         /* Don't print anything; we should not be dumping junk to
850          * stderr, since that may be bad for some apps. If it's
851          * important enough to display, we need to add a GError
852          * **warning return location wherever we have an error return
853          * location.
854          */
855 }
856 
857 
858 /* Save */
859 
860 typedef struct {
861         GdkPixbufSaveFunc save_func;
862         gpointer user_data;
863         GError **error;
864 } SaveToFunctionIoPtr;
865 
866 static void
png_save_to_callback_write_func(png_structp png_ptr,png_bytep data,png_size_t length)867 png_save_to_callback_write_func (png_structp png_ptr,
868                                  png_bytep   data,
869                                  png_size_t  length)
870 {
871         SaveToFunctionIoPtr *ioptr = png_get_io_ptr (png_ptr);
872 
873         if (!ioptr->save_func ((gchar *)data, length, ioptr->error, ioptr->user_data)) {
874                 /* If save_func has already set an error, which it
875                    should have done, this won't overwrite it. */
876                 png_error (png_ptr, "write function failed");
877         }
878 }
879 
880 static void
png_save_to_callback_flush_func(png_structp png_ptr)881 png_save_to_callback_flush_func (png_structp png_ptr)
882 {
883         ;
884 }
885 
real_save_png(GdkPixbuf * pixbuf,gchar ** keys,gchar ** values,GError ** error,gboolean to_callback,FILE * f,GdkPixbufSaveFunc save_func,gpointer user_data)886 static gboolean real_save_png (GdkPixbuf        *pixbuf,
887                                gchar           **keys,
888                                gchar           **values,
889                                GError          **error,
890                                gboolean          to_callback,
891                                FILE             *f,
892                                GdkPixbufSaveFunc save_func,
893                                gpointer          user_data)
894 {
895        png_structp png_ptr = NULL;
896        png_infop info_ptr;
897        png_textp text_ptr = NULL;
898        guchar *ptr;
899        guchar *pixels;
900        int y;
901        int i;
902        png_bytep row_ptr;
903        png_color_8 sig_bit;
904        int w, h, rowstride;
905        int has_alpha;
906        int bpc;
907        int num_keys;
908        int compression = -1;
909        int x_density = 0;
910        int y_density = 0;
911        gboolean success = TRUE;
912        guchar *icc_profile = NULL;
913        gsize icc_profile_size = 0;
914        SaveToFunctionIoPtr to_callback_ioptr;
915 
916        num_keys = 0;
917 
918        if (keys && *keys) {
919                gchar **kiter = keys;
920                gchar **viter = values;
921 
922                while (*kiter) {
923                        if (strncmp (*kiter, "tEXt::", 6) == 0) {
924                                gchar  *key = *kiter + 6;
925                                int     len = strlen (key);
926                                if (len < 1 || len > 79) {
927                                        g_set_error_literal (error,
928                                                             GDK_PIXBUF_ERROR,
929                                                             GDK_PIXBUF_ERROR_BAD_OPTION,
930                                                             _("Keys for PNG text chunks must have at least 1 and at most 79 characters."));
931                                        success = FALSE;
932                                        goto cleanup;
933                                }
934                                for (i = 0; i < len; i++) {
935                                        if ((guchar) key[i] > 127) {
936                                                g_set_error_literal (error,
937                                                                     GDK_PIXBUF_ERROR,
938                                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
939                                                                     _("Keys for PNG text chunks must be ASCII characters."));
940                                                success = FALSE;
941                                                goto cleanup;
942                                        }
943                                }
944                                num_keys++;
945                        } else if (strcmp (*kiter, "icc-profile") == 0) {
946                                /* decode from base64 */
947                                icc_profile = g_base64_decode (*viter, &icc_profile_size);
948                                if (icc_profile_size < 127) {
949                                        /* This is a user-visible error */
950                                        g_set_error (error,
951                                                     GDK_PIXBUF_ERROR,
952                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
953                                                     _("Color profile has invalid length %d."),
954                                                     (gint)icc_profile_size);
955                                        success = FALSE;
956                                        goto cleanup;
957                                }
958                        } else if (strcmp (*kiter, "compression") == 0) {
959                                char *endptr = NULL;
960                                compression = strtol (*viter, &endptr, 10);
961 
962                                if (endptr == *viter) {
963                                        g_set_error (error,
964                                                     GDK_PIXBUF_ERROR,
965                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
966                                                     _("PNG compression level must be a value between 0 and 9; value “%s” could not be parsed."),
967                                                     *viter);
968                                        success = FALSE;
969                                        goto cleanup;
970                                }
971                                if (compression < 0 || compression > 9) {
972                                        /* This is a user-visible error;
973                                         * lets people skip the range-checking
974                                         * in their app.
975                                         */
976                                        g_set_error (error,
977                                                     GDK_PIXBUF_ERROR,
978                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
979                                                     _("PNG compression level must be a value between 0 and 9; value “%d” is not allowed."),
980                                                     compression);
981                                        success = FALSE;
982                                        goto cleanup;
983                                }
984                        } else if (strcmp (*kiter, "x-dpi") == 0) {
985                                char *endptr = NULL;
986                                x_density = strtol (*viter, &endptr, 10);
987                                if (endptr == *viter)
988                                        x_density = -1;
989 
990                                if (x_density <= 0) {
991                                        /* This is a user-visible error;
992                                         * lets people skip the range-checking
993                                         * in their app.
994                                         */
995                                        g_set_error (error,
996                                                     GDK_PIXBUF_ERROR,
997                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
998                                                     _("PNG x-dpi must be greater than zero; value “%s” is not allowed."),
999                                                     *viter);
1000 
1001                                        success = FALSE;
1002                                        goto cleanup;
1003                                }
1004                        } else if (strcmp (*kiter, "y-dpi") == 0) {
1005                                char *endptr = NULL;
1006                                y_density = strtol (*viter, &endptr, 10);
1007                                if (endptr == *viter)
1008                                        y_density = -1;
1009 
1010                                if (y_density <= 0) {
1011                                        /* This is a user-visible error;
1012                                         * lets people skip the range-checking
1013                                         * in their app.
1014                                         */
1015                                        g_set_error (error,
1016                                                     GDK_PIXBUF_ERROR,
1017                                                     GDK_PIXBUF_ERROR_BAD_OPTION,
1018                                                     _("PNG y-dpi must be greater than zero; value “%s” is not allowed."),
1019                                                     *viter);
1020 
1021                                        success = FALSE;
1022                                        goto cleanup;
1023                                }
1024                        } else {
1025                                g_warning ("Unrecognized parameter (%s) passed to PNG saver.", *kiter);
1026                        }
1027 
1028                        ++kiter;
1029                        ++viter;
1030                }
1031        }
1032 
1033        if (num_keys > 0) {
1034                gchar **kiter = keys;
1035                gchar **viter = values;
1036 
1037                text_ptr = g_new0 (png_text, num_keys);
1038                for (i = 0; i < num_keys; i++) {
1039                        if (strncmp (*kiter, "tEXt::", 6) != 0) {
1040                                 kiter++;
1041                                 viter++;
1042                        }
1043 
1044                        text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
1045                        text_ptr[i].key  = *kiter + 6;
1046                        text_ptr[i].text = g_convert (*viter, -1,
1047                                                      "ISO-8859-1", "UTF-8",
1048                                                      NULL, &text_ptr[i].text_length,
1049                                                      NULL);
1050 
1051 #ifdef PNG_iTXt_SUPPORTED
1052                        if (!text_ptr[i].text) {
1053                                text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE;
1054                                text_ptr[i].text = g_strdup (*viter);
1055                                text_ptr[i].text_length = 0;
1056                                text_ptr[i].itxt_length = strlen (text_ptr[i].text);
1057                                text_ptr[i].lang = NULL;
1058                                text_ptr[i].lang_key = NULL;
1059                        }
1060 #endif
1061 
1062                        if (!text_ptr[i].text) {
1063                                gint j;
1064                                g_set_error (error,
1065                                             GDK_PIXBUF_ERROR,
1066                                             GDK_PIXBUF_ERROR_BAD_OPTION,
1067                                             _("Value for PNG text chunk %s cannot be converted to ISO-8859-1 encoding."), *kiter + 6);
1068                                for (j = 0; j < i; j++)
1069                                        g_free (text_ptr[j].text);
1070                                g_free (text_ptr);
1071                                return FALSE;
1072                        }
1073 
1074                         kiter++;
1075                         viter++;
1076                }
1077        }
1078 
1079        bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
1080        w = gdk_pixbuf_get_width (pixbuf);
1081        h = gdk_pixbuf_get_height (pixbuf);
1082        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
1083        has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
1084        pixels = gdk_pixbuf_get_pixels (pixbuf);
1085 
1086        /* Guaranteed by the caller. */
1087        g_assert (w >= 0);
1088        g_assert (h >= 0);
1089        g_assert (rowstride >= 0);
1090 
1091        png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
1092                                           error,
1093                                           png_simple_error_callback,
1094                                           png_simple_warning_callback);
1095        if (png_ptr == NULL) {
1096 	       success = FALSE;
1097 	       goto cleanup;
1098        }
1099 
1100        info_ptr = png_create_info_struct (png_ptr);
1101        if (info_ptr == NULL) {
1102 	       success = FALSE;
1103 	       goto cleanup;
1104        }
1105        if (setjmp (png_jmpbuf(png_ptr))) {
1106 	       success = FALSE;
1107 	       goto cleanup;
1108        }
1109 
1110        if (num_keys > 0) {
1111                png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
1112        }
1113 
1114        if (to_callback) {
1115                to_callback_ioptr.save_func = save_func;
1116                to_callback_ioptr.user_data = user_data;
1117                to_callback_ioptr.error = error;
1118                png_set_write_fn (png_ptr, &to_callback_ioptr,
1119                                  png_save_to_callback_write_func,
1120                                  png_save_to_callback_flush_func);
1121        } else {
1122                png_init_io (png_ptr, f);
1123        }
1124 
1125        if (compression >= 0)
1126                png_set_compression_level (png_ptr, compression);
1127 
1128 #ifdef PNG_pHYs_SUPPORTED
1129        if (x_density > 0 && y_density > 0)
1130                png_set_pHYs (png_ptr, info_ptr, DPI_TO_DPM (x_density), DPI_TO_DPM (y_density), PNG_RESOLUTION_METER);
1131 #endif
1132 
1133 #if defined(PNG_iCCP_SUPPORTED)
1134         /* the proper ICC profile title is encoded in the profile */
1135         if (icc_profile != NULL) {
1136                 png_set_iCCP (png_ptr, info_ptr,
1137                               "ICC profile", PNG_COMPRESSION_TYPE_BASE,
1138                               (png_bytep) icc_profile, icc_profile_size);
1139         }
1140 #endif
1141 
1142        if (has_alpha) {
1143                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
1144                              PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
1145                              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1146        } else {
1147                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
1148                              PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1149                              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1150        }
1151        /* Note bpc is always 8 */
1152        sig_bit.red = bpc;
1153        sig_bit.green = bpc;
1154        sig_bit.blue = bpc;
1155        sig_bit.alpha = bpc;
1156        png_set_sBIT (png_ptr, info_ptr, &sig_bit);
1157        png_write_info (png_ptr, info_ptr);
1158        png_set_packing (png_ptr);
1159 
1160        for (y = 0, ptr = pixels; y < h; y++, ptr += rowstride) {
1161                row_ptr = (png_bytep)ptr;
1162                png_write_rows (png_ptr, &row_ptr, 1);
1163        }
1164 
1165        png_write_end (png_ptr, info_ptr);
1166 
1167 cleanup:
1168         if (png_ptr != NULL)
1169                 png_destroy_write_struct (&png_ptr, &info_ptr);
1170 
1171         g_free (icc_profile);
1172 
1173         if (text_ptr != NULL) {
1174                 for (i = 0; i < num_keys; i++)
1175                         g_free (text_ptr[i].text);
1176                 g_free (text_ptr);
1177         }
1178 
1179        return success;
1180 }
1181 
1182 static gboolean
gdk_pixbuf__png_image_save(FILE * f,GdkPixbuf * pixbuf,gchar ** keys,gchar ** values,GError ** error)1183 gdk_pixbuf__png_image_save (FILE          *f,
1184                             GdkPixbuf     *pixbuf,
1185                             gchar        **keys,
1186                             gchar        **values,
1187                             GError       **error)
1188 {
1189         return real_save_png (pixbuf, keys, values, error,
1190                               FALSE, f, NULL, NULL);
1191 }
1192 
1193 static gboolean
gdk_pixbuf__png_image_save_to_callback(GdkPixbufSaveFunc save_func,gpointer user_data,GdkPixbuf * pixbuf,gchar ** keys,gchar ** values,GError ** error)1194 gdk_pixbuf__png_image_save_to_callback (GdkPixbufSaveFunc   save_func,
1195                                         gpointer            user_data,
1196                                         GdkPixbuf          *pixbuf,
1197                                         gchar             **keys,
1198                                         gchar             **values,
1199                                         GError            **error)
1200 {
1201         return real_save_png (pixbuf, keys, values, error,
1202                               TRUE, NULL, save_func, user_data);
1203 }
1204 
1205 static gboolean
gdk_pixbuf__png_is_save_option_supported(const gchar * option_key)1206 gdk_pixbuf__png_is_save_option_supported (const gchar *option_key)
1207 {
1208         if (g_strcmp0 (option_key, "compression") == 0 ||
1209             g_strcmp0 (option_key, "icc-profile") == 0 ||
1210             g_strcmp0 (option_key, "x-dpi") == 0 ||
1211             g_strcmp0 (option_key, "y-dpi") == 0 ||
1212             strncmp (option_key, "tEXt::", 6) == 0)
1213                 return TRUE;
1214 
1215         return FALSE;
1216 }
1217 
1218 #ifndef INCLUDE_png
1219 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1220 #else
1221 #define MODULE_ENTRY(function) void _gdk_pixbuf__png_ ## function
1222 #endif
1223 
MODULE_ENTRY(fill_vtable)1224 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1225 {
1226         module->load = gdk_pixbuf__png_image_load;
1227         module->begin_load = gdk_pixbuf__png_image_begin_load;
1228         module->stop_load = gdk_pixbuf__png_image_stop_load;
1229         module->load_increment = gdk_pixbuf__png_image_load_increment;
1230         module->save = gdk_pixbuf__png_image_save;
1231         module->save_to_callback = gdk_pixbuf__png_image_save_to_callback;
1232         module->is_save_option_supported = gdk_pixbuf__png_is_save_option_supported;
1233 }
1234 
MODULE_ENTRY(fill_info)1235 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1236 {
1237         static const GdkPixbufModulePattern signature[] = {
1238                 { "\x89PNG\r\n\x1a\x0a", NULL, 100 },
1239                 { NULL, NULL, 0 }
1240         };
1241 	static const gchar *mime_types[] = {
1242 		"image/png",
1243 		NULL
1244 	};
1245 	static const gchar *extensions[] = {
1246 		"png",
1247 		NULL
1248 	};
1249 
1250 	info->name = "png";
1251         info->signature = (GdkPixbufModulePattern *) signature;
1252 	info->description = NC_("image format", "PNG");
1253 	info->mime_types = (gchar **) mime_types;
1254 	info->extensions = (gchar **) extensions;
1255 	info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
1256 	info->license = "LGPL";
1257 }
1258