前回の2P目の和訳からえらい間が空きましたが、How to write a GIMP plug-in 3Pの和訳です。訳出の意図、注意は1P目同様です。
コードについてはプロトタイプ宣言だけは載せていますので、コード全体は原文を適宜参照してください。
この和訳文書はcreative commons : 帰属 - 非営利 - 同一条件許諾 2.5でライセンスされてます。
原著者: Dave Neary(bolsh@NOSPAM.gimp.org)
第二章で、私はピクセル単位か行単位での画像データ(image data)の操作を説明した。今回は、さらに進めて、今までに作ったプラグインの性能を改善させられるタイル単位でのデータ処理を行う。また、より大きな半径を配慮したアルゴリズムへの改良を行い、パラメータを変更させることができるようなグラフィカルインターフェイスの構築も行う。
前に作った単純なアルゴリズムを確認してみよう。このアルゴリズムは、ピクセル単位で、各レイヤーに(2r+1)x(2r+1) - x,yの形式、rは半径 - の近傍を設定し、その近傍の平均でレイヤーの各ピクセルを置き換えるモノであった。
例の画像で画像の境界で注意を払わなければいけないのはいささか複雑ではある。しかしこのアルゴリズムによるブラー処理は一般的にそれほど悪くはない。
とはいえ、今まで書いてきたアルゴリズムは3x3近傍ピクセルを対象とした処理であった。この章では、対象とする範囲を一般化し、パラメータとして近傍の範囲を取得するようにする。
まずはタイルについて。
タイルとはサイズ64x64ピクセルの画像データブロックです。通常タイルはプラグインに対して一つ一つ要求に応じて、共有メモリから送られます。もちろん、この処理は大量の資源を必要とし避けるべきである。
普通、特定のキャッシュを必要とすることはない、どのタイルも必要とされたとき送られ、他のタイルが呼ばれれば、開放される。それでもなお、以下の関数を使うことによって、一定間隔で回ってくるround tripを避けてプラグインにタイルキャッシュを保持させることができる。
gimp_tile_cache_ntiles (gulong ntiles);
part2の例では、gimp_pixel_rgn_get_row()とgimp_pixel_rgn_set_row()を呼び出したが、キャッシュを使うことはしなかった。
タイル列一列のタイルの数は、タイルの幅でレイヤーの幅を割った数に1足した数となるだろう。そのため、レイヤーの幅が65の場合、我々は二つのタイルをキャッシュすることになる。通常シャドウタイルも処理するので、プラグインの扱うキャッシュのサイズは倍にしている。
gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
キャッシュによって遅かったプラグインは早くなった。300x300の画像上では、最新のブラー処理は3秒かかった。しかし、2000x1500の画像上では遅くなり、142秒かかった。
上記のコードを追加することで、処理速度は向上し、11秒で終了した。我々はまだタイルの変わり目での変化に要する時間を失っている。2行分のタイルキャッシュを取得する変わりに4行分取得するようにしたら、10秒まで減らすことができた。ある点を堺に処理時間の増加を減らすことができたが、より多くのキャッシュを必要とするようになり、またさらなるハードディスクへのアクセスを発生させることになった。
半径パラメータを考慮するために、アルゴリズムを修正しよう。半径が3の場合、ピクセル近傍は7x7とし、半径1の場合は3x3とする。これを実現するために、以前のアルゴリズムを次のように修正する。
このアルゴリズムは一つ前のよりも複雑である、なぜなら平均の計算時間がO(r^2)のアルゴリズムだからである。
このように動作するよう修正されたソースコードを以下に示す。ほとんどの処理は、process_row()関数内で行われる。init_mem()関数とshuffle()関数はブラー処理のソースコードをきれいで小さく保つ。
static void blur (GimpDrawable *drawable); ... static void blur (GimpDrawable *drawable) { ... } static void init_mem (guchar ***row, guchar **outrow, gint num_bytes) { ... } static void process_row (guchar **row, guchar *outrow, gint x1, gint y1, gint width, gint height, gint channels, gint i) { ... } static void shuffle (GimpPixelRgn *rgn_in, guchar **row, gint x1, gint y1, gint width, gint height, gint ypos) { ... }
ユーザが半径を修正しようとするために、もしくは対話的で無いスクリプトにパラメータとして値を渡す場合、run()関数へ戻る必要があり、それは単純なスクリプトでできる。
まず我々は、オプションを保存し返すことができる構造体を作成する。たいてい、これは一つだけパラメータをもつプラグインの為に使われる。
typedef struct { gint radius; } MyBlurVals; // /* Set up default values for options */ static MyBlurVals bvals = { 3 /* radius */ };
次に、execution modes(実行モード)を考慮するためにrun()関数を修正する。interactive mode(対話モード)や、一つ前のフィルタを繰り返すモードにおいて、われわれはgimp_get_data()関数で利用された値を取得しようとした。この関数は一番最初に入力パラメータとして一意なデータ識別子を提供してくれる。たいてい、プロシージャ名を使う。
最終的には、対話モードで、オプションを修正するためのグラフィカルインターフェイスを構築するための数行を追加した。
static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { ... }
私は、GTK+プログラミングの詳細は詳しくない、GTK+プログラミングについては他の素晴らしいサイトを参照してくれ。まず最初に試したのは非常に単純なものだ。GIMPのウィジェットユーティリティを使い、ヘッダーを持つウィンドウをGimpDialogで作成し、数値を制御する(GtkAdjustmentと関連付けられた)GtkSpinButtonも作り、そしてGtkFrameでラベルを作成した。
以下のパートでは、このような作業がどれだけ簡単にできるかを示すために、パラメターの効果をリアルタイムに示すプレビューダイアログを追加する。
最終的に、Gladeのツリー表示パネルで以下のように表示される程度になった。
GIMP2.2で、GNOMEヒューマンインターフェイスガイドラインに沿って、首尾一貫した振る舞いを行うように考慮された、パラメータを伴うウィジェットが存在する。GimpPreviewは2.2から現れた、とりあえずそれを使わない版を作ってみる。
static gboolean blur_dialog (GimpDrawable *drawable) { ... }
GimpPreviewを追加するのは本当に簡単だ。最初に、gimp_drawable_preview_new()関数を使ったGtkWidgetを作成する。その時、無効化シグナル(訳者注)をこの関数に設定し、プレビューが更新される度にblur()関数を呼ぶようにする。また、MyBlurValsにプレビューが有効かどうかを覚えておくための第二引数を追加する。
簡易的なプレビューの更新方法とは、プレビューパラメータをblur()関数内に追加することです。そして、プレビュー引数がNULLでないなら、限定的にGimpPreviewを行う。そして、run()関数からblur()関数を呼ぶ場合、プレビューパラメータはNULLに設定する。
限定的なGimpPreviewを行う為に、gimp_preview_get_position()とgimp_preview_get_size()を利用する。これで表示されている箇所だけにぼかし処理をかけることができる。
最適な方法に到達するには、コードのいくらかを調整しないといけない。例えば、プレビュー生成時はプログレスバーの更新は必要ないし、GimpPixelRgn初期化時、タイルキャッシュがコアに送り返さないようにさせる。
最終的に、gimp_drawable_preview_draw_region()によって更新されたプレビューを表示させる。プラグインの効果をリアルタイムに表示するダイアログボックスを得られる。さらには、GIMPコアのおかげで既にこのプラグインは選択領域も考慮できているからだ。
static void blur (GimpDrawable *drawable, GimpPreview *preview) { ... } static gboolean blur_dialog (GimpDrawable *drawable) { ... }
タイルキャッシュ処理、UI、プレビュー処理についてはそれぞれのコードを見てくれ。
この記事では、我々はいくつかのGIMPプラグインの側面を基本的な概念を見てきた。単純なアルゴリズムによる画像データの処理を行ってきた、そしてパフォーマンス問題をどのようにして避けるべきなのかという道を示した。最終的に我々はアルゴリズムを一般化し、パラメータを追加し、GIMPウィジェットを良いユーザインターフェイスにするために使った。
この記事を書いている間中、私を助けてくれた妻AnneとDavid Odinへ感謝する。
体裁がぐっちゃぐちゃなんで、どうにかしたい。っていうかやっぱり、翻訳支援ツールとか使ってみるべきだった。後、語尾もですます調にするべきだったなぁ。
さて、次はどうするべか。ってせっかく訳したんやし何かPlug-inを作ってみんとなぁ、調べたことが無駄になる...。
gimp_drawable_preview_new()では、invalidated signalというシグナルが定義されていて、最初何の意味かさっぱりわからなかった。
「無効化されたシグナル」とか直訳して、”シグナルを無効化する関数があるんかなぁ”とか思ったけども、どうやら違うらしい。っていうかそれじゃあ意味が通らないし。
invalidateはGUIプログラミングでupdateやrepaintと同じ再描画に関する信号としてよく使われる名前のようで、「描画した効果がなくなった」ことを示すシグナルだということでした。windowsでのゲームプログラミングの記事でその名前が確認できました。
というわけで、上記の文章でinvalidated signalが表れた箇所は、再描画の必要がある際、invalidated signalが発行されるとblur()関数を呼ぶようにしている。
土曜日に机、棚よりも一足早くやってきました、アーロンチェア。一番安い、スタンダードですが、大方問題無し。アームレストの高さ調節がスタンダードではできないので、腕の行き場所に悩みました。しかし、思いっきりリクライニングさせてキーボードに手を伸ばすとそもそもアームレスとが机の下に隠れる上、腕が机に完全に乗っかってしまったので問題解決。
前傾姿勢ではないので座っている間、足に全然力がかかりません。おかげで、立ち上がるとき体を重く感じます(何
現状まぁそんなもんなんだろうなぁって感じはするけど、「一般的にOSS翻訳は軽視されており」ってのはどうなんだろ。debianやFedoraでは翻訳に関するMLがあったりするし、GNOME、KDE等にしても翻訳が軽く見られているなんて思えないんだけどなぁ。まぁ思えないってだけで中がほんとはどうなのか見たことないからなんともいえんが。
この発表をされてる樋口さんってSunで翻訳スタイルガイドを策定された人かな。ここんとこオープンソースな翻訳支援ツールもいくつかあるんで、これから試してみたい。
いつまでも質の低い和訳を垂れ流す訳にもいかんしのぅ。