1 /* GdkPixbuf library - JPEG 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 Library 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 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library 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.
28 #include "gdk-pixbuf.h"
29 #include "gdk-pixbuf-io.h"
34 setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
35 gboolean *fatal_error_occurred,
36 png_uint_32* width_p, png_uint_32* height_p,
39 png_uint_32 width, height;
40 int bit_depth, color_type, interlace_type, compression_type, filter_type;
43 /* Get the image info */
45 png_get_IHDR (png_read_ptr, png_info_ptr,
53 /* set_expand() basically needs to be called unless
54 we are already in RGB/RGBA mode
56 if (color_type == PNG_COLOR_TYPE_PALETTE &&
59 /* Convert indexed images to RGB */
60 png_set_expand (png_read_ptr);
62 } else if (color_type == PNG_COLOR_TYPE_GRAY &&
65 /* Convert grayscale to RGB */
66 png_set_expand (png_read_ptr);
68 } else if (png_get_valid (png_read_ptr,
69 png_info_ptr, PNG_INFO_tRNS)) {
71 /* If we have transparency header, convert it to alpha
73 png_set_expand(png_read_ptr);
75 } else if (bit_depth < 8) {
77 /* If we have < 8 scale it up to 8 */
78 png_set_expand(png_read_ptr);
81 /* Conceivably, png_set_packing() is a better idea;
82 * God only knows how libpng works
86 /* If we are 16-bit, convert to 8-bit */
87 if (bit_depth == 16) {
88 png_set_strip_16(png_read_ptr);
91 /* If gray scale, convert to RGB */
92 if (color_type == PNG_COLOR_TYPE_GRAY ||
93 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
94 png_set_gray_to_rgb(png_read_ptr);
97 /* If interlaced, handle that */
98 if (interlace_type != PNG_INTERLACE_NONE) {
99 png_set_interlace_handling(png_read_ptr);
102 /* Update the info the reflect our transformations */
103 png_read_update_info(png_read_ptr, png_info_ptr);
105 png_get_IHDR (png_read_ptr, png_info_ptr,
115 *color_type_p = color_type;
117 #ifndef G_DISABLE_CHECKS
118 /* Check that the new info is what we want */
120 if (bit_depth != 8) {
121 g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
122 *fatal_error_occurred = TRUE;
126 if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
127 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
128 g_warning("Transformed PNG not RGB or RGBA.");
129 *fatal_error_occurred = TRUE;
133 channels = png_get_channels(png_read_ptr, png_info_ptr);
134 if ( ! (channels == 3 || channels == 4) ) {
135 g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
136 *fatal_error_occurred = TRUE;
142 /* Destroy notification function for the libart pixbuf */
144 free_buffer (gpointer user_data, gpointer data)
149 /* Shared library entry point */
154 png_infop info_ptr, end_info;
155 gboolean failed = FALSE;
156 gint i, depth, ctype, inttype, passes, bpp;
161 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
165 info_ptr = png_create_info_struct (png_ptr);
167 png_destroy_read_struct (&png_ptr, NULL, NULL);
171 end_info = png_create_info_struct (png_ptr);
173 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
177 if (setjmp (png_ptr->jmpbuf)) {
178 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
182 png_init_io (png_ptr, f);
183 png_read_info (png_ptr, info_ptr);
185 setup_png_transformations(png_ptr, info_ptr, &failed, &w, &h, &ctype);
188 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
192 if (ctype & PNG_COLOR_MASK_ALPHA)
197 pixels = malloc (w * h * bpp);
199 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
203 rows = g_new (png_bytep, h);
205 for (i = 0; i < h; i++)
206 rows[i] = pixels + i * w * bpp;
208 png_read_image (png_ptr, rows);
209 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
212 if (ctype & PNG_COLOR_MASK_ALPHA)
213 return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, TRUE,
217 return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, FALSE,
222 /* These avoid the setjmp()/longjmp() crap in libpng */
223 static void png_error_callback (png_structp png_read_ptr,
224 png_const_charp error_msg);
226 static void png_warning_callback(png_structp png_read_ptr,
227 png_const_charp warning_msg);
229 /* Called at the start of the progressive load */
230 static void png_info_callback (png_structp png_read_ptr,
231 png_infop png_info_ptr);
233 /* Called for each row; note that you will get duplicate row numbers
234 for interlaced PNGs */
235 static void png_row_callback (png_structp png_read_ptr,
240 /* Called after reading the entire image */
241 static void png_end_callback (png_structp png_read_ptr,
242 png_infop png_info_ptr);
244 typedef struct _LoadContext LoadContext;
246 struct _LoadContext {
247 png_structp png_read_ptr;
248 png_infop png_info_ptr;
250 ModulePreparedNotifyFunc notify_func;
251 gpointer notify_user_data;
255 guint fatal_error_occurred : 1;
260 image_begin_load (ModulePreparedNotifyFunc func, gpointer user_data)
264 lc = g_new0(LoadContext, 1);
266 lc->fatal_error_occurred = FALSE;
268 lc->notify_func = func;
269 lc->notify_user_data = user_data;
271 /* Create the main PNG context struct */
273 lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
274 lc, /* error/warning callback data */
276 png_warning_callback);
278 if (lc->png_read_ptr == NULL) {
283 /* Create the auxiliary context struct */
285 lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
287 if (lc->png_info_ptr == NULL) {
288 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
293 png_set_progressive_read_fn(lc->png_read_ptr,
294 lc, /* callback data */
304 image_stop_load (gpointer context)
306 LoadContext* lc = context;
308 g_return_if_fail(lc != NULL);
310 gdk_pixbuf_unref(lc->pixbuf);
312 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
317 image_load_increment(gpointer context, guchar *buf, guint size)
319 LoadContext* lc = context;
321 g_return_val_if_fail(lc != NULL, FALSE);
323 /* Invokes our callbacks as needed */
324 png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
326 if (lc->fatal_error_occurred)
332 /* Called at the start of the progressive load, once we have image info */
334 png_info_callback (png_structp png_read_ptr,
335 png_infop png_info_ptr)
338 png_uint_32 width, height;
340 gboolean have_alpha = FALSE;
341 gboolean failed = FALSE;
343 lc = png_get_progressive_ptr(png_read_ptr);
345 if (lc->fatal_error_occurred)
349 setup_png_transformations(lc->png_read_ptr,
352 &width, &height, &color_type);
355 lc->fatal_error_occurred = TRUE;
359 /* If we have alpha, set a flag */
360 if (color_type & PNG_COLOR_MASK_ALPHA)
363 lc->pixbuf = gdk_pixbuf_new(have_alpha, width, height);
365 if (lc->pixbuf == NULL) {
366 /* Failed to allocate memory */
367 lc->fatal_error_occurred = TRUE;
371 /* Notify the client that we are ready to go */
374 (* lc->notify_func) (lc->pixbuf, lc->notify_user_data);
379 /* Called for each row; note that you will get duplicate row numbers
380 for interlaced PNGs */
382 png_row_callback (png_structp png_read_ptr,
388 guchar* old_row = NULL;
390 lc = png_get_progressive_ptr(png_read_ptr);
392 if (lc->fatal_error_occurred)
395 old_row = lc->pixbuf->art_pixbuf->pixels + (row_num * lc->pixbuf->art_pixbuf->rowstride);
397 png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
400 /* Called after reading the entire image */
402 png_end_callback (png_structp png_read_ptr,
403 png_infop png_info_ptr)
407 lc = png_get_progressive_ptr(png_read_ptr);
409 if (lc->fatal_error_occurred)
412 /* Doesn't do anything for now */
417 png_error_callback(png_structp png_read_ptr,
418 png_const_charp error_msg)
422 lc = png_get_error_ptr(png_read_ptr);
424 lc->fatal_error_occurred = TRUE;
426 fprintf(stderr, "Fatal error loading PNG: %s\n", error_msg);
430 png_warning_callback(png_structp png_read_ptr,
431 png_const_charp warning_msg)
435 lc = png_get_error_ptr(png_read_ptr);
437 fprintf(stderr, "Warning loading PNG: %s\n", warning_msg);