2006年声優言及数(3)グラフ画像・メタデータ読み取り

そんなわけで、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に貼ると長い。でもとりあえずメタデータの取り出し方は押さえられたような感じですかね・・・。続いてはいよいよ実データの取り扱いに入ろうかと思います。

*1:ハルヒでよく見る表現(笑)