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"
33 /* Destroy notification function for the libart pixbuf */
35 free_buffer (gpointer user_data, gpointer data)
40 /* Shared library entry point */
45 png_infop info_ptr, end_info;
46 gint i, depth, ctype, inttype, passes, bpp;
51 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
55 info_ptr = png_create_info_struct (png_ptr);
57 png_destroy_read_struct (&png_ptr, NULL, NULL);
61 end_info = png_create_info_struct (png_ptr);
63 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
67 if (setjmp (png_ptr->jmpbuf)) {
68 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
72 png_init_io (png_ptr, f);
73 png_read_info (png_ptr, info_ptr);
74 png_get_IHDR (png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype, NULL, NULL);
76 /* Ok, we want to work with 24 bit images.
77 * However, PNG can vary depth per channel.
78 * So, we use the png_set_expand function to expand
79 * everything into a format libart expects.
80 * We also use png_set_strip_16 to reduce down to 8 bit/chan.
82 if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
83 png_set_expand (png_ptr);
85 if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
86 png_set_expand (png_ptr);
88 if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
89 png_set_expand (png_ptr);
90 g_warning ("FIXME: We are going to crash");
94 png_set_strip_16 (png_ptr);
96 /* We also have png "packing" bits into bytes if < 8 */
98 png_set_packing (png_ptr);
100 /* Lastly, if the PNG is greyscale, convert to RGB */
101 if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
102 png_set_gray_to_rgb (png_ptr);
104 /* ...and if we're interlaced... */
105 passes = png_set_interlace_handling (png_ptr);
107 png_read_update_info (png_ptr, info_ptr);
109 if (ctype & PNG_COLOR_MASK_ALPHA)
114 pixels = malloc (w * h * bpp);
116 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
120 rows = g_new (png_bytep, h);
122 for (i = 0; i < h; i++)
123 rows[i] = pixels + i * w * bpp;
125 png_read_image (png_ptr, rows);
126 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
129 if (ctype & PNG_COLOR_MASK_ALPHA)
130 return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, TRUE,
134 return gdk_pixbuf_new_from_data (pixels, ART_PIX_RGB, FALSE,
139 /* These avoid the setjmp()/longjmp() crap in libpng */
140 static void png_error_callback (png_structp png_read_ptr,
141 png_const_charp error_msg);
143 static void png_warning_callback(png_structp png_read_ptr,
144 png_const_charp warning_msg);
146 /* Called at the start of the progressive load */
147 static void png_info_callback (png_structp png_read_ptr,
148 png_infop png_info_ptr);
150 /* Called for each row; note that you will get duplicate row numbers
151 for interlaced PNGs */
152 static void png_row_callback (png_structp png_read_ptr,
157 /* Called after reading the entire image */
158 static void png_end_callback (png_structp png_read_ptr,
159 png_infop png_info_ptr);
161 typedef struct _LoadContext LoadContext;
163 struct _LoadContext {
164 png_structp png_read_ptr;
165 png_infop png_info_ptr;
167 ModulePreparedNotifyFunc notify_func;
168 gpointer notify_user_data;
172 guint fatal_error_occurred : 1;
177 image_begin_load (ModulePreparedNotifyFunc func, gpointer user_data)
181 lc = g_new0(LoadContext, 1);
183 lc->fatal_error_occurred = FALSE;
185 lc->notify_func = func;
186 lc->notify_user_data = user_data;
188 /* Create the main PNG context struct */
190 lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
191 lc, /* error/warning callback data */
193 png_warning_callback);
195 if (lc->png_read_ptr == NULL) {
200 /* Create the two auxiliary context structs */
202 lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
204 if (lc->png_info_ptr == NULL) {
205 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
210 png_set_progressive_read_fn(lc->png_read_ptr,
211 lc, /* callback data */
221 image_stop_load (gpointer context)
223 LoadContext* lc = context;
225 g_return_if_fail(lc != NULL);
227 gdk_pixbuf_unref(lc->pixbuf);
229 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
234 image_load_increment(gpointer context, guchar *buf, guint size)
236 LoadContext* lc = context;
238 g_return_val_if_fail(lc != NULL, FALSE);
240 /* Invokes our callbacks as needed */
241 png_process_data(lc->png_read_ptr, lc->png_info_ptr, buf, size);
243 if (lc->fatal_error_occurred)
249 /* Called at the start of the progressive load, once we have image info */
251 png_info_callback (png_structp png_read_ptr,
252 png_infop png_info_ptr)
255 png_uint_32 width, height;
256 int bit_depth, color_type, filter_type,
257 compression_type, interlace_type, channels;
258 gboolean have_alpha = FALSE;
260 lc = png_get_progressive_ptr(png_read_ptr);
262 if (lc->fatal_error_occurred)
265 /* Get the image info */
267 png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
275 /* set_expand() basically needs to be called unless
276 we are already in RGB/RGBA mode
278 if (color_type == PNG_COLOR_TYPE_PALETTE &&
281 /* Convert indexed images to RGB */
282 png_set_expand (lc->png_read_ptr);
284 } else if (color_type == PNG_COLOR_TYPE_GRAY &&
287 /* Convert grayscale to RGB */
288 png_set_expand (lc->png_read_ptr);
290 } else if (png_get_valid (lc->png_read_ptr,
291 lc->png_info_ptr, PNG_INFO_tRNS)) {
293 /* If we have transparency header, convert it to alpha
295 png_set_expand(lc->png_read_ptr);
297 } else if (bit_depth < 8) {
299 /* If we have < 8 scale it up to 8 */
300 png_set_expand(lc->png_read_ptr);
303 /* Conceivably, png_set_packing() is a better idea;
304 * God only knows how libpng works
308 /* If we are 16-bit, convert to 8-bit */
309 if (bit_depth == 16) {
310 png_set_strip_16(lc->png_read_ptr);
313 /* If gray scale, convert to RGB */
314 if (color_type == PNG_COLOR_TYPE_GRAY ||
315 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
316 png_set_gray_to_rgb(lc->png_read_ptr);
319 /* If we have alpha, set a flag */
320 if (color_type & PNG_COLOR_MASK_ALPHA)
323 /* Update the info the reflect our transformations */
324 png_read_update_info(lc->png_read_ptr, lc->png_info_ptr);
326 png_get_IHDR (lc->png_read_ptr, lc->png_info_ptr,
334 #ifndef G_DISABLE_CHECKS
335 /* Check that the new info is what we want */
337 if (bit_depth != 8) {
338 g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
339 lc->fatal_error_occurred = TRUE;
343 if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
344 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
345 g_warning("Transformed PNG not RGB or RGBA.");
346 lc->fatal_error_occurred = TRUE;
350 channels = png_get_channels(lc->png_read_ptr, lc->png_info_ptr);
351 if ( ! (channels == 3 || channels == 4) ) {
352 g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
353 lc->fatal_error_occurred = TRUE;
358 lc->pixbuf = gdk_pixbuf_new(have_alpha, width, height);
360 if (lc->pixbuf == NULL) {
361 /* Failed to allocate memory */
362 lc->fatal_error_occurred = TRUE;
366 /* Notify the client that we are ready to go */
369 (* lc->notify_func) (lc->pixbuf, lc->notify_user_data);
374 /* Called for each row; note that you will get duplicate row numbers
375 for interlaced PNGs */
377 png_row_callback (png_structp png_read_ptr,
383 guchar* old_row = NULL;
385 lc = png_get_progressive_ptr(png_read_ptr);
387 if (lc->fatal_error_occurred)
390 old_row = lc->pixbuf->art_pixbuf->pixels + (row_num * lc->pixbuf->art_pixbuf->rowstride);
392 png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
395 /* Called after reading the entire image */
397 png_end_callback (png_structp png_read_ptr,
398 png_infop png_info_ptr)
402 lc = png_get_progressive_ptr(png_read_ptr);
404 if (lc->fatal_error_occurred)
407 /* Doesn't do anything for now */
412 png_error_callback(png_structp png_read_ptr,
413 png_const_charp error_msg)
417 lc = png_get_error_ptr(png_read_ptr);
419 lc->fatal_error_occurred = TRUE;
421 fprintf(stderr, "Fatal error loading PNG: %s\n", error_msg);
425 png_warning_callback(png_structp png_read_ptr,
426 png_const_charp warning_msg)
430 lc = png_get_error_ptr(png_read_ptr);
432 fprintf(stderr, "Warning loading PNG: %s\n", warning_msg);