PIC loader
X68000シリーズを中心に使われていた画像フォーマットPICをGIMPで開くためのプラグイン(Python-fu)です。
メモ
- X68000の15ビット色PICファイルのみの対応です。
- 画像データ内にコメント(アスペクト比指定の文字列[拡張ヘッダ]を含む)があればGIMPの画像コメントとして格納します。(画像メニュー > 画像の情報 で出てくるダイアログの「コメント」タブで確認できます)
- アスペクト比の補正はしません。必要ならGIMPで縦横サイズの変更をしてくださいませ。
ソース
#!/usr/bin/env python # -*- coding: utf-8 -*- from gimpfu import * from struct import * bitCounter = 0 #1ビットずつ読むためのビット位置を表す bitBuffer = '0' #1ビットずつ読むためのバッファ width = 0 height = 0 cBuff = [-1] class ColorTable: def __init__(self, color, next, prev): self.color = color self.next = next self.prev = prev table = [] for i in range(128): table.append(ColorTable(0, i-1, i+1)) table[127].prev = 0 table[0].next = 127 color_p = 0 colorbits = 15 octa = [0]*64 #パレット計算用テーブル def picload(filename, raw_filename): #:param str filename: ファイルパス #:param str raw_filename: 選択ファイル名 #:rtype: gimp.Image #:return: 読み込んだ画像 global colorbits global octa global width global height global cBuff fp = open(filename, 'rb') header = fp.read(3) if not header[0:3] == "PIC": gimp.message("X68 PICファイルではありません") fp.close() return 0 comment = '' a = fp.read(1) while ( a !=chr(0x1A) ): #コメント読み込み comment = comment + a a = fp.read(1) a = "" while ( a!=chr(0) ): #真のコメント終了まで読み飛ばし a = fp.read(1) a=fp.read(1) #予約1バイト読み飛ばし comtype = ord(fp.read(1)) #機種タイプとモード platform = comtype & 0x0F #機種タイプ #0:X68k #1:PC-88VA #2:FM-TOWNS #3:MAC #$F:汎用 mode = comtype >> 4 #機種ごとのモード if (platform != 0): gimp.message("対応していないPICです") return 0 colorbits = unpack('>H', fp.read(2))[0] #色のビット数 width = unpack('>H', fp.read(2))[0] #横サイズ height = unpack('>H', fp.read(2))[0] #縦サイズ if (colorbits == 16): #5bit+1bit を 8bit に for i in range(64): octa[i] = int(round(i * 255 * 1.0 / 63)) else: #5bit を 8bit に octa = [0]*64 for i in range(32): octa[i*2] = int(round(i * 255 * 1.0 / 31)) octa[i*2+1] = octa[i*2] # 連鎖記録バッファ cBuff = [-1]* width * height #GIMPに画像を作成 image = gimp.Image(width, height, RGB) image.filename = filename #レイヤー作成 layer = pdb.gimp_layer_new(image, width, height, RGB, filename, 100, NORMAL_MODE ) image.add_layer(layer, 0) #コメント追加 if ( comment != '' ): comment_utf8 = unicode(comment, 'shift_jis').encode('utf-8') image.attach_new_parasite('gimp-comment',TRUE,comment_utf8) # 展開 x = -1 # 展開中の位置 X y = 0 # 展開中の位置 Y c = 0 # 現在の色(X68) 初期値は0 color =[0,0,0] # 現在の色(GIMP) while ( TRUE ): l = read_len( fp ) # 長さを読む l -= 1 # 次の変化点まで繰り返す while ( l ): # 右端の処理 x += 1 if ( x == width ): y += 1 if ( y >= height ): return image x = 0 gimp.progress_update(float(y)/float(height)) # プログレスバーの更新 # 連鎖点上を通過した時は、現在の色を変更 c = cBuff[y*width+x] if ( c != -1 ): color = x68toGimp( c ) # 現在の色を書き込む layer.set_pixel( x,y, color ) l -= 1 # 右端の処理 x += 1 if ( x == width ): y += 1 if ( y >= height ): return image x = 0 gimp.progress_update(float(y)/float(height)) # プログレスバーの更新 # 新しい色の読み込み c = read_color( fp ) color = x68toGimp( c ) layer.set_pixel( x,y, color ) # 連鎖ありなら、連鎖の展開 if ( bit_load(1, fp) != 0): expand_chain(x, y, c, fp ) # キャッシュから色を取り出し # その色が最新になるように更新する。 def get_color( idx ): global color_p global table if ( color_p != idx ): # まず位置idxをキャッシュから切り離す table[table[idx].prev].next = table[idx].next table[table[idx].next].prev = table[idx].prev # 最新色の次にidxを新たにセット table[table[color_p].prev].next = idx table[idx].prev = table[color_p].prev table[color_p].prev = idx table[idx].next = color_p # 最新色位置を更新 color_p = idx return table[idx].color # 新しい色をキャッシュに登録 def new_color( c ): global color_p global table color_p = table[color_p].prev table[color_p].color = c return ( c ) # 色の読み込み def read_color(fp): global colorbits if ( bit_load(1,fp) == 0 ): # キャッシュミス if ( colorbits == 15 ): c = bit_load(15,fp)<<1 elif ( colorbits == 16 ): c = bit_load(16,fp) return ( new_color( c )) else: # キャッシュヒット return ( get_color( bit_load(7,fp))) # 連鎖の展開 def expand_chain( x, y, c, fp ): global width global height global cBuff while (TRUE): a = bit_load(2,fp) if ( a == 0 ): if ( bit_load(1,fp) == 0 ): # 終わり return if ( bit_load(1,fp) == 0 ): # 左2つ x -= 2 else: # 右2つ x += 2 elif ( a == 1 ): # 左1つ x -= 1 elif ( a == 2 ): # 真下 pass elif ( a == 3 ): # 右1つ x += 1 y += 1 if ( y < height ): #画面を超えていないのなら連鎖を書き込む cBuff[width*y+x] = c # X68色をGIMP色に換算 def x68toGimp( c ): global octa i = c & 0b0000000000000001 g = ( (c & 0b1111100000000000)>>10 ) + i r = ( (c & 0b0000011111000000)>>5 ) + i b = c & 0b0000000000111111 color = [octa[r],octa[g],octa[b]] return color # 長さの読み込み def read_len( fp ): a = 1 while ( bit_load(1,fp) != 0 ): a += 1 return ( bit_load(a,fp) + (1 << a) - 1) # 1ビットずつ読む/ sizeビット分を返す def bit_load( size, fp ): global bitCounter global bitBuffer a = 0 b = 0 size0 = size mask = [ 0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000 ] while ( size > 0 ): if ( bitCounter == 0 ): # 0(空)なら1バイト読む bitBuffer = fp.read(1) bitCounter = 7 else: bitCounter -= 1 n = ord(bitBuffer) & mask[bitCounter] b = 1 if n else 0 a = (a<<1) + b size -= 1 return a # 登録ハンドラ def register_load_handlers(): gimp.register_load_handler('python_fu_PIC_load', 'pic,xpic', '') pdb['gimp-register-file-handler-mime']('python_fu_PIC_load', 'image/xpic') # pdb['gimp-register-thumbnail-loader']('python_fu_PIC_load', '') register( "python_fu_PIC_load", #コマンド名 "X68用PIC形式画像を読み込みます", #プロシージャブラウザに表示されるプラグインに関する情報 "X68用PIC形式画像を読み込みます", #プラグインのヘルプ "Akira M", #プラグインの作成者 "Akira M", #プラグインの著作権保有者 "2017", #年 "X68 PIC", #メニューの中でプラグインに使用されるラベル None, #プラグインで処理する対象となる画像のタイプ [ #input args. Format (type, name, description, default [, extra]) (PF_STRING, 'filename', 'The name of the file to load', None), (PF_STRING, 'raw-filename', 'The name entered', None), ], #プラグインのメソッドのパラメーター [(PF_IMAGE, 'image', 'Output image')], #プラグインのメソッドの結果 Format (type, name, description) picload, on_query = register_load_handlers, menu = "<Load>", ) main()
おまけ: PIC形式ファイルフォーマット
やなぎさわさんによる解説。LHAだったのをZIPで圧縮し直して転載します。