Version 7.11
クリッピングではみ出さない!
「今回も引き続きデバイスコンテキストの話。まずはこれから」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 255 ) );
RECT stRect;
::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &stRect );
// 縦に2倍。
stRect.bottom *= 2;
::FillRect( hCanvasDC, &stRect, hBrush );
::DeleteObject( hBrush );
}
『うお、 IDC_S_CANVAS からはみ出した!』
「はみ出るようにわざと縦に2倍してるからね」
『つまり、座標をウィンドウの外に持っていったら、そこも描画しちゃ
うってわけね』
「同じアプリケーションの中でだけどね。このように、デバイスコンテキス
トは勝手には〈はみ出ないよう注意する〉ってことをしません」
『つまり、描画するときに自分ではみ出ないかどうかチェックしろってこと
ね』
「いや、そういうわけじゃなくて、〈描いていいのはこの範囲だけ〉ってこ
とをデバイスコンテキストに指定できるんです」
『あ、それを使えば気にせずできるんだ』
「そゆこと。で、それを使うために必要なものがあるんで、まずそれを使っ
てみましょう」
『ってことは今回は憶えることが結構あるのね……』
「そゆこと。まずは【リージョン】から」
『りーじょん?』
「リージョンは英語で region 、〈領域〉って意味。デバイスコンテキスト
上を〈領域で囲む〉時に使いもの」
『そっか、それで〈描ける範囲〉を囲めばいいわけだ』
「そゆこと。と、その前にリージョンだけ使ってみます」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 255 ) );
// リージョンを作ります。
HRGN hRgn;
hRgn = ::CreateRoundRectRgn( 0, 0, 50, 50, 30, 30 );
// リージョンで囲んだ部分だけ塗り潰します。
::FillRgn( hCanvasDC, hRgn, hBrush );
::DeleteObject( hBrush );
// リージョンを削除します。
::DeleteObject( hRgn );
}
『あ、なんか面取りされた四角が出た』
「まず CreateRoundRectRgn() で、こういう面取り四角の領域を作ります。
ここで作るのは領域だけだからね」
『つまり、ここで青く塗り潰されてるとこを囲んでる線を作ってるってこ
と?』
「そう、その線だけを作ってるわけ。リージョンは枠線だけ。作っただけ
じゃ見えないけど、その分、いろんな事に使えます」
『というわけで FillRgn() で塗り潰してるわけね』
「これは FillRect() と同じく、リージョンで囲まれた範囲を塗り潰す
API 」
『で、最後に削除と。これまでの応用って感じ』
「基本的なルールは同じだからね。で、ここで重要なのは、リージョンは複
雑な図形も表せるってこと」
『そういえば、この面取り四角って結構複雑よね』
「でしょう。それに、これよりもっと複雑なのもできるから、それを使えば
それで複雑な図形の塗りつぶしとかもできるわけ」
『おお! これって結構便利そう』
「ま、その分、データの管理が面倒だけどね」
『確かに、そういう複雑な図形って使うの面倒そう……』
「ま、今回はそういう複雑なのはパス。リージョンっていう、複雑な領域を
囲む線を作れるってことを憶えといて」
『あ、領域ってことは、ちゃんと囲んでないとダメ? 途中で線が途切れて
るとか』
「っていうのはリージョンになりません。リージョンは必ず閉じてないと使
えないからね」
『確かに、塗りつぶしとかできないもんね』
「では今回の本題」
『はみ出させない方法ねー』
「基本的には今のと同じ。塗り潰す範囲をリージョンで囲んで、塗り潰すだ
け。では実際にやってみましょう」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateSolidBrush( RGB( 0, 0, 255 ) );
RECT stRect;
::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &stRect );
// リージョンを作ります。
HRGN hRgn = ::CreateRectRgnIndirect( &stRect );
// そのリージョンでクリッピングします。
::SelectClipRgn( hCanvasDC, hRgn );
// 塗りつぶしは縦に2倍。
stRect.bottom *= 2;
// 塗り潰し。
::FillRect( hCanvasDC, &stRect, hBrush );
::DeleteObject( hBrush );
::DeleteObject( hRgn );
}
『う、長……っと、最初2行は塗るとこのデバイスコンテキストを取って、
ブラシを作ってと。その次に塗るとこの範囲を取ってきてと』
「リージョンはこの範囲にすればいいわけ」
『それが CreateRectRgnIndirect() って API で一発でできちゃうわけね。
これは便利かも』
「で、ここで使うのが SelectClipRgn() って API 。これを使うと、デバイ
スコンテキストにリージョンをくっつけることができるんです」
『つまりブラシとかをくっつけるのと同じね。あれ? でもそれだったら
SelectObject() じゃないの?』
「 SelectObject() でももちろん大丈夫だよ。 SelectObject() のリファレ
ンス見てみて」
『ちゃんと CreateRectRgnIndirect() で作ったリージョンも大丈夫って書
いてあるね』
「でも SelectClipRgn() でも同じ。このリージョン、ペンとかとなんか違
うでしょ」
『そういえば、あのめんどい入れ替えがないね』
「そう。リージョンはペンなんかの他の GDI と違って、デバイスコンテキ
ストが持つのはオリジナルのコピーなんです」
『コピー?』
「そう、 SelectClipRgn() した時、 CreateRectRgnIndirect() のコピーが
作られてそれを デバイスコンテキストが持つんです」
『それで入れ替えがいらない?』
「入れ替える前のは必ずどっかで作られてるはずだからね。それに、デバイ
スコンテキストは最初、リージョンを持ってない状態だから」
『最初に持ってるのを取っておく、って必要がないわけね』
「この辺はリージョンの特別なところかな。で、あとはこれまでと同じ」
『わざとはみ出るように塗り潰しても、っと、まだ実行してなかった。実
行! お、ちゃんとはみ出てない! IDC_S_CANVAS ギリギリぴっちりまで塗
られてる!』
「あとはブラシとリージョンの削除」
『すごいねー』
「この、はみ出さない領域を作ることを【クリッピング】って言います」
『くりっぴんぐ?』
「クリッピングは英語で clipping 、切り取って抜き出したもの、切り抜き
とかそういう意味があります」
『そういえばミュージッククリップとか言うもんね』
「このふたつを組み合わせて【クリッピングリージョン】って言うのが一般
的かな。クリッピングの機能そのものは他の OS とかにもあるから」
『憶えると便利そうねー。それにしても、こういうふうに簡単にできちゃう
となんか自然な感じ』
「ちゃんとクリッピングされてるって確認するなら」
HRGN hRgn = ::CreateRectRgnIndirect( &stRect );
「を」
HRGN hRgn
= ::CreateRoundRectRgn
( stRect.left, stRect.top
, stRect.right, stRect.bottom
, 50, 50 );
『あ、角丸めバージョン』
「にすれば、角が丸まるから」
『クリッピングされてるのが分かるね。これだけで、もうはみ出る心配はな
いわけねー』
「簡単でしょ?」
『うーん、確かにひとつひとつは簡単なんだけど……』
「そ。プログラミングのポイントはそこだね。ひとつひとつは簡単。で、そ
れを守ることが大切」
『どゆこと?』
「今はただ塗り潰すだけだから CAnimeDlg::OnBDraw() の中だけでいいけ
ど、もっと複雑なことをすることになると」
『いろんなメンバ関数で色々する?』
「そういうふうになってくると、ものすごくごちゃごちゃしてきて、ひとつ
ひとつは簡単でも」
『絡み合って複雑になっちゃう』
「実は火美ちゃんが言うことももっともで、このプログラムも結構複雑な方
なんだよね」
『やっぱ、いろんなのが入ってるから?』
「そう、デバイスコンテキスト取得して、ペン用意して、リージョン作っ
て、っていっぺんにやっちゃってるからね」
『え? でもバラバラにしちゃったらごちゃごちゃになっちゃうって、さっ
き水希ちゃん言ったじゃない』
「そう、だから適度にバラバラにする、っていうか、他の方法でまとめるの
が重要かな。そのへんはまた今度ね〜」
/*
Preview Next Story!
*/
『あ、今日は短い』
「毎回200行きっかりって結構大変かも……」
『オーバーすることもあるもんね』
「今回なんかも、書くとかなりオーバーするから」
『2回に分けるわけね』
「というわけで次回」
< Version 7.12 文字を書こう! >
『につづく!』
「短い回があるってことは、全体的には増えることになるかも」
『そんなんばっかりだから……』