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