2 * io-png.c: GdkPixBuf I/O for PNG files.
3 * Copyright (C) 1999 Mark Crichton
4 * Author: Mark Crichton <crichton@gimp.org>
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.
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.
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.
24 #include "gdk-pixbuf.h"
25 #include "gdk-pixbuf-io.h"
28 /* Shared library entry point */
29 GdkPixBuf *image_load(FILE * f)
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;
36 art_u8 *pixels, *temp, *rowdata;
39 g_return_val_if_fail(f != NULL, NULL);
41 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
44 info_ptr = png_create_info_struct(png_ptr);
46 png_destroy_read_struct(&png_ptr, NULL, NULL);
49 end_info = png_create_info_struct(png_ptr);
51 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
54 if (setjmp(png_ptr->jmpbuf)) {
55 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
58 png_init_io(png_ptr, f);
59 png_read_info(png_ptr, info_ptr);
61 png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
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.
71 if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
72 png_set_expand(png_ptr);
74 if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
75 png_set_expand(png_ptr);
77 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
78 png_set_expand(png_ptr);
81 png_set_strip_16(png_ptr);
83 /* We also have png "packing" bits into bytes if < 8 */
85 png_set_packing(png_ptr);
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);
91 /* ...and if we're interlaced... */
92 passes = png_set_interlace_handling(png_ptr);
94 /* Update our info structs */
95 png_read_update_info(png_ptr, info_ptr);
97 /* Allocate some memory and set up row array */
98 /* This "inhales vigirously"... */
99 if (ctype & PNG_COLOR_MASK_ALPHA)
104 pixels = art_alloc(w * h * bpp);
105 rows = g_malloc(h * sizeof(png_bytep));
107 if ((!pixels) || (!rows)) {
108 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
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) {
115 for (n = 0; n < i; n++)
119 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
124 /* And we FINALLY get here... */
125 png_read_image(png_ptr, rows);
126 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
128 /* Now stuff the bytes into pixels & free rows[y] */
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];
139 temp[3] = rowdata[(x * bpp) + 3];
146 /* Return the GdkPixBuf */
147 pixbuf = g_new(GdkPixBuf, 1);
149 if (ctype & PNG_COLOR_MASK_ALPHA)
150 pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4));
152 pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3));
154 /* Ok, I'm anal...shoot me */
155 if (!(pixbuf->art_pixbuf)) {
161 pixbuf->ref_count = 0;
162 pixbuf->unref_func = NULL;
167 int image_save(GdkPixBuf *pixbuf, FILE *file)
177 g_return_val_if_fail(file != NULL, NULL);
178 g_return_val_if_fail(pixbuf != NULL, NULL);
180 h = pixbuf->art_pixbuf->height;
181 w = pixbuf->art_pixbuf->width;
183 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
185 if (png_ptr == NULL) {
190 info_ptr = png_create_info_struct(png_ptr);
191 if (info_ptr == NULL) {
193 png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
197 if (setjmp(png_ptr->jmpbuf)) {
199 png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
203 png_init_io(png_ptr, file);
204 if (pixbuf->art_pixbuf->has_alpha) {
206 type = PNG_COLOR_TYPE_RGB_ALPHA;
209 type = PNG_COLOR_TYPE_RGB;
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);
220 data = pixbuf->art_pixbuf->pixels;
222 for (y = 0; y < h; y++) {
223 if (pixbuf->art_pixbuf->has_alpha)
224 row_ptr[y] = data + (w * y * 4);
226 row_ptr[y] = data + (w * y * 3);
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);