io-png.c 6.43 KB
Newer Older
1
/*
2
 * io-png.c: GdkPixBuf I/O for PNG files.
3 4
 * Copyright (C) 1999 Mark Crichton
 * Author: Mark Crichton <crichton@gimp.org>
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
19 20 21 22
 *
 */
#include <config.h>
#include <stdio.h>
23
#include <glib.h>
24 25 26 27 28
#include "gdk-pixbuf.h"
#include "gdk-pixbuf-io.h"
#include <png.h>

/* Shared library entry point */
29
GdkPixBuf *image_load(FILE * f)
30
{
31 32 33 34 35 36 37
    png_structp png_ptr;
    png_infop info_ptr, end_info;
    gint i, depth, ctype, inttype, passes, bpp;		/* bpp = BYTES/pixel */
    png_uint_32 w, h, x, y;
    png_bytepp rows;
    art_u8 *pixels, *temp, *rowdata;
    GdkPixBuf *pixbuf;
Michael Meeks's avatar
Michael Meeks committed
38
    ArtPixBuf *art_pixbuf;
39

Michael Meeks's avatar
Michael Meeks committed
40
    g_return_val_if_fail (f != NULL, NULL);
41

Michael Meeks's avatar
Michael Meeks committed
42 43 44 45
    png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
				      NULL, NULL, NULL);
    if (!png_ptr)
	    return NULL;
46

Michael Meeks's avatar
Michael Meeks committed
47
    info_ptr = png_create_info_struct (png_ptr);
48
    if (!info_ptr) {
Michael Meeks's avatar
Michael Meeks committed
49 50
	    png_destroy_read_struct (&png_ptr, NULL, NULL);
	    return NULL;
51
    }
Michael Meeks's avatar
Michael Meeks committed
52 53

    end_info = png_create_info_struct (png_ptr);
54
    if (!end_info) {
Michael Meeks's avatar
Michael Meeks committed
55 56
	    png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
	    return NULL;
57
    }
Michael Meeks's avatar
Michael Meeks committed
58 59 60 61

    if (setjmp (png_ptr->jmpbuf)) {
	    png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
	    return NULL;
62
    }
63

Michael Meeks's avatar
Michael Meeks committed
64 65 66 67
    png_init_io   (png_ptr, f);
    png_read_info (png_ptr, info_ptr);
    png_get_IHDR  (png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
		   NULL, NULL);
68

69 70 71 72 73 74 75
    /* Ok, we want to work with 24 bit images.
     * However, PNG can vary depth per channel.
     * So, we use the png_set_expand function to expand
     * everything into a format libart expects.
     * We also use png_set_strip_16 to reduce down to 8 bit/chan.
     */
    if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
Michael Meeks's avatar
Michael Meeks committed
76
	png_set_expand (png_ptr);
77

78
    if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
Michael Meeks's avatar
Michael Meeks committed
79
	png_set_expand (png_ptr);
80

Michael Meeks's avatar
Michael Meeks committed
81 82 83 84
    if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
	png_set_expand (png_ptr);
	g_warning ("FIXME: We are going to crash");
    }
85

86
    if (depth == 16)
Michael Meeks's avatar
Michael Meeks committed
87
	png_set_strip_16 (png_ptr);
88

89 90
    /* We also have png "packing" bits into bytes if < 8 */
    if (depth < 8)
Michael Meeks's avatar
Michael Meeks committed
91
	png_set_packing (png_ptr);
92

93 94
    /* Lastly, if the PNG is greyscale, convert to RGB */
    if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
Michael Meeks's avatar
Michael Meeks committed
95
	png_set_gray_to_rgb (png_ptr);
96

97
    /* ...and if we're interlaced... */
Michael Meeks's avatar
Michael Meeks committed
98
    passes = png_set_interlace_handling (png_ptr);
99

100
    /* Update our info structs */
Michael Meeks's avatar
Michael Meeks committed
101
    png_read_update_info (png_ptr, info_ptr);
102

103
    /* Allocate some memory and set up row array */
Michael Meeks's avatar
Michael Meeks committed
104
    /* This "inhales vigorously"... */
105 106 107 108
    if (ctype & PNG_COLOR_MASK_ALPHA)
	bpp = 4;
    else
	bpp = 3;
109

Michael Meeks's avatar
Michael Meeks committed
110 111
    pixels = art_alloc (w * h * bpp);
    rows   = g_malloc  (h * sizeof(png_bytep));
112

Michael Meeks's avatar
Michael Meeks committed
113 114
    if (!pixels || !rows) {
	png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
115 116 117 118
	return NULL;
    }
    /* Icky code, but it has to be done... */
    for (i = 0; i < h; i++) {
Michael Meeks's avatar
Michael Meeks committed
119
	if ((rows[i] = g_malloc (w * sizeof (art_u8) * bpp)) == NULL) {
120 121
	    int n;
	    for (n = 0; n < i; n++)
Michael Meeks's avatar
Michael Meeks committed
122 123 124 125
		g_free (rows[i]);
	    g_free (rows);
	    art_free (pixels);
	    png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
126 127 128
	    return NULL;
	}
    }
129

130
    /* And we FINALLY get here... */
Michael Meeks's avatar
Michael Meeks committed
131 132
    png_read_image (png_ptr, rows);
    png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
133

134
    /* Now stuff the bytes into pixels & free rows[y] */
135

136
    temp = pixels;
137

138 139 140 141 142 143 144 145 146
    for (y = 0; y < h; y++) {
	(png_bytep) rowdata = rows[y];
	for (x = 0; x < w; x++) {
	    temp[0] = rowdata[(x * bpp)];
	    temp[1] = rowdata[(x * bpp) + 1];
	    temp[2] = rowdata[(x * bpp) + 2];
	    if (bpp == 4)
		temp[3] = rowdata[(x * bpp) + 3];
	    temp += bpp;
147
	}
Michael Meeks's avatar
Michael Meeks committed
148
	g_free (rows[y]);
149
    }
Michael Meeks's avatar
Michael Meeks committed
150
    g_free (rows);
151

152
    if (ctype & PNG_COLOR_MASK_ALPHA)
Michael Meeks's avatar
Michael Meeks committed
153
	    art_pixbuf = art_pixbuf_new_rgba (pixels, w, h, (w * 4));
154
    else
Michael Meeks's avatar
Michael Meeks committed
155
	    art_pixbuf = art_pixbuf_new_rgb  (pixels, w, h, (w * 3));
156

Michael Meeks's avatar
Michael Meeks committed
157
    pixbuf = gdk_pixbuf_new (art_pixbuf, NULL);
158

Michael Meeks's avatar
Michael Meeks committed
159 160
    if (!pixbuf)
        art_free (pixels);
161

162
    return pixbuf;
163
}
164 165 166

int image_save(GdkPixBuf *pixbuf, FILE *file)
{
167 168 169 170
     png_structp png_ptr;
     png_infop info_ptr;
     art_u8 *data;
     gint y, h, w;
171
     png_bytepp row_ptr;
172 173
     png_color_8 sig_bit;
     gint type;
174 175 176

     g_return_val_if_fail(file != NULL, FALSE);
     g_return_val_if_fail(pixbuf != NULL, FALSE);
177 178 179 180 181 182 183 184

     h = pixbuf->art_pixbuf->height;
     w = pixbuf->art_pixbuf->width;

     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
				       NULL, NULL, NULL);
     if (png_ptr == NULL) {
	  fclose(file);
185
	  return FALSE;
186 187 188 189 190 191
     }

     info_ptr = png_create_info_struct(png_ptr);
     if (info_ptr == NULL) {
	  fclose(file);
	  png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
192
	  return FALSE;
193 194 195 196 197
     }

     if (setjmp(png_ptr->jmpbuf)) {
	  fclose(file);
	  png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
198
	  return FALSE;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
     }

     png_init_io(png_ptr, file);
     if (pixbuf->art_pixbuf->has_alpha) {
	  sig_bit.alpha = 8;
	  type = PNG_COLOR_TYPE_RGB_ALPHA;
     } else {
	  sig_bit.alpha = 0;
	  type = PNG_COLOR_TYPE_RGB;
     }

     sig_bit.red = sig_bit.green = sig_bit.blue = 8;
     png_set_IHDR(png_ptr, info_ptr, w, h, 8, type, PNG_INTERLACE_NONE,
		  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
     png_set_sBIT(png_ptr, info_ptr, &sig_bit);
     png_write_info(png_ptr, info_ptr);
     png_set_shift(png_ptr, &sig_bit);
     png_set_packing(png_ptr);

     data = pixbuf->art_pixbuf->pixels;
219
     row_ptr = g_new(png_byte *, h);
220

221 222 223 224
     for (y = 0; y < h; y++)
	 row_ptr[y] = data + y * pixbuf->art_pixbuf->rowstride;
#if 0
     {
225 226 227 228 229
	  if (pixbuf->art_pixbuf->has_alpha)
	       row_ptr[y] = data + (w * y * 4);
	  else
	       row_ptr[y] = data + (w * y * 3);
     }
230
#endif
231 232

     png_write_image(png_ptr, row_ptr);
233 234
     g_free (row_ptr);

235 236 237 238 239
     png_write_end(png_ptr, info_ptr);
     png_destroy_write_struct(&png_ptr, (png_infopp) NULL);

     return TRUE;
}