#include <stdio.h> #include <stdlib.h> #include <png.h> #if 0 #define LOG(x...) fprintf(stderr,"error: " x) #else #define LOG(x...) do {} while (0) #endif void *loadpng(const char *fn, unsigned *_width, unsigned *_height) { FILE *fp = 0; unsigned char header[8]; unsigned char *data = 0; unsigned char **rowptrs = 0; png_structp p = 0; png_infop pi = 0; png_uint_32 width, height; int bitdepth, colortype, imethod, cmethod, fmethod, i; p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if(p == 0) { LOG("%s: failed to allocate png read struct\n", fn); return 0; } pi = png_create_info_struct(p); if(pi == 0) { LOG("%s: failed to allocate png info struct\n", fn); goto oops; } fp = fopen(fn, "rb"); if(fp == 0) { LOG("%s: failed to open file\n", fn); return 0; } if(fread(header, 8, 1, fp) != 1) { LOG("%s: failed to read header\n", fn); goto oops; } if(png_sig_cmp(header, 0, 8)) { LOG("%s: header is not a PNG header\n", fn); goto oops; } if(setjmp(png_jmpbuf(p))) { LOG("%s: png library error\n", fn); oops: png_destroy_read_struct(&p, &pi, 0); if(fp != 0) fclose(fp); if(data != 0) free(data); if(rowptrs != 0) free(rowptrs); return 0; } png_init_io(p, fp); png_set_sig_bytes(p, 8); png_read_info(p, pi); png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype, &imethod, &cmethod, &fmethod); // printf("PNG: %d x %d (d=%d, c=%d)\n", // width, height, bitdepth, colortype); switch(colortype){ case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(p); break; case PNG_COLOR_TYPE_RGB: if(png_get_valid(p, pi, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(p); } else { png_set_filler(p, 0xff, PNG_FILLER_AFTER); } break; case PNG_COLOR_TYPE_RGB_ALPHA: break; case PNG_COLOR_TYPE_GRAY: if(bitdepth < 8) { png_set_gray_1_2_4_to_8(p); } default: LOG("%s: unsupported (grayscale?) color type\n"); goto oops; } if(bitdepth == 16) { png_set_strip_16(p); } data = (unsigned char*) malloc((width * 4) * height); rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height); if((data == 0) || (rowptrs == 0)){ LOG("could not allocate data buffer\n"); goto oops; } for(i = 0; i < height; i++) { rowptrs[i] = data + ((width * 4) * i); } png_read_image(p, rowptrs); png_destroy_read_struct(&p, &pi, 0); fclose(fp); if(rowptrs != 0) free(rowptrs); *_width = width; *_height = height; return (void*) data; } typedef struct { const unsigned char* base; const unsigned char* end; const unsigned char* cursor; } PngReader; static void png_reader_read_data( png_structp png_ptr, png_bytep data, png_size_t length ) { PngReader* reader = png_get_io_ptr(png_ptr); png_size_t avail = (png_size_t)(reader->end - reader->cursor); if (avail > length) avail = length; memcpy( data, reader->cursor, avail ); reader->cursor += avail; } void *readpng(const unsigned char *base, size_t size, unsigned *_width, unsigned *_height) { PngReader reader; unsigned char *data = 0; unsigned char **rowptrs = 0; png_structp p = 0; png_infop pi = 0; png_uint_32 width, height; int bitdepth, colortype, imethod, cmethod, fmethod, i; p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if(p == 0) { LOG("%s: failed to allocate png read struct\n", fn); return 0; } pi = png_create_info_struct(p); if(pi == 0) { LOG("%s: failed to allocate png info struct\n", fn); goto oops; } reader.base = base; reader.end = base + size; reader.cursor = base; if(size < 8 || png_sig_cmp((unsigned char*)base, 0, 8)) { LOG("%s: header is not a PNG header\n", fn); goto oops; } reader.cursor += 8; if(setjmp(png_jmpbuf(p))) { LOG("%s: png library error\n", fn); oops: png_destroy_read_struct(&p, &pi, 0); if(data != 0) free(data); if(rowptrs != 0) free(rowptrs); return 0; } png_set_read_fn (p, &reader, png_reader_read_data); png_set_sig_bytes(p, 8); png_read_info(p, pi); png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype, &imethod, &cmethod, &fmethod); // printf("PNG: %d x %d (d=%d, c=%d)\n", // width, height, bitdepth, colortype); switch(colortype){ case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(p); break; case PNG_COLOR_TYPE_RGB: if(png_get_valid(p, pi, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(p); } else { png_set_filler(p, 0xff, PNG_FILLER_AFTER); } break; case PNG_COLOR_TYPE_RGB_ALPHA: break; case PNG_COLOR_TYPE_GRAY: if(bitdepth < 8) { png_set_gray_1_2_4_to_8(p); } default: LOG("%s: unsupported (grayscale?) color type\n"); goto oops; } if(bitdepth == 16) { png_set_strip_16(p); } data = (unsigned char*) malloc((width * 4) * height); rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height); if((data == 0) || (rowptrs == 0)){ LOG("could not allocate data buffer\n"); goto oops; } for(i = 0; i < height; i++) { rowptrs[i] = data + ((width * 4) * i); } png_read_image(p, rowptrs); png_destroy_read_struct(&p, &pi, 0); if(rowptrs != 0) free(rowptrs); *_width = width; *_height = height; return (void*) data; } #if 0 int main(int argc, char **argv) { unsigned w,h; unsigned char *data; if(argc < 2) return 0; data = loadpng(argv[1], &w, &h); if(data != 0) { printf("w: %d h: %d\n", w, h); } return 0; } #endif