PNGのデータ部分の読み取りにはpng_read_png()という関数を使います*1。APIの解説は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
長々と書きましたが、参考になれば。