月別アーカイブ: 2018年1月

screentone 2D and 3D

コミックブックアーカイブビューアを作っている筆者でありますが。
スクリーントーンの描写が変になる場合があることに困っていた。

Clutter(OpenGL ES) でリサイズしているのが悪いのかな?
と思い GdkPixbuf をリサイズしてからレンダリングで解決した。
よかったよかった、じゃない!もう少し調べよう。

Jトーン 網点(ドットパターン) J-00|スクリーントーン 通販【ジェイトーン・ネットショッピング】

トーンは上記のサンプル画像をお借りしてと。

#!/usr/bin/env python3

import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Clutter", "1.0")
gi.require_version("GtkClutter", "1.0")
from gi.repository import Gtk, Gdk, GdkPixbuf, Clutter, GtkClutter, Cogl

class DrawWindow(Gtk.Window):
    """
        2D and 3D View Check
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # tone
        self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("tone.jpg")
        # cairo (2D)
        self.da = Gtk.DrawingArea()
        self.da.connect("draw", self.on_draw)
        # OpenGL ES (3D)
        embed = GtkClutter.Embed()
        stage = embed.get_stage()
        stage.set_background_color(Clutter.Color.new(0, 0, 0, 255))
        stage.connect("allocation-changed", self.on_stage_allocation_changed)
        self.actor = Clutter.Actor()
        image = Clutter.Image()
        image.set_data(
            self.pixbuf.get_pixels(),
            Cogl.PixelFormat.RGBA_8888 if self.pixbuf.get_has_alpha() else Cogl.PixelFormat.RGB_888,
            self.pixbuf.get_width(),
            self.pixbuf.get_height(),
            self.pixbuf.get_rowstride()
        )
        self.actor.set_content(image)
        stage.add_child(self.actor)
        # set
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(self.da, True, True, 0)
        vbox.pack_start(embed, True, True, 0)
        self.add(vbox)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_stage_allocation_changed(self, actor, box, flags):
        aw = box.x2
        ah = box.y2
        w = self.pixbuf.get_width()
        h = self.pixbuf.get_height()
        if aw * h > ah * w:
            width = w * ah / h
            height = ah
            x = (aw - width) / 2
            y = 0
        else:
            width = aw
            height = h * aw / w
            x = 0
            y = (ah - height) / 2
        self.actor.set_size(width , height)
        self.actor.set_position(x, y)

    def on_draw(self, widget, cr):
        d_width = widget.get_allocated_width()
        d_height = widget.get_allocated_height()
        cr.set_source_rgb(0, 0, 0)
        cr.rectangle(0, 0, d_width, d_height)
        cr.fill()
        p_width = self.pixbuf.get_width()
        p_height = self.pixbuf.get_height()
        if (d_width * p_height) > (d_height * p_width):
            width = p_width * d_height / p_height
            height = d_height
        else:
            width = d_width
            height = p_height * d_width / p_width
        pixbuf = GdkPixbuf.Pixbuf.scale_simple(self.pixbuf, width, height, GdkPixbuf.InterpType.BILINEAR)
        Gdk.cairo_set_source_pixbuf(cr, pixbuf, (d_width-width)/2, (d_height-height)/2)
        cr.paint()

GtkClutter.init()
DrawWindow()
Gtk.main()

動かす。

上半分が cairo で下が Clutter です。
拡大はまったく同じ描写になるけど縮小は全然違うものになることが解る。
なんだよこの 3D 描写。。。。。

画像処理や 3D に詳しいわけではないので細かい解説はできないけど。
単なるラスターデータとして扱う 2D と RGB として扱う 3D の違いだろう。
網点を RGB 色として認識した結果こんな悲惨なことに。

とにかく 3D では小さめな画像かアニメ調のベタ塗りでないとこんなことになる。
スクリーントーンを多用するコミック表示には徹底的に向いていないようです。

Clutter で cairo が使える手段があるのはそういうことか。
cairo で作り替えしよ。

text/x-python3

Fedora 27 では ContentType に text/x-python3 が追加されていた。
…ことを昨日久々に Python ネタを書いてやっと気が付く筆者であった。
Gedit で python の所に登録したスニペットが流し込めなかったということで。

とうとう Python2 からの完全置き換えをあきらめたということかな。
ちなみに Gedit はモードラインが無いなら ContentType で見分けている。
どういう振り分けをしているのかしらべてみよう。

/usr/share/mime/magic
を Gedit で無理矢理開いて Ctrl+F から python3 を検索。

あぁシバンのみで判断しているってことね。
拡張子を py にしただけのファイルは text/x-python のままだ。

python3+15 って何だろと思ってアレコレ試してみた。
先頭から 15 文字目までにコレを見つけたら Python3 だと認識、でいいのかな。

#!!!!!!!!!!!/usr/bin/env python3

から ! を一文字増やし Ctrl+s で Gedit のステータスバー表記が Python に変わる。

いや実験です、この shebang は当然動作しません。
しかし 15 文字っていったいどんだけ変な場所に env があるんだよ。

とにかくこれで Python2/3 コードの振り分けが簡単になる。
いや現行の Fedora デフォルトに Python2 は含まれていませんけど。

multiprocessing, staticmethod

Python3 にて multiprocessing ではまったので覚書

#!/usr/bin/env python3

import multiprocessing

def global_pool(num):
    """ return is a Python Type Onry """
    return num

class MpTest():
    def __init__(self):
        self.n = 10
        pool = multiprocessing.Pool()
        for num in range(10):
            #pool.apply_async(self.on_pool, args=(num,), callback=self.on_pool_result) # NG
            #pool.apply_async(global_pool, args=(num,), callback=self.on_pool_result) # OK
            pool.apply_async(self.static_pool, args=(num,), callback=self.on_pool_result) # OK
        pool.close()
        pool.join()

    def on_pool(self, num):
        """ Not Work """
        return num

    @staticmethod
    def static_pool(num):
        """ OK """
        return num

    def on_pool_result(self, result):
        print(result, end=",")

if __name__ == '__main__':
    MpTest()

で。
ランダムな順に出力され並行処理されているのが確認できる。

Pool はインスタンスメソッドにはアクセスできないのね。
実は去年からドン詰まりしていたのがやっと解決。

グローバル関数にするのもアレだし、ってことで static にしてもイケた。
あぁ @staticmethod はこういう場合に使うんだな。

それより数値や文字列という Python の型しか戻せないのが痛い。
Gir で GdkPixbuf を数個作るのを並行処理みたいなことはできない。
というかしようと思ったんだけど、どうやらこの手段では無理っぽい。

read command

read コマンドはファイルを読み出せると今頃知った。
いやまあ、この人と同じようなことをやろうとしたら下のほうに。

sh – IFSに改行のみを指定したい – スタック・オーバーフロー

早速実験コードを書いてみたらあら大変。
アスタリスクが展開されているしインデントが消えている。
運よく C 言語ソースの読み込みで試したおかげで即解った。

bashで * が意図しないワイルドカード展開される場合の対処 – 座敷牢日誌

read だとシェルと同じワイルドカード展開をしてしまうのか。
そういうものだと思うしか無いけど。

readするときはIFS=を付けておくとstrictな感じで気持ちが良い – Qiita

while IFS= なんて手段があったのか!
早速試したけどマジでデフォルトに影響しないんだね。
後で IFS をデフォルトに書き戻す必要が無いってこと。

ということで先頭数文字をオフセットして書き出すスクリプトでも。
ちなみに空行でもスルーしてくれるので文字列長を得る必要は無い。

#!/bin/sh

# 先頭の何文字かをオフセットして出力する bash スクリプト
# [スクリプトファイル名] [オフセットしたい数値] [ファイル名]
# 書き出しはリダイレクトを使う

if [[ $# -lt 2 ]]; then
    echo $0 [オフセットしたい数値] [ファイル名]
else
    while IFS= read -r line; do
        echo "${line:$1}"
    done < $2
fi

# @ for
#_IFS=$IFS
#IFS=$'\n'
#lines=`cat $2`
#for line in $lines; do
#    echo ${line:$1}
#done
#IFS=$_IFS

短っ!

正直スクリプトなんて思ったとおりに動けばそれで充分なんだけどさ。
ココまでスッキリするとチョッピリ嬉しいですね。

Gjs 1.50 GtkWindow

GitHub の gjs サンプルコードが更新されていることに気が付いた。

gjs/gtk-application.js at master ? GNOME/gjs ? GitHub

あれ、_init というメソッドが定義されていたの?
constructor を無理に使わなくてもいいってことみたい。
_init なら以前書いたみたく super() 以降を別関数に分けなくてもイケるのかな。

GtkApplication を使うなら Gtk.init() は不要だった。
実は Y901x を作っていた時に気がついていた、コッチに書くのを忘れていた。

constructor の引数は JSON しか受け付けなかったけど _init だとどうだ?
わかんない時はとっととサンプルコードを書いて動かすほうが速いので早速。
単なる Window ではつまらないので Evince のバインドで。

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";
imports.gi.versions.EvinceDocument = "3.0";
imports.gi.versions.EvinceView = "3.0";

const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
const EvinceDocument = imports.gi.EvinceDocument;
const EvinceView = imports.gi.EvinceView;

var EvinceWindow = GObject.registerClass({
    GTypeName: "EvinceWindow"
}, class EvinceWindow extends Gtk.Window {
    /*constructor(props={}) {
        super(props);
    }
    create() {*/
    _init(text) {
        super._init({title: text});
        this.model = new EvinceView.DocumentModel();
        let view = new EvinceView.View();
        view.set_model(this.model);
        // Scroll
        let scroll = new Gtk.ScrolledWindow();
        scroll.add(view);
        this.add(scroll);
        /* No need
        this.drag_dest_set(
            Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
            [Gtk.TargetEntry.new("text/uri-list", 0, 0)],
            Gdk.DragAction.MOVE
        );*/
        this.drag_dest_add_uri_targets();
        this.connect("drag-data-received", (widget, drag_context, x, y, data, info, time)=> {
            let uri = data.get_uris()[0];
            let doc = EvinceDocument.Document.factory_get_document(uri);
            this.model.set_document(doc);
        });
        //this.connect("hide", Gtk.main_quit); //arguments Error
        this.connect("hide", ()=>Gtk.main_quit());
        this.show_all();
    }
});

// When GtkApplication is not used.
Gtk.init(null);

EvinceDocument.init();

let ev = new EvinceWindow("Drop the PDF File!");
//ev.create();
Gtk.main();

なるほど、_init を使うほうが圧倒的に簡単になるやん。
ちなみに gir で Evince 等を使うドキュメントは以下に。
GNOME JavaScript Docs

それより drag_dest_set 指定はいつのまに不要になったのだ?
以前は drag_dest_add_uri_targets だけでは動作しなかった気がするんだが。