| #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 |