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

PNGのデータ部分の読み取りにはpng_read_png()という関数を使います*1APIの解説はlibpngのマニュアル含めてWebに山ほど情報があるんですけど、実際にデータがどのように入るかについて解説している資料がほとんどなかったので、まとめてみることにします。分かってしまえば当たり前のことではあるんですけどね。

libpngにおいてpngのデータ部分は、png_byte型の配列として取り扱います。Cygwin版では、png_byte型はunsigned charとして定義されているようです。

typedef unsigned char png_byte;

マニュアルには明記されていないようですが、png_byteは1バイトの大きさを持つことになっているようです。

8bit

8bit RGBであれば1つのpng_byte型のデータにR,G,Bがそれぞれ1つずつ入ることになります。

R  G  B   R  G  B
11 22 33  44 55 66
--------  --------
(1pixel)  (1pixel)

つまり2ピクセルのデータであれば、2*3=6個のpng_byte型が必要になります。RGBAの場合は当然2*4=8個になります。

4/2/1bit

逆に8bit以下の場合はというと、これは8bitに入るようにパックされます。例えば4bit paletteの場合は、1つのpng_byte型の中に2つのピクセル情報を持つことになります。2bitなら4つ、1bitなら8つパックされます。

23 -> パレット番号2、パレット番号3のピクセルデータ

実装

それなりに汎用性を考えるとこんな感じになるかと思います。パックされたデータを読むには、png_byteをbit_depthずつシフトさせて読んでいきます。

#define PNG_BITS (sizeof(png_byte)*8)
int i, j, k, l;
png_bytepp row_pointers;
png_uint_32 width=0, height=0;
int bit_depth=0, color_type=0, interlace_method=0, compression_method=0, filter_method=0;
int channels, pack, full_bytes, mod_pxs, mask;

png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
print_information(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, &compression_method, &filter_method);
channels = png_get_channels(png_ptr, info_ptr);
pack = PNG_BITS/bit_depth, full_bytes = width/pack, mod_pxs = width%pack;
mask = ~(~0 << bit_depth);
printf("pack=%d, bytes=%d, mod=%d\n", pack, full_bytes, mod_pxs);

row_pointers = png_get_rows(png_ptr, info_ptr);
for(i=0; i<height; i++){
     for(j=0; j<full_bytes; j++){
          for(k=0; k<pack; k++){
               for(l=0; l<channels; l++){
                    printf("%02x", (row_pointers[i][j*channels+l] >> (PNG_BITS - bit_depth*(k+1))) & mask);
               }
               putchar(' ');
          }
     }
     for(k=0; k<mod_pxs; k++){ /* if 8bit or 16 bit, mod_pxs should be zero */
          printf("%02x ", (row_pointers[i][j] >> (PNG_BITS - bit_depth*(k+1))) & mask);
     }
     putchar('\n');
}

実行例-1bit

$ ./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
pack=8, bytes=1, mod=4
00 00 00 00 01 01 00 00 00 00 00 00
00 01 00 00 00 01 00 00 00 00 00 00
00 00 01 01 01 01 01 01 01 00 00 00
00 00 00 00 00 01 00 00 00 00 00 00
00 00 00 00 01 01 01 01 01 00 00 00
00 00 00 01 01 00 00 01 00 01 00 00
00 00 01 00 01 00 00 01 00 00 01 00
00 01 00 00 01 00 01 00 00 00 01 00
00 01 00 00 01 01 00 00 00 00 01 00
00 01 00 01 00 01 00 00 00 01 00 00
00 00 01 00 00 00 00 00 01 00 00 00
00 00 00 00 00 00 01 01 00 00 00 00

パレット番号0が白(FF,FF,FF)を表しているようなので、見やすくするためにスペースに変換してみます。

$ ./png 1bit-1ch.png | sed 's/00/  /g' | tail -12
            01 01
   01          01
      01 01 01 01 01 01 01
               01
            01 01 01 01 01
         01 01       01    01
      01    01       01       01
   01       01    01          01
   01       01 01             01
   01    01    01          01
      01                01
                  01 01

実行例-2bit

2bit。

$ ./png 2bit-1ch.png
input file: [2bit-1ch.png]
png signature: 89 50 4e 47 0d 0a 1a 0a

chunk IHDR:
---------------------------------
width              :11
height             :12
bit_depth          :2
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: 4
  0: (00, 66, ff)
  1: (ff, 17, 17)
  2: (33, 66, ff)
  3: (ff, ff, ff)

chunk tRNS: NONE
pack=4, bytes=2, mod=3
03 03 03 03 02 02 03 03 03 03 03
03 02 03 03 03 00 03 03 03 03 03
03 03 01 02 02 01 02 00 02 03 03
03 03 03 03 03 02 03 03 03 03 03
03 03 03 03 02 02 02 02 00 03 03
03 03 03 00 01 03 03 02 03 01 03
03 03 02 03 02 03 03 00 03 03 02
03 01 03 03 02 03 02 03 03 03 00
03 00 03 03 01 02 03 03 03 03 02
03 01 03 00 03 00 03 03 03 01 03
03 03 02 03 03 03 03 03 01 03 03
03 03 03 03 03 03 00 02 03 03 03

パレット番号3が白。

$ ./png 2bit-1ch.png | sed 's/03/  /g' | tail -12
            02 02
   02          00
      01 02 02 01 02 00 02
               02
            02 02 02 02 00
         00 01       02    01
      02    02       00       02
   01       02    02          00
   00       01 02             02
   01    00    00          01
      02                01
                  00 02

実行例-4bit

4bit。

$ ./png 4bit-1ch.png
input file: [4bit-1ch.png]
png signature: 89 50 4e 47 0d 0a 1a 0a

chunk IHDR:
---------------------------------
width              :11
height             :12
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: 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
pack=2, bytes=5, mod=1
09 09 09 09 04 04 09 09 09 09 09
09 04 09 09 09 00 09 09 09 09 09
09 09 01 04 04 01 04 05 04 09 09
09 09 09 09 09 04 09 09 09 09 09
09 09 09 09 04 03 04 04 00 09 09
09 09 09 07 01 09 09 04 09 01 09
09 09 04 09 04 09 09 00 09 09 04
09 01 09 09 06 09 04 09 09 09 02
09 00 09 09 01 04 09 09 09 09 04
09 01 09 00 09 08 09 09 09 01 09
09 09 04 09 09 09 09 09 01 09 09
09 09 09 09 09 09 00 04 09 09 09

4bitのパレットとなるとさすがに数が多い。

$ ./png 4bit-1ch.png | sed 's/09/  /g' | tail -12
            04 04
   04          00
      01 04 04 01 04 05 04
               04
            04 03 04 04 00
         07 01       04    01
      04    04       00       04
   01       06    04          02
   00       01 04             04
   01    00    08          01
      04                01
                  00 04

実行例-8bit RGB

8bit。

$ ./png 8bit-3ch.png
input file: [8bit-3ch.png]
png signature: 89 50 4e 47 0d 0a 1a 0a

chunk IHDR:
---------------------------------
width              :11
height             :12
bit_depth          :8
color_type         :2 (PNG_COLOR_TYPE_RGB)
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
pack=1, bytes=11, mod=0
ffffff ffffff ffffff ffffff 3366ff 3366ff ffffff ffffff ffffff ffffff ffffff
ffffff 3366ff ffffff ffffff ffffff 0066ff ffffff ffffff ffffff ffffff ffffff
ffffff ffffff ff1717 3366ff 3366ff ff1717 3366ff 0066ff 3366ff ffffff ffffff
ffffff ffffff ffffff ffffff ffffff 3366ff ffffff ffffff ffffff ffffff ffffff
ffffff ffffff ffffff ffffff 3366ff 3366ff 3366ff 3366ff 0066ff ffffff ffffff
ffffff ffffff ffffff 0066ff ff1717 ffffff ffffff 3366ff ffffff ff1717 ffffff
ffffff ffffff 3366ff ffffff 3366ff ffffff ffffff 0066ff ffffff ffffff 3366ff
ffffff ff1717 ffffff ffffff 3366ff ffffff 3366ff ffffff ffffff ffffff 0066ff
ffffff 0066ff ffffff ffffff ff1717 3366ff ffffff ffffff ffffff ffffff 3366ff
ffffff ff1717 ffffff 0066ff ffffff 0066ff ffffff ffffff ffffff ff1717 ffffff
ffffff ffffff 3366ff ffffff ffffff ffffff ffffff ffffff ff1717 ffffff ffffff
ffffff ffffff ffffff ffffff ffffff ffffff 0066ff 3366ff ffffff ffffff ffffff

RGBなのでパレットデータは無し。普通にffffffが白になります。

$ ./png 8bit-3ch.png | sed 's/ffffff/      /g' | tail -12
                            3366ff 3366ff
       3366ff                      0066ff
              ff1717 3366ff 3366ff ff1717 3366ff 0066ff 3366ff
                                   3366ff
                            3366ff 3366ff 3366ff 3366ff 0066ff
                     0066ff ff1717               3366ff        ff1717
              3366ff        3366ff               0066ff               3366ff
       ff1717               3366ff        3366ff                      0066ff
       0066ff               ff1717 3366ff                             3366ff
       ff1717        0066ff        0066ff                      ff1717
              3366ff                                    ff1717
                                          0066ff 3366ff

横長い。

実行例-8bit RGBA

ラストはアルファチャンネルを含んだRGBAデータ。

$ ./png 8bit-4ch.png
input file: [8bit-4ch.png]
png signature: 89 50 4e 47 0d 0a 1a 0a

chunk IHDR:
---------------------------------
width              :11
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
pack=1, bytes=11, mod=0
ffffff7f ffffff7f ffffff7f ffffff7f 3366ff7f 3366ff7f ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f
ffffff7f 3366ff7f ffffff7f ffffff7f ffffff7f 0066ff7f ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f
ffffff7f ffffff7f ff17177f 3366ff7f 3366ff7f ff17177f 3366ff7f 0066ff7f 3366ff7f ffffff7f ffffff7f
ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f 3366ff7f ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f
ffffff7f ffffff7f ffffff7f ffffff7f 3366ff7f 3366ff7f 3366ff7f 3366ff7f 0066ff7f ffffff7f ffffff7f
ffffff7f ffffff7f ffffff7f 0066ff7f ff17177f ffffff7f ffffff7f 3366ff7f ffffff7f ff17177f ffffff7f
ffffff7f ffffff7f 3366ff7f ffffff7f 3366ff7f ffffff7f ffffff7f 0066ff7f ffffff7f ffffff7f 3366ff7f
ffffff7f ff17177f ffffff7f ffffff7f 3366ff7f ffffff7f 3366ff7f ffffff7f ffffff7f ffffff7f 0066ff7f
ffffff7f 0066ff7f ffffff7f ffffff7f ff17177f 3366ff7f ffffff7f ffffff7f ffffff7f ffffff7f 3366ff7f
ffffff7f ff17177f ffffff7f 0066ff7f ffffff7f 0066ff7f ffffff7f ffffff7f ffffff7f ff17177f ffffff7f
ffffff7f ffffff7f 3366ff7f ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f ff17177f ffffff7f ffffff7f
ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f ffffff7f 0066ff7f 3366ff7f ffffff7f ffffff7f ffffff7f

最後の7fがアルファなので、ffffff7fが一応白ってことになるかな。

$ ./png 8bit-4ch.png | sed 's/ffffff7f/        /g' | tail -12
                                    3366ff7f 3366ff7f
         3366ff7f                            0066ff7f
                  ff17177f 3366ff7f 3366ff7f ff17177f 3366ff7f 0066ff7f 3366ff7f
                                             3366ff7f
                                    3366ff7f 3366ff7f 3366ff7f 3366ff7f 0066ff7f
                           0066ff7f ff17177f                   3366ff7f          ff17177f
                  3366ff7f          3366ff7f                   0066ff7f                   3366ff7f
         ff17177f                   3366ff7f          3366ff7f                            0066ff7f
         0066ff7f                   ff17177f 3366ff7f                                     3366ff7f
         ff17177f          0066ff7f          0066ff7f                            ff17177f
                  3366ff7f                                              ff17177f
                                                      0066ff7f 3366ff7f

長々と書きましたが、参考になれば。

*1:これは高レベルAPIで、もう少し手順が複雑でカスタマイズ性の高い低レベルAPIも用意されているのですが・・・それはまた後ほど