io-png.c 4.63 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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    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;

    g_return_val_if_fail(f != NULL, NULL);

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,
				     NULL);

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
	png_destroy_read_struct(&png_ptr, NULL, NULL);
	return NULL;
    }
    end_info = png_create_info_struct(png_ptr);
    if (!end_info) {
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	return NULL;
    }
    if (setjmp(png_ptr->jmpbuf)) {
	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
	return NULL;
    }
    png_init_io(png_ptr, f);
    png_read_info(png_ptr, info_ptr);
60

61 62
    png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
		 NULL, NULL);
63

64 65 66 67 68 69
    /* 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.
     */
70

71 72
    if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
	png_set_expand(png_ptr);
73

74 75
    if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
	png_set_expand(png_ptr);
76

77 78
    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
	png_set_expand(png_ptr);
79

80 81
    if (depth == 16)
	png_set_strip_16(png_ptr);
82

83 84 85
    /* We also have png "packing" bits into bytes if < 8 */
    if (depth < 8)
	png_set_packing(png_ptr);
86

87 88 89
    /* Lastly, if the PNG is greyscale, convert to RGB */
    if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
	png_set_gray_to_rgb(png_ptr);
90

91 92
    /* ...and if we're interlaced... */
    passes = png_set_interlace_handling(png_ptr);
93

94 95
    /* Update our info structs */
    png_read_update_info(png_ptr, info_ptr);
96

97 98 99 100 101 102
    /* Allocate some memory and set up row array */
    /* This "inhales vigirously"... */
    if (ctype & PNG_COLOR_MASK_ALPHA)
	bpp = 4;
    else
	bpp = 3;
103

104 105
    pixels = art_alloc(w * h * bpp);
    rows = g_malloc(h * sizeof(png_bytep));
106

107 108 109 110 111 112 113 114 115 116 117
    if ((!pixels) || (!rows)) {
	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
	return NULL;
    }
    /* Icky code, but it has to be done... */
    for (i = 0; i < h; i++) {
	if ((rows[i] = g_malloc(w * sizeof(art_u8) * bpp)) == NULL) {
	    int n;
	    for (n = 0; n < i; n++)
		g_free(rows[i]);
	    g_free(rows);
118
	    art_free(pixels);
119 120 121 122
	    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
	    return NULL;
	}
    }
123

124 125 126
    /* And we FINALLY get here... */
    png_read_image(png_ptr, rows);
    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 133 134 135 136 137 138 139 140
    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;
141
	}
142 143 144
	g_free(rows[y]);
    }
    g_free(rows);
145

146 147
    /* Return the GdkPixBuf */
    pixbuf = g_new(GdkPixBuf, 1);
148

149 150 151 152
    if (ctype & PNG_COLOR_MASK_ALPHA)
	pixbuf->art_pixbuf = art_pixbuf_new_rgba(pixels, w, h, (w * 4));
    else
	pixbuf->art_pixbuf = art_pixbuf_new_rgb(pixels, w, h, (w * 3));
153

154
    /* Ok, I'm anal...shoot me */
155 156 157
    if (!(pixbuf->art_pixbuf)) {
        art_free(pixels);
        g_free(pixbuf);
158
	return NULL;
159 160
    }

161 162
    pixbuf->ref_count = 0;
    pixbuf->unref_func = NULL;
163

164
    return pixbuf;
165
}
166 167 168 169

int image_save(GdkPixBuf *pixbuf, FILE *file)
{
     png_structp,