]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-png.c
Add missing <stdlib.h> include. Add ifdef so we compile without warnings
[~andy/gtk] / gdk-pixbuf / io-png.c
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, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <png.h>
30 #include "gdk-pixbuf-private.h"
31 #include "gdk-pixbuf-io.h"
32
33 \f
34
35 static void
36 setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
37                           gboolean *fatal_error_occurred,
38                           png_uint_32* width_p, png_uint_32* height_p,
39                           int* color_type_p)
40 {
41         png_uint_32 width, height;
42         int bit_depth, color_type, interlace_type, compression_type, filter_type;
43 #ifndef G_DISABLE_CHECKS
44         int channels;
45 #endif
46         
47         /* Get the image info */
48
49         png_get_IHDR (png_read_ptr, png_info_ptr,
50                       &width, &height,
51                       &bit_depth,
52                       &color_type,
53                       &interlace_type,
54                       &compression_type,
55                       &filter_type);
56
57         /* set_expand() basically needs to be called unless
58            we are already in RGB/RGBA mode
59         */
60         if (color_type == PNG_COLOR_TYPE_PALETTE &&
61             bit_depth <= 8) {
62
63                 /* Convert indexed images to RGB */
64                 png_set_expand (png_read_ptr);
65
66         } else if (color_type == PNG_COLOR_TYPE_GRAY &&
67                    bit_depth < 8) {
68
69                 /* Convert grayscale to RGB */
70                 png_set_expand (png_read_ptr);
71
72         } else if (png_get_valid (png_read_ptr, 
73                                   png_info_ptr, PNG_INFO_tRNS)) {
74
75                 /* If we have transparency header, convert it to alpha
76                    channel */
77                 png_set_expand(png_read_ptr);
78                 
79         } else if (bit_depth < 8) {
80
81                 /* If we have < 8 scale it up to 8 */
82                 png_set_expand(png_read_ptr);
83
84
85                 /* Conceivably, png_set_packing() is a better idea;
86                  * God only knows how libpng works
87                  */
88         }
89
90         /* If we are 16-bit, convert to 8-bit */
91         if (bit_depth == 16) {
92                 png_set_strip_16(png_read_ptr);
93         }
94
95         /* If gray scale, convert to RGB */
96         if (color_type == PNG_COLOR_TYPE_GRAY ||
97             color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
98                 png_set_gray_to_rgb(png_read_ptr);
99         }
100         
101         /* If interlaced, handle that */
102         if (interlace_type != PNG_INTERLACE_NONE) {
103                 png_set_interlace_handling(png_read_ptr);
104         }
105         
106         /* Update the info the reflect our transformations */
107         png_read_update_info(png_read_ptr, png_info_ptr);
108         
109         png_get_IHDR (png_read_ptr, png_info_ptr,
110                       &width, &height,
111                       &bit_depth,
112                       &color_type,
113                       &interlace_type,
114                       &compression_type,
115                       &filter_type);
116
117         *width_p = width;
118         *height_p = height;
119         *color_type_p = color_type;
120         
121 #ifndef G_DISABLE_CHECKS
122         /* Check that the new info is what we want */
123         
124         if (bit_depth != 8) {
125                 g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
126                 *fatal_error_occurred = TRUE;
127                 return;
128         }
129
130         if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
131                 color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
132                 g_warning("Transformed PNG not RGB or RGBA.");
133                 *fatal_error_occurred = TRUE;
134                 return;
135         }
136
137         channels = png_get_channels(png_read_ptr, png_info_ptr);
138         if ( ! (channels == 3 || channels == 4) ) {
139                 g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
140                 *fatal_error_occurred = TRUE;
141                 return;
142         }
143 #endif
144 }
145
146 static void
147 png_simple_error_callback(png_structp png_save_ptr,
148                           png_const_charp error_msg)
149 {
150         GError **error;
151         
152         error = png_get_error_ptr(png_save_ptr);
153
154         /* I don't trust libpng to call the error callback only once,
155          * so check for already-set error
156          */
157         if (error && *error == NULL) {
158                 g_set_error (error,
159                              GDK_PIXBUF_ERROR,
160                              GDK_PIXBUF_ERROR_FAILED,
161                              _("Fatal error in PNG image file: %s"),
162                              error_msg);
163         }
164 }
165
166 static void
167 png_simple_warning_callback(png_structp png_save_ptr,
168                             png_const_charp warning_msg)
169 {
170         /* Don't print anything; we should not be dumping junk to
171          * stderr, since that may be bad for some apps. If it's
172          * important enough to display, we need to add a GError
173          * **warning return location wherever we have an error return
174          * location.
175          */
176 }
177
178 /* Destroy notification function for the pixbuf */
179 static void
180 free_buffer (guchar *pixels, gpointer data)
181 {
182         g_free (pixels);
183 }
184
185 /* Shared library entry point */
186 static GdkPixbuf *
187 gdk_pixbuf__png_image_load (FILE *f, GError **error)
188 {
189         png_structp png_ptr;
190         png_infop info_ptr, end_info;
191         gboolean failed = FALSE;
192         gint i, ctype, bpp;
193         png_uint_32 w, h;
194         png_bytepp rows;
195         guchar *pixels;
196
197         png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
198                                           error,
199                                           png_simple_error_callback,
200                                           png_simple_warning_callback);
201         if (!png_ptr)
202                 return NULL;
203
204         info_ptr = png_create_info_struct (png_ptr);
205         if (!info_ptr) {
206                 png_destroy_read_struct (&png_ptr, NULL, NULL);
207                 return NULL;
208         }
209
210         end_info = png_create_info_struct (png_ptr);
211         if (!end_info) {
212                 png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
213                 return NULL;
214         }
215
216         if (setjmp (png_ptr->jmpbuf)) {
217                 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
218                 return NULL;
219         }
220
221         png_init_io (png_ptr, f);
222         png_read_info (png_ptr, info_ptr);
223
224         setup_png_transformations(png_ptr, info_ptr, &failed, &w, &h, &ctype);
225
226         if (failed) {
227                 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
228                 return NULL;
229         }
230         
231         if (ctype & PNG_COLOR_MASK_ALPHA)
232                 bpp = 4;
233         else
234                 bpp = 3;
235
236         pixels = g_try_malloc (w * h * bpp);
237         if (!pixels) {
238                 /* Check error NULL, normally this would be broken,
239                  * but libpng makes me want to code defensively.
240                  */
241                 if (error && *error == NULL) {
242                         g_set_error (error,
243                                      GDK_PIXBUF_ERROR,
244                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
245                                      _("Insufficient memory to load PNG file"));
246                 }
247                 
248                 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
249                 return NULL;
250         }
251
252         rows = g_new (png_bytep, h);
253
254         for (i = 0; i < h; i++)
255                 rows[i] = pixels + i * w * bpp;
256
257         png_read_image (png_ptr, rows);
258         png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
259         g_free (rows);
260
261         if (ctype & PNG_COLOR_MASK_ALPHA)
262                 return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
263                                                  w, h, w * 4,
264                                                  free_buffer, NULL);
265         else
266                 return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
267                                                  w, h, w * 3,
268                                                  free_buffer, NULL);
269 }
270
271 /* I wish these avoided the setjmp()/longjmp() crap in libpng instead
272    just allow you to change the error reporting. */
273 static void png_error_callback  (png_structp png_read_ptr,
274                                  png_const_charp error_msg);
275
276 static void png_warning_callback(png_structp png_read_ptr,
277                                  png_const_charp warning_msg);
278
279 /* Called at the start of the progressive load */
280 static void png_info_callback   (png_structp png_read_ptr,
281                                  png_infop   png_info_ptr);
282
283 /* Called for each row; note that you will get duplicate row numbers
284    for interlaced PNGs */
285 static void png_row_callback   (png_structp png_read_ptr,
286                                 png_bytep   new_row,
287                                 png_uint_32 row_num,
288                                 int pass_num);
289
290 /* Called after reading the entire image */
291 static void png_end_callback   (png_structp png_read_ptr,
292                                 png_infop   png_info_ptr);
293
294 typedef struct _LoadContext LoadContext;
295
296 struct _LoadContext {
297         png_structp png_read_ptr;
298         png_infop   png_info_ptr;
299
300         ModulePreparedNotifyFunc prepare_func;
301         ModuleUpdatedNotifyFunc update_func;
302         gpointer notify_user_data;
303
304         GdkPixbuf* pixbuf;
305
306         /* row number of first row seen, or -1 if none yet seen */
307
308         gint first_row_seen_in_chunk;
309
310         /* pass number for the first row seen */
311
312         gint first_pass_seen_in_chunk;
313         
314         /* row number of last row seen */
315         gint last_row_seen_in_chunk;
316
317         gint last_pass_seen_in_chunk;
318
319         /* highest row number seen */
320         gint max_row_seen_in_chunk;
321         
322         guint fatal_error_occurred : 1;
323
324         GError **error;
325 };
326
327 static gpointer
328 gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func,
329                                   ModuleUpdatedNotifyFunc update_func,
330                                   gpointer user_data,
331                                   GError **error)
332 {
333         LoadContext* lc;
334         
335         lc = g_new0(LoadContext, 1);
336         
337         lc->fatal_error_occurred = FALSE;
338
339         lc->prepare_func = prepare_func;
340         lc->update_func = update_func;
341         lc->notify_user_data = user_data;
342
343         lc->first_row_seen_in_chunk = -1;
344         lc->last_row_seen_in_chunk = -1;
345         lc->first_pass_seen_in_chunk = -1;
346         lc->last_pass_seen_in_chunk = -1;
347         lc->max_row_seen_in_chunk = -1;
348         lc->error = error;
349         
350         /* Create the main PNG context struct */
351
352                 
353         lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
354                                                   lc, /* error/warning callback data */
355                                                   png_error_callback,
356                                                   png_warning_callback);
357
358         if (lc->png_read_ptr == NULL) {
359                 g_free(lc);
360                 /* error callback should have set the error */
361                 return NULL;
362         }
363         
364         if (setjmp (lc->png_read_ptr->jmpbuf)) {
365                 if (lc->png_info_ptr)
366                         png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
367                 g_free(lc);
368                 /* error callback should have set the error */
369                 return NULL;
370         }
371
372         /* Create the auxiliary context struct */
373
374         lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
375
376         if (lc->png_info_ptr == NULL) {
377                 png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
378                 g_free(lc);
379                 /* error callback should have set the error */
380                 return NULL;
381         }
382
383         png_set_progressive_read_fn(lc->png_read_ptr,
384                                     lc, /* callback data */
385                                     png_info_callback,
386                                     png_row_callback,
387                                     png_end_callback);
388         
389
390         /* We don't want to keep modifying error after returning here,
391          * it may no longer be valid.
392          */
393         lc->error = NULL;
394         
395         return lc;
396 }
397
398 static gboolean
399 gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
400 {
401         LoadContext* lc = context;
402
403         g_return_val_if_fail(lc != NULL, TRUE);
404
405         /* FIXME this thing needs to report errors if
406          * we have unused image data
407          */
408         
409         if (lc->pixbuf)
410                 gdk_pixbuf_unref (lc->pixbuf);
411         
412         png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
413         g_free(lc);
414
415         return TRUE;
416 }
417
418 static gboolean
419 gdk_pixbuf__png_image_load_increment(gpointer context,
420                                      const guchar *buf, guint size,
421                                      GError **error)
422 {
423         LoadContext* lc = context;
424
425         g_return_val_if_fail(lc != NULL, FALSE);
426
427         /* reset */
428         lc->first_row_seen_in_chunk = -1;
429         lc->last_row_seen_in_chunk = -1;
430         lc->first_pass_seen_in_chunk = -1;
431         lc->last_pass_seen_in_chunk = -1;
432         lc->max_row_seen_in_chunk = -1;
433         lc->error = error;
434         
435         /* Invokes our callbacks as needed */
436         if (setjmp (lc->png_read_ptr->jmpbuf)) {
437                 lc->error = NULL;
438                 return FALSE;
439         } else {
440                 png_process_data(lc->png_read_ptr, lc->png_info_ptr,
441                                  (guchar*) buf, size);
442         }
443
444         if (lc->fatal_error_occurred) {
445                 lc->error = NULL;
446                 return FALSE;
447         } else {
448                 if (lc->first_row_seen_in_chunk >= 0) {
449                         /* We saw at least one row */
450                         gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
451                         
452                         g_assert(pass_diff >= 0);
453                         
454                         if (pass_diff == 0) {
455                                 /* start and end row were in the same pass */
456                                 (lc->update_func)(lc->pixbuf, 0,
457                                                   lc->first_row_seen_in_chunk,
458                                                   lc->pixbuf->width,
459                                                   (lc->last_row_seen_in_chunk -
460                                                    lc->first_row_seen_in_chunk) + 1,
461                                                   lc->notify_user_data);
462                         } else if (pass_diff == 1) {
463                                 /* We have from the first row seen to
464                                    the end of the image (max row
465                                    seen), then from the top of the
466                                    image to the last row seen */
467                                 /* first row to end */
468                                 (lc->update_func)(lc->pixbuf, 0,
469                                                   lc->first_row_seen_in_chunk,
470                                                   lc->pixbuf->width,
471                                                   (lc->max_row_seen_in_chunk -
472                                                    lc->first_row_seen_in_chunk) + 1,
473                                                   lc->notify_user_data);
474                                 /* top to last row */
475                                 (lc->update_func)(lc->pixbuf,
476                                                   0, 0, 
477                                                   lc->pixbuf->width,
478                                                   lc->last_row_seen_in_chunk + 1,
479                                                   lc->notify_user_data);
480                         } else {
481                                 /* We made at least one entire pass, so update the
482                                    whole image */
483                                 (lc->update_func)(lc->pixbuf,
484                                                   0, 0, 
485                                                   lc->pixbuf->width,
486                                                   lc->max_row_seen_in_chunk + 1,
487                                                   lc->notify_user_data);
488                         }
489                 }
490
491                 lc->error = NULL;
492                 
493                 return TRUE;
494         }
495 }
496
497 /* Called at the start of the progressive load, once we have image info */
498 static void
499 png_info_callback   (png_structp png_read_ptr,
500                      png_infop   png_info_ptr)
501 {
502         LoadContext* lc;
503         png_uint_32 width, height;
504         int color_type;
505         gboolean have_alpha = FALSE;
506         gboolean failed = FALSE;
507         
508         lc = png_get_progressive_ptr(png_read_ptr);
509
510         if (lc->fatal_error_occurred)
511                 return;
512
513         setup_png_transformations(lc->png_read_ptr,
514                                   lc->png_info_ptr,
515                                   &failed,
516                                   &width, &height, &color_type);
517
518         if (failed) {
519                 lc->fatal_error_occurred = TRUE;
520                 return;
521         }
522
523         /* If we have alpha, set a flag */
524         if (color_type & PNG_COLOR_MASK_ALPHA)
525                 have_alpha = TRUE;
526         
527         lc->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
528
529         if (lc->pixbuf == NULL) {
530                 /* Failed to allocate memory */
531                 lc->fatal_error_occurred = TRUE;
532                 if (lc->error && *lc->error == NULL) {
533                         g_set_error (lc->error,
534                                      GDK_PIXBUF_ERROR,
535                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
536                                      _("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
537                                      width, height);
538                 }
539                 return;
540         }
541         
542         /* Notify the client that we are ready to go */
543
544         if (lc->prepare_func)
545                 (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
546         
547         return;
548 }
549
550 /* Called for each row; note that you will get duplicate row numbers
551    for interlaced PNGs */
552 static void
553 png_row_callback   (png_structp png_read_ptr,
554                     png_bytep   new_row,
555                     png_uint_32 row_num,
556                     int pass_num)
557 {
558         LoadContext* lc;
559         guchar* old_row = NULL;
560
561         lc = png_get_progressive_ptr(png_read_ptr);
562
563         if (lc->fatal_error_occurred)
564                 return;
565
566         if (lc->first_row_seen_in_chunk < 0) {
567                 lc->first_row_seen_in_chunk = row_num;
568                 lc->first_pass_seen_in_chunk = pass_num;
569         }
570
571         lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
572         lc->last_row_seen_in_chunk = row_num;
573         lc->last_pass_seen_in_chunk = pass_num;
574         
575         old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);
576
577         png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
578 }
579
580 /* Called after reading the entire image */
581 static void
582 png_end_callback   (png_structp png_read_ptr,
583                     png_infop   png_info_ptr)
584 {
585         LoadContext* lc;
586
587         lc = png_get_progressive_ptr(png_read_ptr);
588
589         if (lc->fatal_error_occurred)
590                 return;
591 }
592
593 static void
594 png_error_callback(png_structp png_read_ptr,
595                    png_const_charp error_msg)
596 {
597         LoadContext* lc;
598         
599         lc = png_get_error_ptr(png_read_ptr);
600         
601         lc->fatal_error_occurred = TRUE;
602
603         /* I don't trust libpng to call the error callback only once,
604          * so check for already-set error
605          */
606         if (lc->error && *lc->error == NULL) {
607                 g_set_error (lc->error,
608                              GDK_PIXBUF_ERROR,
609                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
610                              _("Fatal error reading PNG image file: %s"),
611                              error_msg);
612         }
613
614         longjmp (png_read_ptr->jmpbuf, 1);
615 }
616
617 static void
618 png_warning_callback(png_structp png_read_ptr,
619                      png_const_charp warning_msg)
620 {
621         LoadContext* lc;
622         
623         lc = png_get_error_ptr(png_read_ptr);
624
625         /* Don't print anything; we should not be dumping junk to
626          * stderr, since that may be bad for some apps. If it's
627          * important enough to display, we need to add a GError
628          * **warning return location wherever we have an error return
629          * location.
630          */
631 }
632
633
634 /* Save */
635
636 static gboolean
637 gdk_pixbuf__png_image_save (FILE          *f, 
638                             GdkPixbuf     *pixbuf, 
639                             gchar        **keys,
640                             gchar        **values,
641                             GError       **error)
642 {
643        png_structp png_ptr;
644        png_infop info_ptr;
645        guchar *ptr;
646        guchar *pixels;
647        int x, y, j;
648        png_bytep row_ptr;
649        png_bytep data;
650        png_color_8 sig_bit;
651        int w, h, rowstride;
652        int has_alpha;
653        int bpc;
654
655        if (keys && *keys) {
656                g_warning ("Bad option name '%s' passed to PNG saver",
657                           *keys);
658                return FALSE;
659 #if 0
660                gchar **kiter = keys;
661                gchar **viter = values;
662
663                
664                while (*kiter) {
665                        
666                        ++kiter;
667                        ++viter;
668                }
669 #endif
670        }
671        data = NULL;
672        
673        bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
674        w = gdk_pixbuf_get_width (pixbuf);
675        h = gdk_pixbuf_get_height (pixbuf);
676        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
677        has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
678        pixels = gdk_pixbuf_get_pixels (pixbuf);
679
680        png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
681                                           error,
682                                           png_simple_error_callback,
683                                           png_simple_warning_callback);
684
685        g_return_val_if_fail (png_ptr != NULL, FALSE);
686
687        info_ptr = png_create_info_struct (png_ptr);
688        if (info_ptr == NULL) {
689                png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
690                return FALSE;
691        }
692        if (setjmp (png_ptr->jmpbuf)) {
693                png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
694                return FALSE;
695        }
696        png_init_io (png_ptr, f);
697        if (has_alpha) {
698                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
699                              PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
700                              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
701 #ifdef WORDS_BIGENDIAN
702                png_set_swap_alpha (png_ptr);
703 #else
704                png_set_bgr (png_ptr);
705 #endif
706        } else {
707                png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
708                              PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
709                              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
710                data = g_try_malloc (w * 3 * sizeof (char));
711
712                if (data == NULL) {
713                        /* Check error NULL, normally this would be broken,
714                         * but libpng makes me want to code defensively.
715                         */
716                        if (error && *error == NULL) {
717                                g_set_error (error,
718                                             GDK_PIXBUF_ERROR,
719                                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
720                                             _("Insufficient memory to save PNG file"));
721                        }
722                        png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
723                        return FALSE;
724                }
725        }
726        sig_bit.red = bpc;
727        sig_bit.green = bpc;
728        sig_bit.blue = bpc;
729        sig_bit.alpha = bpc;
730        png_set_sBIT (png_ptr, info_ptr, &sig_bit);
731        png_write_info (png_ptr, info_ptr);
732        png_set_shift (png_ptr, &sig_bit);
733        png_set_packing (png_ptr);
734
735        ptr = pixels;
736        for (y = 0; y < h; y++) {
737                if (has_alpha)
738                        row_ptr = (png_bytep)ptr;
739                else {
740                        for (j = 0, x = 0; x < w; x++)
741                                memcpy (&(data[x*3]), &(ptr[x*3]), 3);
742
743                        row_ptr = (png_bytep)data;
744                }
745                png_write_rows (png_ptr, &row_ptr, 1);
746                ptr += rowstride;
747        }
748
749        if (data)
750                g_free (data);
751
752        png_write_end (png_ptr, info_ptr);
753        png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
754
755        return TRUE;
756 }
757
758
759
760 void
761 gdk_pixbuf__png_fill_vtable (GdkPixbufModule *module)
762 {
763   module->load = gdk_pixbuf__png_image_load;
764   module->begin_load = gdk_pixbuf__png_image_begin_load;
765   module->stop_load = gdk_pixbuf__png_image_stop_load;
766   module->load_increment = gdk_pixbuf__png_image_load_increment;
767   module->save = gdk_pixbuf__png_image_save;
768 }