1#include <stdio.h>
2#include <stdlib.h>
3
4#include <png.h>
5
6#if 0
7#define LOG(x...) fprintf(stderr,"error: " x)
8#else
9#define LOG(x...) do {} while (0)
10#endif
11
12void *loadpng(const char *fn, unsigned *_width, unsigned *_height)
13{
14    FILE *fp = 0;
15    unsigned char header[8];
16    unsigned char *data = 0;
17    unsigned char **rowptrs = 0;
18    png_structp p = 0;
19    png_infop pi = 0;
20
21    png_uint_32 width, height;
22    int bitdepth, colortype, imethod, cmethod, fmethod, i;
23
24    p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
25    if(p == 0) {
26        LOG("%s: failed to allocate png read struct\n", fn);
27        return 0;
28    }
29
30    pi = png_create_info_struct(p);
31    if(pi == 0) {
32        LOG("%s: failed to allocate png info struct\n", fn);
33        goto oops;
34    }
35
36    fp = fopen(fn, "rb");
37    if(fp == 0) {
38        LOG("%s: failed to open file\n", fn);
39        return 0;
40    }
41
42    if(fread(header, 8, 1, fp) != 1) {
43        LOG("%s: failed to read header\n", fn);
44        goto oops;
45    }
46
47    if(png_sig_cmp(header, 0, 8)) {
48        LOG("%s: header is not a PNG header\n", fn);
49        goto oops;
50    }
51
52    if(setjmp(png_jmpbuf(p))) {
53        LOG("%s: png library error\n", fn);
54    oops:
55        png_destroy_read_struct(&p, &pi, 0);
56        if(fp != 0) fclose(fp);
57        if(data != 0) free(data);
58        if(rowptrs != 0) free(rowptrs);
59        return 0;
60    }
61
62    png_init_io(p, fp);
63    png_set_sig_bytes(p, 8);
64
65    png_read_info(p, pi);
66
67    png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype,
68                 &imethod, &cmethod, &fmethod);
69//    printf("PNG: %d x %d (d=%d, c=%d)\n",
70//           width, height, bitdepth, colortype);
71
72    switch(colortype){
73    case PNG_COLOR_TYPE_PALETTE:
74        png_set_palette_to_rgb(p);
75        break;
76
77    case PNG_COLOR_TYPE_RGB:
78        if(png_get_valid(p, pi, PNG_INFO_tRNS)) {
79            png_set_tRNS_to_alpha(p);
80        } else {
81            png_set_filler(p, 0xff, PNG_FILLER_AFTER);
82        }
83        break;
84
85    case PNG_COLOR_TYPE_RGB_ALPHA:
86        break;
87
88    case PNG_COLOR_TYPE_GRAY:
89        if(bitdepth < 8) {
90            png_set_gray_1_2_4_to_8(p);
91        }
92
93    default:
94        LOG("%s: unsupported (grayscale?) color type\n");
95        goto oops;
96    }
97
98    if(bitdepth == 16) {
99        png_set_strip_16(p);
100    }
101
102    data = (unsigned char*) malloc((width * 4) * height);
103    rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height);
104
105    if((data == 0) || (rowptrs == 0)){
106        LOG("could not allocate data buffer\n");
107        goto oops;
108    }
109
110    for(i = 0; i < height; i++) {
111        rowptrs[i] = data + ((width * 4) * i);
112    }
113
114    png_read_image(p, rowptrs);
115
116    png_destroy_read_struct(&p, &pi, 0);
117    fclose(fp);
118    if(rowptrs != 0) free(rowptrs);
119
120    *_width = width;
121    *_height = height;
122
123    return (void*) data;
124}
125
126
127typedef struct
128{
129    const unsigned char*  base;
130    const unsigned char*  end;
131    const unsigned char*  cursor;
132
133} PngReader;
134
135static void
136png_reader_read_data( png_structp  png_ptr,
137                      png_bytep   data,
138                      png_size_t  length )
139{
140  PngReader* reader = png_get_io_ptr(png_ptr);
141  png_size_t avail  = (png_size_t)(reader->end - reader->cursor);
142
143  if (avail > length)
144      avail = length;
145
146  memcpy( data, reader->cursor, avail );
147  reader->cursor += avail;
148}
149
150
151void *readpng(const unsigned char *base, size_t   size, unsigned *_width, unsigned *_height)
152{
153    PngReader  reader;
154    unsigned char *data = 0;
155    unsigned char **rowptrs = 0;
156    png_structp p = 0;
157    png_infop pi = 0;
158
159    png_uint_32 width, height;
160    int bitdepth, colortype, imethod, cmethod, fmethod, i;
161
162    p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
163    if(p == 0) {
164        LOG("%s: failed to allocate png read struct\n", fn);
165        return 0;
166    }
167
168    pi = png_create_info_struct(p);
169    if(pi == 0) {
170        LOG("%s: failed to allocate png info struct\n", fn);
171        goto oops;
172    }
173
174    reader.base   = base;
175    reader.end    = base + size;
176    reader.cursor = base;
177
178    if(size < 8 || png_sig_cmp((unsigned char*)base, 0, 8)) {
179        LOG("%s: header is not a PNG header\n", fn);
180        goto oops;
181    }
182
183    reader.cursor += 8;
184
185    if(setjmp(png_jmpbuf(p))) {
186        LOG("%s: png library error\n", fn);
187    oops:
188        png_destroy_read_struct(&p, &pi, 0);
189        if(data != 0) free(data);
190        if(rowptrs != 0) free(rowptrs);
191        return 0;
192    }
193
194    png_set_read_fn (p, &reader, png_reader_read_data);
195    png_set_sig_bytes(p, 8);
196
197    png_read_info(p, pi);
198
199    png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype,
200                 &imethod, &cmethod, &fmethod);
201//    printf("PNG: %d x %d (d=%d, c=%d)\n",
202//           width, height, bitdepth, colortype);
203
204    switch(colortype){
205    case PNG_COLOR_TYPE_PALETTE:
206        png_set_palette_to_rgb(p);
207        break;
208
209    case PNG_COLOR_TYPE_RGB:
210        if(png_get_valid(p, pi, PNG_INFO_tRNS)) {
211            png_set_tRNS_to_alpha(p);
212        } else {
213            png_set_filler(p, 0xff, PNG_FILLER_AFTER);
214        }
215        break;
216
217    case PNG_COLOR_TYPE_RGB_ALPHA:
218        break;
219
220    case PNG_COLOR_TYPE_GRAY:
221        if(bitdepth < 8) {
222            png_set_gray_1_2_4_to_8(p);
223        }
224
225    default:
226        LOG("%s: unsupported (grayscale?) color type\n");
227        goto oops;
228    }
229
230    if(bitdepth == 16) {
231        png_set_strip_16(p);
232    }
233
234    data    = (unsigned char*) malloc((width * 4) * height);
235    rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height);
236
237    if((data == 0) || (rowptrs == 0)){
238        LOG("could not allocate data buffer\n");
239        goto oops;
240    }
241
242    for(i = 0; i < height; i++) {
243        rowptrs[i] = data + ((width * 4) * i);
244    }
245
246    png_read_image(p, rowptrs);
247
248    png_destroy_read_struct(&p, &pi, 0);
249    if(rowptrs != 0) free(rowptrs);
250
251    *_width = width;
252    *_height = height;
253
254    return (void*) data;
255}
256
257
258#if 0
259int main(int argc, char **argv)
260{
261    unsigned w,h;
262    unsigned char *data;
263
264    if(argc < 2) return 0;
265
266
267    data = loadpng(argv[1], &w, &h);
268
269    if(data != 0) {
270        printf("w: %d  h: %d\n", w, h);
271    }
272
273    return 0;
274}
275#endif
276