Skip to content

Instantly share code, notes, and snippets.

@chikuchikugonzalez
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chikuchikugonzalez/c5b82e0c53392dcb801c to your computer and use it in GitHub Desktop.
Save chikuchikugonzalez/c5b82e0c53392dcb801c to your computer and use it in GitHub Desktop.
EDGE形式ファイルをGIMPで開くためのGIMPプラグイン (Python-fu)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# GIMP Plug-in for the EDGE file format.
# http://takabosoft.com/edge
#
# Base on EdgeLIB 1.00
# http://takabosoft.com/edge/tool
#
#----------------------------------- LICENSE -----------------------------------
# The MIT License
#
# Copyright (c) 2014 TANAKA Kenichi <chikuchikugonzalez@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#
# 以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフト
# ウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無
# 償で許可します。これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒
# 布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相
# 手に同じことを許可する権利も無制限に含まれます。
#
# 上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分
# に記載するものとします。
#
# ソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証も
# なく提供されます。ここでいう保証とは、商品性、特定の目的への適合性、および権利
# 非侵害についての保証も含みますが、それに限定されるものではありません。 作者ま
# たは著作権者は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起
# 因または関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切
# の請求、損害、その他の義務について何らの責任も負わないものとします。
#----------------------------------- LICENSE -----------------------------------
from __future__ import print_function
from gimpfu import *
import math
import struct
import sys
import traceback
# EDGEファイル読み込み関数.
def load_edge(filename, raw_filename):
"""
EDGE形式ファイルを読み込みます.
:param str filename: ファイルパス
:param str raw_filename: 選択ファイル名
:rtype: gimp.Image
:return: 読み込んだ画像
"""
with open(filename, 'rb') as fp:
header = fp.read(10)
if not header[0:4] == "EDGE":
raise RuntimeException("EDGE形式ファイルじゃないようです")
# 画像サイズとレイヤ数
width, height, numlayers = struct.unpack("<iiH", fp.read(10))
# 透過色インデックス
transcolor = struct.unpack('<B', fp.read(1))[0]
# カラーパレット
palette = struct.unpack('<768B', fp.read(768))
# 画像を準備
image = gimp.Image(width, height, INDEXED)
image.filename = filename
# 背景色は設定しておく
gimp.set_background(palette[(3 * transcolor)], palette[(3 * transcolor) + 1], palette[(3 * transcolor) + 2])
# colormap
# see http://stackoverflow.com/questions/22622370/gimp-script-fu-how-can-i-set-a-value-in-the-colormap-directly
pdb.gimp_image_set_colormap(image, 768, palette)
# レイヤー展開用
def decompress(fp, dstmax):
numcomps = struct.unpack('<i', fp.read(4))[0]
positions = []
lengths = []
values = []
for n in range(numcomps):
position, length, value = struct.unpack('<IIB', fp.read(9))
positions.append(position)
lengths.append(length)
values.append(value)
srcmax = struct.unpack('<i', fp.read(4))[0]
srcbuf = struct.unpack('<%dB' % (srcmax,), fp.read(srcmax))
comp = 0
dest = 0
dstbuf = [0] * dstmax
i = 0
while i <= srcmax:
if (comp < numcomps) and (i == positions[comp]):
dstbuf[dest:dest+(lengths[comp])] = [values[comp]] * lengths[comp]
dest += lengths[comp]
comp += 1
#i -= 1
#i += 1
continue
if i < srcmax:
dstbuf[dest] = srcbuf[i]
dest += 1
i += 1
return dstbuf
# レイヤーを展開
edgelayers = []
numpixels = width * height
#edgelayername_max = 80
for no in range(numlayers):
# レイヤ名を取得
layername = fp.read(80).split('\0')[0]
layervisible = struct.unpack('<?', fp.read(1))[0]
layerdata = decompress(fp, numpixels)
layer = gimp.Layer(image, layername, width, height, INDEXED_IMAGE, 100, NORMAL_MODE)
layer.visible = layervisible
layer.add_alpha()
layer.fill(BACKGROUND_FILL)
layer.fill(TRANSPARENT_FILL)
for h in range(height):
for w in range(width):
b = layerdata[(width * h) + w]
if b != transcolor:
# 透過色以外を塗る
layer.set_pixel(w, h, (b, 255))
image.add_layer(layer, no)
# 戻す
return image
# EDGEファイル書き出し関数.
def save_edge(image, drawable, filename, raw_filename):
"""
EDGEとして画像を出力します.
インデックスカラー画像で、256色以下のみ対応します.
:param gimp.Image image: 入力画像
:param gimp.Drawable drawable: ???
:param str filename: ファイルパス
:param str raw_filename: ???
:rtype: void
:return: void
"""
if image.base_type != INDEXED:
raise RuntimeError('インデックスカラー画像以外は未対応です')
with open(filename, 'wb') as fp:
# ヘッダ
fp.write(struct.pack('<4s6x', 'EDGE'))
# 幅・高さ・レイヤ数
fp.write(struct.pack('<iiH', image.width, image.height, len(image.layers)))
# 背景色を透過色扱いにする (実験)
bgcolor = tuple(gimp.get_background()[:3])
# パレット
colormapsize, colormap = pdb.gimp_image_get_colormap(image)
numcolors = colormapsize // 3
# 透過職を探す
bgindex = 0
bgdist = None
for n in range(numcolors):
col = (colormap[(3 * n)], colormap[(3 * n) + 1], colormap[(3 * n) + 2])
if bgcolor == col:
bgindex = n
break
else:
# 計算しないと
distance = math.sqrt(((col[0] - bgcolor[0]) ** 2) + ((col[1] - bgcolor[1]) ** 2) + ((col[2] - bgcolor[2]) ** 2))
if (bgdist is None) or (bgdist > distance):
bgdist = distance
bgindex = n
# 透過とパレットを書き込む
fp.write(struct.pack('<B', bgindex))
fp.write(str(bytearray(colormap)))
for n in range(numcolors - 256): # パディング
fp.write(struct.pack('<3B', 0, 0, 0))
def compress(pixels):
"""
圧縮したバイト列を作ります.
:param pixels: Pixel Region
:rtype: bytes
:return: 圧縮済みバイト列
"""
srcdata = bytearray(pixels)
dstdata = bytearray()
srclength = len(srcdata)
srcpos = 0
dstpos = 0
output = bytearray()
# 圧縮パケット生成
packets = []
while srcpos < srclength:
i = srcpos + 1
j = 0
while i < srclength:
if srcdata[i] != srcdata[srcpos]:
break
i += 1
j += 1
if j > 9:
packet = {'length': j + 1, 'position': dstpos, 'value': srcdata[srcpos]}
packets.append(packet)
srcpos += (j + 1)
else:
dstdata.append(srcdata[srcpos])
srcpos += 1
dstpos += 1
# バイナリ化
output += struct.pack('<i', len(packets))
for packet in packets:
output += struct.pack('<IIB', packet['position'], packet['length'], packet['value'])
output += struct.pack('<i', len(dstdata))
output += dstdata
return str(output)
# レイヤーを書き込む
for i, layer in enumerate(image.layers):
# 名前と可視性を記録
fp.write(struct.pack('<80s', layer.name))
fp.write(struct.pack('<?', layer.visible))
# ピクセルを記録
region = bytearray(layer.get_pixel_rgn(0, 0, image.width, image.height)[0:, 0:])
if layer.has_alpha:
pixels = bytearray()
for i in range(0, len(region), 2):
v = region[i]
a = region[i + 1]
if a == 0:
pixels.append(bgindex)
else:
pixels.append(v)
else:
pixels = region
fp.write(compress(pixels))
# 登録ハンドラ
def register_load_handlers():
gimp.register_load_handler('file-edge-load', 'edg', '')
pdb['gimp-register-file-handler-mime']('file-edge-load', 'image/edge')
pdb['gimp-register-thumbnail-loader']('file-edge-load', 'file-edge-load-thumb')
def register_save_handlers():
gimp.register_save_handler('file-edge-save', 'edg', '')
# プラグインを登録
register(
'file-edge-load', # プラグイン名
'Load an EDGE (.edg) file', # 説明
'Load an EDGE (.edg) file',
'chikuchikugonzalez', # 作者
'chikuchikugonzalez', # Copyright
'2014', # 年
'EDGE 形式', # 画像開くダイアログのラベル
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),
],
[
# Results.
# Format (type, name, description)
(PF_IMAGE, 'image', 'Output image')
],
load_edge, # コールバック
on_query = register_load_handlers,
menu = "<Load>",
)
register(
'file-edge-save',
'Save an EDGE (.edg) file',
'Save an EDGE (.edg) file',
'chikuchikugonzalez',
'chikuchikugonzalez',
'2014',
'EDGE 形式',
'INDEXED*',
[
(PF_IMAGE, 'image', 'Input image', None),
(PF_DRAWABLE, 'drawable', 'Input drawable', None),
(PF_STRING, 'filename', 'The name of the file', None),
(PF_STRING, 'raw_filename', 'Then name of the file', None),
],
[],
save_edge,
on_query = register_save_handlers,
menu = '<Save>',
)
# ???
main()
#==============================================================================#
# vim: set sts=4 ts=4 sw=4 expandtab:
@chikuchikugonzalez
Copy link
Author

たぶん、decompresslayer.set_pixel がくそ重い

@chikuchikugonzalez
Copy link
Author

そこそこのパフォーマンス出た

@chikuchikugonzalez
Copy link
Author

書き込みできた('ω')

@chikuchikugonzalez
Copy link
Author

なお、元にしたのは付属の file-openraster.py プラグインです。
それ以外にPython実装のI/O関係のプラグイン見つかんなかったんだけど、やっぱC実装しろとそういうことなんだろうか

@chikuchikugonzalez
Copy link
Author

参考資料

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment