そんなわけで、libpngのリファレンスとソースコードをためつすがめつ*1作ってみました。グラフに入っているメタデータはIHDR、PLTE、tRNSの3つのchunkなので、その情報を表示させてみることにします。ちょっと長いかもですが、難しいことは何もやってないです。
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <png.h> #define MAX_FILENAME_LEN 256 #define MAX_MSG_LEN 256 #define PNG_BYTES_TO_CHECK 8 void read_png_to_bit(char *); void print_information(png_structp, png_infop); void fail_and_destruct(const char *, FILE *, png_structp *, png_infop *, png_infop *); void destruct(FILE *, png_structp *, png_infop *, png_infop *); int main(int argc, char **argv){ char filename[MAX_FILENAME_LEN]; strncpy(filename, ((argc < 2) ? "sample.png" : argv[1]), MAX_FILENAME_LEN); read_png_to_bit(filename); return EXIT_SUCCESS; } void read_png_to_bit(char *filename) { FILE *fp; char sig[PNG_BYTES_TO_CHECK], msg[MAX_MSG_LEN]; int i; png_structp png_ptr = NULL; png_infop info_ptr = NULL; printf("input file: [%s]\n", filename); if((fp=fopen(filename, "rb")) == NULL){ sprintf(msg, "%d:%s", errno, strerror(errno)); fail_and_destruct(msg, NULL, NULL, NULL, NULL); } /* signature check */ if(fread(sig, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK){ sprintf(msg, "%d:%s", errno, strerror(errno)); fail_and_destruct(msg, fp, NULL, NULL, NULL); } printf("png signature:"); for(i=0; i<PNG_BYTES_TO_CHECK; i++){ printf(" %02x", 0x000000FF & sig[i]); } printf("\n"); if(png_sig_cmp(sig, 0, PNG_BYTES_TO_CHECK) != 0){ sprintf(msg, "%s is not a valid PNG file.", filename); fail_and_destruct(msg, fp, NULL, NULL, NULL); } /* create read structure */ if((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL){ fail_and_destruct("can't allocate for png_struct.", fp, NULL, NULL, NULL); } /* create info structure */ if((info_ptr = png_create_info_struct(png_ptr)) == NULL){ fail_and_destruct("can't allocate for png_info.", fp, &png_ptr, NULL, NULL); } /* set exception handler */ if(setjmp(png_ptr->jmpbuf)){ fail_and_destruct("internal error.", fp, &png_ptr, &info_ptr, NULL); } /* read */ png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); png_init_io(png_ptr, fp); print_information(png_ptr, info_ptr); destruct(fp, &png_ptr, &info_ptr, NULL); } void print_information(png_structp png_ptr, png_infop info_ptr) { int i; png_uint_32 width=0, height=0; int bit_depth=0, color_type=0, interlace_method=0, compression_method=0, filter_method=0; png_colorp palette=NULL; int num_palette=0; png_bytep trans=NULL; int num_trans=0; png_color_16p trans_values=NULL; png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, &compression_method, &filter_method); printf("\n"); printf("chunk IHDR:\n"); printf("---------------------------------\n"); printf("width :%lu\n", width); printf("height :%lu\n", height); printf("bit_depth :%d\n", bit_depth); printf("color_type :%d ", color_type); switch(color_type){ case PNG_COLOR_TYPE_GRAY : printf("(PNG_COLOR_TYPE_GRAY)\n"); break; case PNG_COLOR_TYPE_PALETTE : printf("(PNG_COLOR_TYPE_PALETTE)\n"); break; case PNG_COLOR_TYPE_RGB : printf("(PNG_COLOR_TYPE_RGB)\n"); break; case PNG_COLOR_TYPE_RGB_ALPHA : printf("(PNG_COLOR_TYPE_RGB_ALPHA)\n"); break; case PNG_COLOR_TYPE_GRAY_ALPHA : printf("(PNG_COLOR_TYPE_GRAY_ALPHA)\n"); break; default: printf("(UNKNOWN)\n"); break; } printf("interlace_method :%d ", interlace_method); switch(interlace_method){ case PNG_INTERLACE_NONE : printf("(PNG_INTERLACE_NONE)\n"); break; case PNG_INTERLACE_ADAM7 : printf("(PNG_INTERLACE_ADAM7)\n"); break; case PNG_INTERLACE_LAST : printf("(PNG_INTERLACE_LAST)\n"); break; default: printf("(UNKNOWN)\n"); break; } printf("compression_method :%d ", compression_method); switch(compression_method){ /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ case PNG_COMPRESSION_TYPE_BASE : printf("(PNG_COMPRESSION_TYPE_BASE)\n"); break; default: printf("(UNKNOWN)\n"); break; } printf("filter_method :%d ", filter_method); switch(filter_method){ /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ case PNG_FILTER_TYPE_BASE : printf("(PNG_FILTER_TYPE_BASE)\n"); break; default: printf("(UNKNOWN)\n"); break; } printf("\n"); png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); printf("chunk PLTE:\n"); printf("---------------------------------\n"); printf("entries: %d\n", num_palette); for(i=0; i<num_palette; i++, palette++){ printf(" %d: (%02x, %02x, %02x)\n", i, palette->red, palette->green, palette->blue); } printf("\n"); png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values); printf("chunk tRNS:\n"); printf("---------------------------------\n"); printf("values(for palette): %d\n", num_trans); for(i=0; i<num_trans; i++){ printf(" %d: %d\n", i, trans[i]); } printf("chunk tRNS: "); if(png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values)){ printf("\n---------------------------------\n"); printf("values(for palette): %d\n", num_trans); for(i=0; i<num_trans; i++){ printf(" %d: %d\n", i, trans[i]); } printf("trans_values(for non-palette):\n"); printf(" index =%02x\n", trans_values->index); printf(" red =%04x\n", trans_values->red); printf(" green =%04x\n", trans_values->green); printf(" blue =%04x\n", trans_values->blue); printf(" gray =%04x\n", trans_values->gray); } else{ printf("NONE"); } printf("\n"); } void fail_and_destruct(const char * msg, FILE *fp, png_structp *png_ptr, png_infop *info_ptr, png_infop *end_ptr) { fprintf(stderr, "%s\n", msg); destruct(fp, png_ptr, info_ptr, end_ptr); exit(EXIT_FAILURE); } void destruct(FILE *fp, png_structp *png_ptr, png_infop *info_ptr, png_infop *end_ptr) { if(fp != NULL){ fclose(fp); } if(png_ptr != NULL){ png_destroy_read_struct(png_ptr, info_ptr, end_ptr); } }
実行結果
$ gcc -Wall -o png png.c -lpng #libpngをリンクする $ ./png nana.png input file: [nana.png] png signature: 89 50 4e 47 0d 0a 1a 0a chunk IHDR: --------------------------------- width :400 height :200 bit_depth :4 color_type :3 (PNG_COLOR_TYPE_PALETTE) interlace_method :1 (PNG_INTERLACE_ADAM7) compression_method :0 (PNG_COMPRESSION_TYPE_BASE) filter_method :0 (PNG_FILTER_TYPE_BASE) chunk PLTE: --------------------------------- entries: 6 0: (ff, ff, ff) 1: (00, ff, 00) 2: (ff, 00, 00) 3: (7f, 7f, 7f) 4: (00, 00, 00) 5: (52, 79, e7) chunk tRNS: --------------------------------- values(for palette): 1 0: 0 trans_values(for non-palette): index =00 red =0000 green =0000 blue =0000 gray =0000
ついでにその他のPNGファイルも試してみる。
$ ./png 1bit-1ch.png input file: [1bit-1ch.png] png signature: 89 50 4e 47 0d 0a 1a 0a chunk IHDR: --------------------------------- width :12 height :12 bit_depth :1 color_type :3 (PNG_COLOR_TYPE_PALETTE) interlace_method :0 (PNG_INTERLACE_NONE) compression_method :0 (PNG_COMPRESSION_TYPE_BASE) filter_method :0 (PNG_FILTER_TYPE_BASE) chunk PLTE: --------------------------------- entries: 2 0: (ff, ff, ff) 1: (00, 00, 00) chunk tRNS: NONE
$ ./png 4bit-1ch.png input file: [4bit-1ch.png] png signature: 89 50 4e 47 0d 0a 1a 0a chunk IHDR: --------------------------------- width :12 height :12 bit_depth :4 color_type :3 (PNG_COLOR_TYPE_PALETTE) interlace_method :0 (PNG_INTERLACE_NONE) compression_method :0 (PNG_COMPRESSION_TYPE_BASE) filter_method :0 (PNG_FILTER_TYPE_BASE) chunk PLTE: --------------------------------- entries: 10 0: (00, 66, ff) 1: (ff, 17, 17) 2: (ef, 17, 83) 3: (ef, 17, 95) 4: (33, 66, ff) 5: (17, d1, ff) 6: (ef, a0, 17) 7: (66, ef, 17) 8: (d2, ef, 17) 9: (ff, ff, ff) chunk tRNS: NONE
$ ./png 8bit-4ch.png input file: [8bit-4ch.png] png signature: 89 50 4e 47 0d 0a 1a 0a chunk IHDR: --------------------------------- width :12 height :12 bit_depth :8 color_type :6 (PNG_COLOR_TYPE_RGB_ALPHA) interlace_method :0 (PNG_INTERLACE_NONE) compression_method :0 (PNG_COMPRESSION_TYPE_BASE) filter_method :0 (PNG_FILTER_TYPE_BASE) chunk PLTE: --------------------------------- entries: 0 chunk tRNS: NONE
やっぱりblogに貼ると長い。でもとりあえずメタデータの取り出し方は押さえられたような感じですかね・・・。続いてはいよいよ実データの取り扱いに入ろうかと思います。