やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

GIMP python-fu xcfファイル保存(class)

 コードを細かく構造化できた。

成果物

使い方

git clone https://github.com/ytyaru/Python.Gimp.Save.Xcf.Class.20191218122716
cd Python.Gimp.Save.Xcf.Class.20191218122716/src
./run.sh

コード

run.sh

Run() {
    IsExistCmd() { type "$1" > /dev/null 2>&1; }
    Install() { ! IsExistCmd "$1" && sudo apt -y install "$1"; }
    IsExistPkg() { dpkg -l | grep "$1" > /dev/null 2>&1; }
    InstallPkg() { ! IsExistPkg "$1" && sudo apt -y install "$1"; }
    RunBatch() { gimp --no-interface --console-messages --no-data --no-splash --batch-interpreter python-fu-eval --batch - < "$1"; }
    Install gimp
    InstallPkg gimp-python
    RunBatch "$(cd $(dirname $0); pwd)/save_xcf.py"
}
Run "$@"

save_xcf.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from gimpfu import *
import os,sys

class Image(object):
    def __init__(self):
        self.image = None
        self.layer = None
    def __create_image(self, width=512, height=512, _type=RGB):
        self.image = gimp.Image(width, height, _type) # type=RGB,GRAY,INDEXED
    def __create_layer(self, name="layer01", width=512, height=512, _type=RGBA_IMAGE, opacity=100, mode=NORMAL_MODE):
        self.layer = gimp.Layer(self.image, name, width, height, _type, 100, mode)
        self.layer.fill(TRANSPARENT_FILL)
    def __add_layer(self):
        self.image.add_layer(self.layer, 0)
    def create(self, width=512, height=512):
        self.__create_image(width=width, height=height)
        self.__create_layer(width=width, height=height)
        self.__add_layer()

class Drawer(object):
    def __init__(self, layer):
        self.layer = layer
    def draw(self):
        self.__draw_rectangle()       
        self.__to_argb()
    def __draw_rectangle(self, r=1.0, g=0.0, b=0.0, x=128, y=128, w=256, h=256):
        import cairo
        self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.layer.width, self.layer.height)
        ctx = cairo.Context(self.surface)
        ctx.set_source_rgb(r, g, b)
        ctx.rectangle(x, y, w, h)
        ctx.fill()
    def __get_rgba_str(self, src):
        import struct
        rgba_buf = ""
        l = len(src)
        for i in range(l / 4):
            i0 = i * 4
            i1 = i0 + 4
            bgra = struct.unpack('@L', src[i0 : i1])[0]
            a = (bgra >> 24) & 0x0ff
            r = (bgra >> 16) & 0x0ff
            g = (bgra >> 8) & 0x0ff
            b = bgra & 0x0ff
            rgba = struct.pack('4B', r, g, b, a)
            rgba_buf += rgba
        return rgba_buf
    def __to_argb(self):
        src = self.surface.get_data()
        dst = self.__get_rgba_str(src)
        w = self.layer.width
        h = self.layer.height
        rgn = self.layer.get_pixel_rgn(0, 0, w, h, True, True)
        rgn[0:w, 0:h] = str(dst)
        self.layer.flush()
        self.layer.merge_shadow()
        self.layer.update(0, 0, w, h)

class Xcf(object):
    def __init__(self): pass
    def save(self, image, path=""):
        save_path = self.__path()
        pdb.gimp_xcf_save(0,image,image.layers[0],save_path,save_path)
        gimp.message("Python " + sys.version)
        gimp.message("ファイル保存しました。: " + save_path)
    def __path(self, path=""):
        dir_path = path
        if "" == dir_path: dir_path = "/tmp/work/"
        try:
            os.makedirs(dir_path)
        except: pass
            #import traceback
            #traceback.print_exc()
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
        save_path = os.path.join(dir_path, timestamp + ".xcf")
        return save_path 


i = Image()
i.create()
d = Drawer(i.layer)
d.draw()
x = Xcf()
x.save(i.image)
pdb.gimp_quit(1)

API

gimp.Layer

gimp.Layer(img, name, width, height, type, opacity, mode)

 type, mode にはどんな値が入りうるのか不明。

 GIMP メニュー ヘルププロシージャーブラウザで調べてみた。layerで検索。

 gimp-layer-newがそれっぽい。

 typeは以下。

The layer type { RGB-IMAGE (0), RGBA-IMAGE (1), GRAY-IMAGE (2), GRAYA-IMAGE (3), INDEXED-IMAGE (4), INDEXEDA-IMAGE (5) }

 modeは以下。

The layer combination mode { LAYER-MODE-NORMAL-LEGACY (0), LAYER-MODE-DISSOLVE (1), LAYER-MODE-BEHIND-LEGACY (2), LAYER-MODE-MULTIPLY-LEGACY (3), LAYER-MODE-SCREEN-LEGACY (4), LAYER-MODE-OVERLAY-LEGACY (5), LAYER-MODE-DIFFERENCE-LEGACY (6), LAYER-MODE-ADDITION-LEGACY (7), LAYER-MODE-SUBTRACT-LEGACY (8), LAYER-MODE-DARKEN-ONLY-LEGACY (9), LAYER-MODE-LIGHTEN-ONLY-LEGACY (10), LAYER-MODE-HSV-HUE-LEGACY (11), LAYER-MODE-HSV-SATURATION-LEGACY (12), LAYER-MODE-HSL-COLOR-LEGACY (13), LAYER-MODE-HSV-VALUE-LEGACY (14), LAYER-MODE-DIVIDE-LEGACY (15), LAYER-MODE-DODGE-LEGACY (16), LAYER-MODE-BURN-LEGACY (17), LAYER-MODE-HARDLIGHT-LEGACY (18), LAYER-MODE-SOFTLIGHT-LEGACY (19), LAYER-MODE-GRAIN-EXTRACT-LEGACY (20), LAYER-MODE-GRAIN-MERGE-LEGACY (21), LAYER-MODE-COLOR-ERASE-LEGACY (22), LAYER-MODE-OVERLAY (23), LAYER-MODE-LCH-HUE (24), LAYER-MODE-LCH-CHROMA (25), LAYER-MODE-LCH-COLOR (26), LAYER-MODE-LCH-LIGHTNESS (27), LAYER-MODE-NORMAL (28), LAYER-MODE-BEHIND (29), LAYER-MODE-MULTIPLY (30), LAYER-MODE-SCREEN (31), LAYER-MODE-DIFFERENCE (32), LAYER-MODE-ADDITION (33), LAYER-MODE-SUBTRACT (34), LAYER-MODE-DARKEN-ONLY (35), LAYER-MODE-LIGHTEN-ONLY (36), LAYER-MODE-HSV-HUE (37), LAYER-MODE-HSV-SATURATION (38), LAYER-MODE-HSL-COLOR (39), LAYER-MODE-HSV-VALUE (40), LAYER-MODE-DIVIDE (41), LAYER-MODE-DODGE (42), LAYER-MODE-BURN (43), LAYER-MODE-HARDLIGHT (44), LAYER-MODE-SOFTLIGHT (45), LAYER-MODE-GRAIN-EXTRACT (46), LAYER-MODE-GRAIN-MERGE (47), LAYER-MODE-VIVID-LIGHT (48), LAYER-MODE-PIN-LIGHT (49), LAYER-MODE-LINEAR-LIGHT (50), LAYER-MODE-HARD-MIX (51), LAYER-MODE-EXCLUSION (52), LAYER-MODE-LINEAR-BURN (53), LAYER-MODE-LUMA-DARKEN-ONLY (54), LAYER-MODE-LUMA-LIGHTEN-ONLY (55), LAYER-MODE-LUMINANCE (56), LAYER-MODE-COLOR-ERASE (57), LAYER-MODE-ERASE (58), LAYER-MODE-MERGE (59), LAYER-MODE-SPLIT (60), LAYER-MODE-PASS-THROUGH (61), LAYER-MODE-REPLACE (62), LAYER-MODE-ANTI-ERASE (63) }

 これはscript-fu(scheme言語)上の定義名。python-fuでは別名になっている。たとえは今回使った値との対応表は以下だと思われる。

python-fu script-fu int値
RGBA_IMAGE RGBA-IMAGE 1
NORMAL_MODE LAYER-MODE-NORMAL 28

 こういう定義名もどこかにまとめてほしいのだが。

所感

 これでpython2でもclass化できることが確認できた。

 えらい苦労した。Pythonのコードは汚くみえる。def,selfが多すぎて。何よりカプセル化できないのが致命的。

対象環境

$ uname -a
Linux raspberrypi 4.19.75-v7l+ #1270 SMP Tue Sep 24 18:51:41 BST 2019 armv7l GNU/Linux