]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-png.c
Fixed 2 C syntax bugs, Daniel
[~andy/gtk] / gdk-pixbuf / io-png.c
1 /*
2  * io-png.c: GdkPixBuf I/O for PNG files.
3  * Copyright (C) 1999 Mark Crichton
4  * Author: Mark Crichton <crichton@gimp.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
19  *
20  */
21 #include <config.h>
22 #include <stdio.h>
23 #include <glib.h>
24 #include "gdk-pixbuf.h"
25 #include "gdk-pixbuf-io.h"
26 #include <png.h>
27
28 /* Shared library entry point */
29 GdkPixBuf *image_load(FILE * f)
30 {
31     png_structp png_ptr;
32     png_infop info_ptr, end_info;
33     gint i, depth, ctype, inttype, passes, bpp;         /* bpp = BYTES/pixel */
34     png_uint_32 w, h, x, y;
35     png_bytepp rows;
36     art_u8 *pixels, *temp, *rowdata;
37     GdkPixBuf *pixbuf;
38
39     g_return_val_if_fail(f != NULL, NULL);
40
41     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
42                                      NULL);
43
44     info_ptr = png_create_info_struct(png_ptr);
45     if (!info_ptr) {
46         png_destroy_read_struct(&png_ptr, NULL, NULL);
47         return NULL;
48     }
49     end_info = png_create_info_struct(png_ptr);
50     if (!end_info) {
51         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
52         return NULL;
53     }
54     if (setjmp(png_ptr->jmpbuf)) {
55         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
56         return NULL;
57     }
58     png_init_io(png_ptr, f);
59     png_read_info(png_ptr, info_ptr);
60
61     png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
62                  NULL, NULL);
63
64     /* Ok, we want to work with 24 bit images.
65      * However, PNG can vary depth per channel.
66      * So, we use the png_set_expand function to expand
67      * everything into a format libart expects.
68      * We also use png_set_strip_16 to reduce down to 8 bit/chan.
69      */
70
71     if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
72         png_set_expand(png_ptr);
73
74     if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
75         png_set_expand(png_ptr);
76
77     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
78         png_set_expand(png_ptr);
79
80     if (depth == 16)
81         png_set_strip_16(png_ptr);
82
83     /* We also have png "packing" bits into bytes if < 8 */
84     if (depth < 8)
85         png_set_packing(png_ptr);
86
87     /* Lastly, if the PNG is greyscale, convert to RGB */
88     if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
89         png_set_gray_to_rgb(png_ptr);
90
91     /* ...and if we're interlaced... */
92     passes = png_set_interlace_handling(png_ptr);
93
94     /* Update our info structs */
95     png_read_update_info(png_ptr, info_ptr);
96
97     /* Allocate some memory and set up row array */
98     /* This "inhales vigirously"... */
99     if (ctype & PNG_COLOR_MASK_ALPHA)
100         bpp = 4;
101     else
102         bpp = 3;
103
104     pixels = art_alloc(w * h * bpp);
105     rows = g_malloc(h * sizeof(png_bytep));
106
107     if ((!pixels) || (!rows)) {
108         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
109         return NULL;
110     }
111     /* Icky code, but it has to be done... */
112     for (i = 0; i < h; i++) {
113         if ((rows[i] = g_malloc(w * sizeof(art_u8) * bpp)) == NULL) {
114             int n;
115             for (n = 0; n < i; n++)
116                 g_free(rows[i]);
117             g_free(rows);
118             art_free(pixels);
119             png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
120             return NULL;
121         }
122     }
123
124     /* And we FINALLY get here... */
125     png_read_image(png_ptr, rows);
126     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
127
128     /* Now stuff the bytes into pixels & free rows[y] */
129
130     temp = pixels;
131
132     for (y = 0; y < h; y++) {
133         (png_bytep) rowdata = rows[y];
134         for (x = 0; x < w; x++) {
135             temp[0] = rowdata[(x * bpp)];
136             temp[1] = rowdata[(x * bpp) + 1];
137             temp[2] = rowdata[(x * bpp) + 2];
138             if (bpp == 4)
139                 temp[3] = rowdata[(x * bpp) + 3];
140             temp += bpp;
141         }
142         g_free(rows[y]);
143     }
144     g_free(rows);
145
146     /* Return the GdkPixBuf */
147     pixbuf = g_new(GdkPixBuf, 1);
148
149     if (ctype & PNG_COLOR_MASK_ALPHA)
150         pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4));
151     else
152         pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3));
153
154     /* Ok, I'm anal...shoot me */
155     if (!(pixbuf->art_pixbuf)) {
156         art_free(pixels);
157         g_free(pixbuf);
158         return NULL;
159     }
160
161     pixbuf->ref_count = 0;
162     pixbuf->unref_func = NULL;
163
164     return pixbuf;
165 }
166
167 int image_save(GdkPixBuf *pixbuf, FILE *file)
168 {
169      png_structp png_ptr;
170      png_infop info_ptr;
171      art_u8 *data;
172      gint y, h, w;
173      png_bytep row_ptr;
174      png_color_8 sig_bit;
175      gint type;
176      
177      g_return_val_if_fail(file != NULL, NULL);
178      g_return_val_if_fail(pixbuf != NULL, NULL);
179
180      h = pixbuf->art_pixbuf->height;
181      w = pixbuf->art_pixbuf->width;
182
183      png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
184                                        NULL, NULL, NULL);
185      if (png_ptr == NULL) {
186           fclose(file);
187           return NULL;
188      }
189
190      info_ptr = png_create_info_struct(png_ptr);
191      if (info_ptr == NULL) {
192           fclose(file);
193           png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
194           return NULL;
195      }
196
197      if (setjmp(png_ptr->jmpbuf)) {
198           fclose(file);
199           png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
200           return NULL;
201      }
202
203      png_init_io(png_ptr, file);
204      if (pixbuf->art_pixbuf->has_alpha) {
205           sig_bit.alpha = 8;
206           type = PNG_COLOR_TYPE_RGB_ALPHA;
207      } else {
208           sig_bit.alpha = 0;
209           type = PNG_COLOR_TYPE_RGB;
210      }
211
212      sig_bit.red = sig_bit.green = sig_bit.blue = 8;
213      png_set_IHDR(png_ptr, info_ptr, w, h, 8, type, PNG_INTERLACE_NONE,
214                   PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
215      png_set_sBIT(png_ptr, info_ptr, &sig_bit);
216      png_write_info(png_ptr, info_ptr);
217      png_set_shift(png_ptr, &sig_bit);
218      png_set_packing(png_ptr);
219
220      data = pixbuf->art_pixbuf->pixels;
221
222      for (y = 0; y < h; y++) {
223           if (pixbuf->art_pixbuf->has_alpha)
224                row_ptr[y] = data + (w * y * 4);
225           else
226                row_ptr[y] = data + (w * y * 3);
227      }
228
229      png_write_image(png_ptr, row_ptr);
230      png_write_end(png_ptr, info_ptr);
231      png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
232
233      return TRUE;
234 }
235