]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-png.c
a1fef8dfbf6efb8ee3450b0118023eb749ced2f8
[~andy/gtk] / gdk-pixbuf / io-png.c
1 /*
2  * io-png.c: GdkPixBuf I/O for PNG files.
3  *
4  * Author:
5  *    Mark Crichton <crichton@gimp.org>
6  *
7  */
8 #include <config.h>
9 #include <stdio.h>
10 #include <glib.h>
11 #include "gdk-pixbuf.h"
12 #include "gdk-pixbuf-io.h"
13 #include <png.h>
14
15 /* Shared library entry point */
16 GdkPixBuf *image_load(FILE * f)
17 {
18         png_structp png_ptr;
19         png_infop info_ptr, end_info;
20         gint i, depth, ctype, inttype, passes;
21         png_uint_32 w, h, x, y;
22         png_bytepp rows;
23         art_u8 *pixels, *temp, *rowdata;
24         GdkPixBuf *pixbuf;
25
26         g_return_val_if_fail (f != NULL, NULL);
27
28         png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL,
29                   NULL);
30
31         info_ptr = png_create_info_struct (png_ptr);
32         if (!info_ptr) {
33                 png_destroy_read_struct(&png_ptr, NULL, NULL);
34                 return NULL;
35         }
36
37         end_info = png_create_info_struct (png_ptr);
38         if (!end_info) {
39                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
40                 return NULL;
41         }
42
43         if (setjmp(png_ptr->jmpbuf)) {
44                 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
45                 return NULL;
46         }
47
48         png_init_io(png_ptr, f);
49         png_read_info(png_ptr, info_ptr);
50
51         png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &ctype, &inttype,
52                      NULL, NULL);
53
54         /* Ok, we want to work with 24 bit images.
55          * However, PNG can vary depth per channel.
56          * So, we use the png_set_expand function to expand
57          * everything into a format libart expects.
58          * We also use png_set_strip_16 to reduce down to 8 bit/chan.
59          */
60
61         if (ctype == PNG_COLOR_TYPE_PALETTE && depth <= 8)
62                 png_set_expand(png_ptr);
63
64         if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
65                 png_set_expand(png_ptr);
66
67         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
68                 png_set_expand(png_ptr);
69
70         if (depth == 16)
71                 png_set_strip_16(png_ptr);
72
73         /* We also have png "packing" bits into bytes if < 8 */
74         if (depth < 8)
75                 png_set_packing(png_ptr);
76
77         /* Add filler bits to non-alpha PNGs */
78         /* make it 255, full opaque */
79         if (depth == 8 && ctype == PNG_COLOR_TYPE_RGB)
80                 png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
81
82         /* Lastly, if the PNG is greyscale, convert to RGB */
83         if (ctype == PNG_COLOR_TYPE_GRAY || ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
84                 png_set_gray_to_rgb(png_ptr);
85
86         /* ...and if we're interlaced... */
87         passes = png_set_interlace_handling(png_ptr);
88
89         /* Update our info structs */
90         png_read_update_info(png_ptr, info_ptr);
91
92         /* Allocate some memory and set up row array */
93         /* This "inhales vigirously"... */
94         pixels = g_malloc(w*h*4);
95         rows = g_malloc(h*sizeof(png_bytep));
96
97         if ((!pixels) || (!rows)) {
98                 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
99                 return NULL;
100         }
101
102         /* Icky code, but it has to be done... */
103         for (i = 0; i < h; i++) {
104                 if ((rows[i] = g_malloc(w*sizeof(art_u8)*4)) == NULL) {
105                         int n;
106                         for (n = 0; n < i; n++)
107                                 g_free(rows[i]);
108                         g_free(rows);
109                         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
110                         return NULL;
111                 }
112         }
113         
114         /* And we FINALLY get here... */
115         png_read_image(png_ptr, rows);
116         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
117         
118         /* Now stuff the bytes into pixels & free rows[y] */
119         /* RGBA order */
120         temp = pixels;
121         for (y = 0; y < h; y++) {
122                 (png_bytep)rowdata = rows[y];
123                 for (x = 0; x < w; x++) {
124                         temp[0] = rowdata[(x*4)];
125                         temp[1] = rowdata[(x*4)+1];
126                         temp[2] = rowdata[(x*4)+2];
127                         temp[3] = rowdata[(x*4)+3];
128                         temp += 4;
129                 }
130                 g_free(rows[y]);
131         }
132         g_free(rows);
133
134         /* Return the GdkPixBuf */
135         pixbuf = g_new(GdkPixBuf, 1);
136
137         pixbuf->art_pixbuf = art_pixbuf_new_rgba (pixels, w, h, (w*4));
138
139         /* Ok, I'm anal...shoot me */
140         if (!(pixbuf->art_pixbuf))
141                 return NULL;
142         pixbuf->ref_count = 0;
143         pixbuf->unref_func = NULL;
144
145         return pixbuf;
146 }