モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

gimpのpngエクスポートとazdrawingについて

この記事には間違った部分が含まれていますが、整合性のために敢えて消さずに残しておきます。誤っている部分は打ち消し線で消してある部分です。

gimpからpngエクスポートした画像が、状況によってはazdrawing 1.4で読み込むと真っ黒になるという症状に見舞われました。inkscapeとかはok。

そこで勉強を兼ねて、png情報を表示するスクリプトpythonで作って調べてみますた…

ポイントは

  • バイト列->数値というか、構造体をpythonのタプルに変換するにはstructモジュールを使う
  • ビッグエンディアンにするにはunpackメソッドの第一引数の先頭に「>」を付ける
#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import sys
import struct

COLOR_TYPE_NAME={ 0:"Grayscale", 2:"Truecolor" , 3:"Index color" , 4:"Grayscale with alpha", 6:"Truecolor with alpha" }

CHUNK_SIGNATURE_PNG=''.join([chr(x) for x in (137,80,78,71,13,10,26,10)])
CHUNK_IHDR=''.join([chr(x)for x in (73,72,68,82)])
CHUNK_PLTE=''.join([chr(x)for x in (80,76,84,69)])
CHUNK_IDAT=''.join([chr(x)for x in (73,68,65,84)])
CHUNK_IEND=''.join([chr(x)for x in (73,69,78,68)])

class PngDecoder:

    def __init__(self):
        self.ifp=None
        pass

    def read_unsigned_int(self):
        src=struct.unpack('>I',self.ifp.read(4))
        return src[0]

    def main(self,fname):
        try:
            print('decoding %s...' % fname)
            self.ifp=open(fname,'rb')
            chunk=self.ifp.read(8)
            if chunk==CHUNK_SIGNATURE_PNG:
                print('png file found')
                self.read_chunks_loop()
        finally:
            self.ifp.close()
        pass

    def read_chunks_loop(self):
        while True:
            length,chunk,body,CRC=self.read_chunk()
            if chunk==CHUNK_IHDR:
                print('-- IHDR --')
                self.read_ihdr(body)
            elif chunk==CHUNK_PLTE:
                print('-- PLTE len:%d --' % length)
                self.read_plte(body)
            elif chunk==CHUNK_IDAT:
                print('-- IDAT len:%d --' % length)
                self.read_idat(body)
            elif chunk==CHUNK_IEND:
                print("*** END ***")
                break

    def read_ihdr(self,body):
        src=struct.unpack('>IIBBBBB',body)
        width,height,bit_depth,color_type,compress_method,filter_method,interace_method=src
        print('size:%dx%d' % (width,height))
        print('bit depth:%d' % bit_depth)
        try:
            print('color type:%s' % COLOR_TYPE_NAME[color_type])
        except KeyError:
            print('color type: undefined (%d)' % color_type)

        print('--methods--')
        print('compress:%d' % compress_method)
        print('filter:%d' % filter_method)
        if interace_method==0:
            pass
        elif interace_method==1:
            print('interaced')
        else:
            print('undefined interace:%d' % interace_method)
        
    def read_plte(self,body):
        print("PLTE reader has not implemented yet")

    def read_idat(self,body):
        print("IDAT reader has not implemented yet")

    def read_chunk(self):
        length=self.read_unsigned_int()
        chunktype=self.ifp.read(4)
        if length > 0:
            body=self.ifp.read(length)
        else:
            body=None
        CRC=self.read_unsigned_int()
        return (length,chunktype,body,CRC)

        pass


if __name__ == '__main__':

    c=PngDecoder()
    c.main(sys.argv[1])

*1

これによると…なんと、gimpの吐き出すpngファイルは8192バイトごとにIDATを切り分け、複数のIDATが連続するという仕様になっていることが判明*2… 他のはそうなってなかったんで、多分ここが原因かな?と。*3

まぁ、ついに、AzDrawing内部を触ってみるかー!と思った時点で次号に続くというかとりあえず今の所困ってない(自分がよくやるワークフローはAzDrawingからgimpであって、それは問題ないんで)どうしたものか。

いつかはAzDrawingにLuaPythonを組み込んでみたい野望があったりなかったり…いつかは。でもSourceforgeに限らずOSSプロジェクトに参加した経験が全くないので、どうすればいいのか全然わからんのですねこれが。まぁやってみて出来たら考えますか…全然出来ない可能性もあるし。

*1:チャンク種別判定回りがあまりにもダサかったので修正

*2:PNGの仕様としては間違ってはいないのだけど。

*3:間違いでした。AZDrawingは複数のIDATに最初から対応しており、問題はありませんでした。