Mar.18.2000
GdkPixbuf の画像を回転変換する。
画像データを任意角度に回転、拡大縮小を含めて新規に生成する関数の作成。
結果画像加工フィルターとして扱える。
GdkPixbuf(Ver0.6)には画像を拡大縮小させつつコピーする関数がありますが、 回転加工する関数はありません。ひょっとしたら将来的に用意されるのかも しれませんが今現在はないということで作成してしまいましょう。
座標の回転演算には三角関数を用います。
回転前の座標を(x,y)、回転角をθ、回転後の座標を(rx,ry)とした場合、
原点(0,0)を中心に回転する式は
rx = cosθ * x + sinθ * y ry = -sinθ * x + cosθ * yで示されます。
rot2 では 1 pixel 毎に三角関数を用いて回転演算をしているので、
処理負荷はかなりなものになっています。もうちょっと軽くはならない
ものでしょうか。
傾きの表現とその計算は 32bit unsigned int で計算します。下16bitを
小数点扱いの下駄とした固定小数点演算とします。結果、pixel座標は
16bit の表現力である 32768 未満となりますが、現実問題としてそんなに
大きな pixbuf を持つことはできないであろうから今は良しとしておきます。
回転転送の中心となる for ループの部分は、単純ループと整数加算のみで
構成されていますので実にアセンブラ向けかと :-)
上の画像群は JPEG 圧縮画像なのでわかりづらいかもしれませんが回転すると
ラインや絵のエッジがぼろぼろになります。単純に回転しただけではある意味
しょうがないところなのですが、やはり気になるものですし、
なにより画像処理に使うには汚すぎます。
そこで回転処理に加えフィルタリング処理を加えより綺麗な画像をめざします。
先の source picture の変換時その座標計算は固定小数点により処理していました。
その際小数点部分を参照し、その値を元に隣の pixel と色をブレンドします。
この処理だけで等倍および拡大時のピクセルは滑らかにフィルタリングされ
より綺麗な画像となります。
縮小時は拡大時と違ってオーバーサンプリングという手法を用います。
縮小表示時は source picture の pixel をとびとびに歯抜けの状態で拾う
ことになります。その情報欠落が縮小時の画像荒れをもたらすので、
飛ばさないように全て拾い上げ混ぜて destination picture の pixel に
反映させます。
ソースの smooth_pixel という関数でその両方がインプリメントされて
います。
拡大時のソフトエリアシングの効果は以下のような感じです。左が rot3 の画像、
右がソフトエリアシングされた rot4 の画像です。
まあ、しょうがないといえばしょうがないのですが、ピクセルフィルタリングには 相当な負荷がかかり処理は重たいです。この処理はリアルタイムでなく 画像処理に向けたインプリメントですのでケースバイケースで rot3 と使い 分けることになると思います。
今回は画像処理を目指したので精度も優先し、そこそこ画像にもこだわった
アルゴリズムやフィルタリングになっています。ところがゲームでリアルタイムに
処理しようという場合、もっと「いいかげん」でもよかったりするんですよね。
例えば回転角が64段階でも良かったり。32x32pixel でも十分だったり。
そういった場合、1 pixel ずつ真面目に計算しなくても、あらかじめ計算結果を
テーブルで持っていてテーブル参照型コピーで済ませることによりいっそうの
高速化が期待できます。もっとも汎用性はなくなりますから、状況に応じて
そういった処理を使い分けることになります。
普段気軽にアルゴリズムとか言いますけど、実際にその違いによって全く結果
が変わるなんていう場面はなかなか目にする機会は無いのではないでしょうか。
結果は同じでも途中の手順の省略の仕方によって処理時間が大きく違って
しまうものです。状況によっては省略(手を抜く)事によっていくらでも処理を
高速化できたりします。そういったあたりがプログラムの醍醐味ですよね。
もっとも表だって目立たない部分ではあるのでプログラマーの満足は自己満足と
なることが多いわけですが :-)