io-png.c 3.49 KB
Newer Older
1
/*
2
 * io-png.c: GdkPixBuf I/O for PNG files.
3 4
 *
 * Author:
5
 *    Mark Crichton <crichton@gimp.org>
6 7 8 9
 *
 */
#include <config.h>
#include <stdio.h>
10
#include <glib.h>
11 12 13 14 15
#include "gdk-pixbuf.h"
#include "gdk-pixbuf-io.h"
#include <png.h>

/* Shared library entry point */
16
GdkPixBuf *image_load(FILE * f)
17
{
18 19 20 21 22 23 24
	png_structp png_ptr;
	png_infop info_ptr, end_info;
	gint i, depth, ctype, inttype, passes;
	png_uint_32 w, h, x, y;
	png_bytepp rows;
	art_u8 *pixels, *temp, *rowdata;
	GdkPixBuf *pixbuf;
25

26
	g_return_val_if_fail (f != NULL, NULL);
27

28 29 30 31 32 33
	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);
34 35 36
		return NULL;
	}

37 38 39 40
	end_info = png_create_info_struct (png_ptr);
	if (!end_info) {
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		return NULL;
41 42
	}

43 44
	if (setjmp(png_ptr->jmpbuf)) {
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
45 46 47
		return NULL;
	}

48 49
	png_init_io(png_ptr, f);
	png_read_info(png_ptr, info_ptr);
50

51 52
	png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
		     NULL, NULL);
53

54 55 56 57 58
	/* 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.
59 60
	 */

61 62
	if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
		png_set_expand(png_ptr);
63

64 65
	if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
		png_set_expand(png_ptr);
66

67 68
	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
		png_set_expand(png_ptr);
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	if (depth == 16)
		png_set_strip_16(png_ptr);

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

	/* Add filler bits to non-alpha PNGs */
	/* make it 255, full opaque */
	if (depth == 8 && ctype == PNG_COLOR_TYPE_RGB)
		png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);

	/* 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);

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

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

	/* Allocate some memory and set up row array */
	/* This "inhales vigirously"... */
	pixels = g_malloc(w*h*4);
	rows = g_malloc(h*sizeof(png_bytep));

	if ((!pixels) || (!rows)) {
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
99 100 101
		return NULL;
	}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	/* Icky code, but it has to be done... */
	for (i = 0; i < h; i++) {
		if ((rows[i] = g_malloc(w*sizeof(art_u8)*4)) == NULL) {
			int n;
			for (n = 0; n < i; n++)
				g_free(rows[i]);
			g_free(rows);
			png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
			return NULL;
		}
	}
	
	/* And we FINALLY get here... */
	png_read_image(png_ptr, rows);
	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
	
	/* Now stuff the bytes into pixels & free rows[y] */
	/* RGBA order */
	temp = pixels;
	for (y = 0; y < h; y++) {
		(png_bytep)rowdata = rows[y];
		for (x = 0; x < w; x++) {
			temp[0] = rowdata[(x*4)];
			temp[1] = rowdata[(x*4)+1];
			temp[2] = rowdata[(x*4)+2];
			temp[3] = rowdata[(x*4)+3];
			temp += 4;
		}
		g_free(rows[y]);
	}
	g_free(rows);

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

	pixbuf->art_pixbuf = art_pixbuf_new_rgba (pixels, w, h, (w*4));

	/* Ok, I'm anal...shoot me */
	if (!(pixbuf->art_pixbuf))
		return NULL;
	pixbuf->ref_count = 0;
	pixbuf->unref_func = NULL;

	return pixbuf;
146
}