2007年10月11日

[010]ラバーバンドを描画する

前回、マウスクリックで直線を描画する簡単なプログラムを作りました。しかしこのプログラムでは始点をクリックしてから終点をクリックするまで何も描画されないので線が描きずらい感じがします。

巷にあるドローソフトでは直線を描画する場合、始点をクリックしてから終点をクリックするまでの間、マウスポインタに追従して伸び縮みする下書線が描画されます。

この伸び縮みする線のことを「ラバーバンド」(rubber-band)などと呼んだりするようです。ラバーバンドとはつまり輪ゴムのことです。ゴムのように伸び縮みする線とはどのようにして描画されているのでしょうか?

今回はラバーバンドを描画する例を作ってみました。下の画像はそのスナップショットです。ウインドウの中でマウスポインタを動かすとそれに追従して左上の角から赤い線が描画されます。

Screenshot086.png

プロジェクトのソースです→RubberBandTest.tar.gz
解凍してできたディレクトリの中のRubberBandTest.prjをanjutaで開けば好きなように弄れる筈です。



どうやってラバーバンドを描画するか?

マウスの移動に伴い、線を描画し、同時に前回描画した線を消去してやれば、マウスの動きに追従して伸び縮みする線を描画することができます。

このとき「前回描画した線をどうやって消すか?」が重要な問題になってきます。

この問題に対してすぐ考え付く方法としては2つの方法があります。

第1の方法は背景色と同じ色で線を描画するという方法です。
しかしこの方法では上の例のように他に描画する図形があった場合、その図形に影響を及ぼしてしまいます。

第2の方法はDrawingArea全体を再描画させることで線を消すという方法です。この場合gtk_widget_queue_draw()メソッドを使って強制的にexposeイベントを発生させるという手段が使えます。

しかし、この方法にも問題があります。この方法ではマウスが移動するたびにDrawingArea全体を再描画するため計算負荷が大きいということです。したがって処理速度の遅いマシンでは描画領域が大きくなるとチラツキが多くなってしまいます。またこの問題とは別に描画色とおなじ色の図形があった場合、その部分で線が見えなくなるという問題もあります。


XORモードによるラバーバンドの描画

グラフィックコンテキストをXORモードに設定することでスマートにラバーバンドを実現することができます。

XORモードで描画を行った場合、設定した前景色で線を描く代わりに前景色と背景のピクセルとのビット毎のXOR演算(*註1)を行い、その結果を使って線を描画します。

XOR演算の特徴として、2回同じ演算をすると元に戻る(*註2)という性質があります。
すなわち
F=A XOR B
とすると
F XOR B = A
となります。


この性質により、XORモードで描画を行った場合、同じ場所に2度線を描くと線が消えることになります。
またこの方法では背景と同化することなく描画を行うことができます。



各ウィジットのプロパティ

ウィジットツリーはwindow1の下に直接drawingareaを貼り付けています。

[window1]
タイトル→「ラバーバンドの描画テスト」
幅→200
高さ→200
シグナル→「destroy」
ハンドラ→「gtk_main_quit」



[drawingarea]
名前→「drawingarea」
イベント→「00000000000000000100」(GDK_POINTER_MOTION_MASK)
シグナル→「realize」
ハンドラ→「on_drawingarea_realize」
シグナル→「expose_event」
ハンドラ→「on_drawingarea_expose_event」
シグナル→「motion_notify_event」
ハンドラ→「on_drawingarea_motion_notify_event」



イベント処理

ソースです→callbacks.c




今回のプログラムで、ミソとなっているのは以下の部分です。
gdk_gc_set_function(rubber_gc, GDK_XOR);

これによって、rubber_gcというグラフィックコンテキストはXORモードに設定されます。あとはこのグラフィックコンテキストを用いて線を描画すれば二度描画された線は消えるのでスマートにラバーバンドを実現できます。



(*註1)
排他的論理和(Exclusive OR,XOR)は、入力値が異なる場合に1を出力し、それ以外の場合(入力値が同じとき)には0を出力します。
AとBの排他的論理和Fは論理式としては次のように表されます。
F=A・B+A・B

真理値表
A B F
0 0 0
0 1 1
1 0 1
1 1 0



(*註2)
F=A・B+A・B
G=F・B+F・B
とするとG=Aであることは下のように真理値表を書いてみると確認できます。

真理値表
A B F G
0 0 0 0
0 1 1 0
1 0 1 1
1 1 0 1


また、ド・モルガンの定理
A+B=AB
AB=A+B

を利用して論理式を展開することでも確認することができます。

G=(A・B+A・B)・B + (A・B+A・B)・B

=A・B+A・BA・B・B

=A・B+(A+B)・(A+B)・B

=A・B+(A・B+B・B)・(A・B+B・B)

=A・B+(A・B+B)・A・B

=A・B+A・B=A



(補足)XORモードにおける実際の描画色について

XORモードでの描画の場合、実際に我々の目に見える描画色はグラフィックコンテキストに設定する前景色と背景色とのXOR演算の結果になります。すなわち、
(描画色) = (前景色) XOR (背景色)

となります。したがって、今回の例のように背景色が白([r,g,b]=[0xffff,0xffff,0xffff])で、前景色が水色([r,g,b]=[0,0xffff,0xffff])の場合、描画色は赤([r,g,b]=[0xffff,0,0])となります。

では逆にラバーバンドを任意の色、例えば赤で描きたい場合にグラフィックコンテキストの前景色はどう設定すればよいのか?
これもXORの性質を利用すれば次のように求めることができます。
(前景色) = (描画色) XOR (背景色)



下のコードは背景色と描画色からGCに設定する前景色を設定する例です。コード中の「」はビット毎のXOR演算を意味します。
posted by knyakki at 17:51| Comment(1) | TrackBack(0) | プログラミング | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
失礼いたします
Posted by エロ at 2008年01月25日 23:51
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。