Layer.cached プロパティを追加しました。
吉里吉里はレイヤツリーのノードごとに、そのノード自身と子孫レイヤを全て重ね合わせた状態の画像をあらかじめ持っておいて、自分の画像や子孫レイヤの画像や状態に変化がない限り、以降の重ね合わせの再計算を避ける機構があります。これを単にキャッシュと呼んでいます。この機能のオン・オフを Layer.cached として TJS から操作できるようにしました。
吉里吉里1の頃は全てのレイヤノードに対してこれが強制的に真でした。レイヤツリーの末端のレイヤに変化があると、親をさかのぼって全部の親のレイヤのキャッシュが更新されるので、あまり動的な画像変化に向いていませんでした。
吉里吉里2ではデフォルトではこの機能はオフです。つまり毎回画面更新の際に画像の重ね合わせの演算をしています。画面の更新があるたびに毎回重ね合わせの計算をするのでこっちの方が遅くなる事もありますが、動的な画面構成に適しています。
どっちがパフォーマンスが良いかは場合によるのですが、自分自身の画像と、子レイヤの画像や状態に変化が無いことが分かっていれば、そのレイヤの cached プロパティを真に設定すれば、毎回の再計算を防ぐことができます。
なんでこういう機構がついてるのかというと、トランジション中に、トランジション元やトランジション先になっているレイヤの、毎回の重ね合わせ演算を避ける為です。トランジション中は自動的にキャッシュが有効になっていたというわけです。キャッシュを行っているレイヤやその子レイヤに変化があると、キャッシュは自動的にその部分が再構成されるので、特にキャッシュが行われてるかどうかは考えずに透過的に扱うことができます。これはトランジション中でもそうですね。
あとは Layer.piledCopy でも一時的に転送元のノードのキャッシュを有効にしてからキャッシュ画像を構成し、それを転送するという処理が内部的に行われています。
サクラエディタ用のキーワードファイル(キーワードが並んでいるファイル)と、キーワードヘルプ(キーワードの説明の並んでいるファイル)をおいておきます。
これはnasmのHTML版マニュアル(英語)から抽出してlynxで整形した物です。Intelとかの公式のドキュメントではないですが、サッパリと説明されていて、ど忘れ対策には良さそうです。…いやまあ、これが上記の「pmulhw/pmullwは符号無し整数を扱う」の勘違いの元となったわけですが。
使用するとこういう事ができます。

色分け表示とか。pmulhw/pmullwの説明にたしかに unsigned って書いてあるよなぁ...(そのうち報告しておこう)

補完候補にも設定できます。
ここ数日は矩形ブラー(ぼかし)を実装してました。
Layer.doBoxBlur メソッドとして実装してあります。縦方向のぼかし加減と横方向のぼかし加減を別々に指定できるので水平ブラーとかできます。
矩形ブラーは、対象ピクセルの周囲の矩形の輝度の平均をとる物ですが、何も考えずに演算すると矩形範囲のピクセルの輝度を全部足してからピクセルの個数で割って…と、ぼかしの程度が多くなると大変な演算量になってしまいます。
今回実装したのは、アルゴリズムは何という名前かわかりませんが、前にゆんさんに頂いていたコードを元にした物で、理論的にはぼかしの強度によらず演算量が一定の物です。実際は画像の端っこに対する特別な処理がぼかしの強度によって変わってくるので一定とは行かないのですが。
あと強度(ドキュメント中では「範囲」としてあります)によって使用する整数型のサイズが決まる仕組みになっていて、強度が小さいぼかしは整数型のサイズが小さく、サイズが小さい方が速いので、やっぱり強度が違うと速度は違います。
アセンブラルーチンがタイプによって10種類ぐらい必要だったのですが、シコシコと書くのしんどかった…。平均を出すときの除算(逆数による乗算)をするのに使うMMX命令のpmulhw/pmullwが符号無し整数を扱う物だとなぜか思いこんでいたので(実際は符号付き整数を扱う)、当初からそれができる物としてプログラムを組んでいたのですが。そのことにはアセンブラルーチン書き始めたときに気づきました。時既に遅し。でもSSEにpmulhuwという、まさに思っていた物があったので、結局MMXとSSE版を分けて書きました。おかげでそのSSE版はMMX版の倍ほど速かったりします。要するにぼかしができるようになったと言うことです。
RSSでここの日記の情報を公開しはじめました。
ページの右下のRSSアイコンからRSSのURLを取得可能です。アイコンは Masahiko Isshiki氏のBLOG から頂きました、ありがとうございます。
linkタグでページにRSSへのリンクを埋め込んであるので、これに対応しているビューアやMozilla Firefox等では自動的に認識すると思います。
いままで吉里吉里のアフィン変換は、内部的に整数で座標を扱っていて、精度的に良い物とは言えませんでした。
バイリニア補間を組み込んでいるときに、あまりに精度が悪くて使い物にならなかったのが問題になって、今までのアフィン変換のルーチンを破棄して、新たに書き直しました。
新しいルーチンでは、内部的に座標を全て実数(固定小数点数)で扱っていますし、TJSからの制御でも頂点に実数での指定を受け付けるので、前の実装でありがちだった「回転させると微妙にガタガタする」ような現象は避けられると思います。
Layer.holdAlphaのデフォルトが偽になります。Layer.holdAlphaは、以前までは各種演算関数のhda引数として存在していた物で、Layer.faceがdfOpaqueの時に、描画先のαを保存するかをどうか指定する物です。これが偽になるということは、以前まで各種演算関数のhda引数とされてきた物が、暗黙のうちにfalseが指定される(描画先αが破壊される)ということになります。以前までの各種演算関数のhda引数はデフォルトで真だったので、これが変わると言うことになります。
具体的にこれで何に影響が出るかというと、KAGだとインライン画像表示に影響し、新しい版のコアだとインライン画像が表示されなくなってしまいます。ので、下位互換性のオプションとしてLayer.holdAlphaのデフォルト値を真にするオプションをつけます。新しい版のKAGではこれに対応して修正がされています。
Layer.faceがdfAddAlpha(加算アルファ合成)の場合のみ、ガンマ補正がdfAddAlpha専用のルーチンを通ります。
加算アルファ合成は加算合成の成分とアルファ合成の成分の両方を表現できますが、このルーチンの場合はいったん成分を加算合成の成分とアルファ合成の成分に変換したあと、アルファ合成の成分に対してのみガンマ補正をして、加算合成の部分はそのままにします。
ちょっと実例がないと分かりづらいかも知れませんが、加算部分についてはそもそもガンマ補正が考えづらいためこうなっています。
演算がちょっと複雑なのですが、がんばって最適化したので、一般的な画像 (完全透明部分と完全不透明部分が多くて、その中間の部分の少ないような画像)では普通のガンマ補正とあまり変わらない速度が出せています。
拡大と縮小に関してのみですが、バイリニアフィルタの実装がほぼ完了しました。といってもすべての演算に対して有効になるわけではなくて、Layer.operateStretchメソッドで type が stFastLinear 以上、mode=opaque (Layer.holdAlpha プロパティは偽)、 または mode=addalpha のときにのみ有効です。
拡大時はちゃんと補間がかかりますが、縮小時はまともな計算をしていないため、あまり綺麗ではないです。とはいってもそれほど問題にはならないとおもいますが………。
もうちょっとバイリニアフィルタの方、高速化できないか検討した後、次はアフィン変換について実装します。