Version 7.04
GDI とペン
「デバイスコンテキストのクセはいっぱいあるから、どんどん教えてくね」
『そんなにいっぱいあるの?』
「結構ねー。というわけで、今回はまず楕円を描いてみましょう」
void CAnimeDlg::OnBDraw()
{
::Ellipse
( ::GetDC( m_cCanvasStatic.GetSafeHwnd() )
, 10, 10, 80, 50
);
}
『これは API 版ね』
「 MFC だとこう」
void CAnimeDlg::OnBDraw()
{
CDC *pcDC = m_cCanvasStatic.GetDC();
pcDC->Ellipse( 10, 10, 80, 50 );
}
『んじゃビルドして実行。左上に楕円ができた』
「 ellipse っていうのはそのまま〈楕円〉って意味。最初ふたつの整数値
が左上の座標、あとふたつの整数値が右下の座標ね」
『 SetPixel() と同じ感じ……あれ? そういえば色は? 問答無用で黒縁
白円になってるけど』
「そこが今回のポイント。前に、デバイスコンテキストは〈画家〉って言っ
たでしょ」
『うん言った』
「この画家は、絵を描くためのペンとかを初めから持ってるんです」
『つまり楕円はそのペンで描いたってこと?』
「そゆこと。だから、色を変える時にはそのペンを持ち替えてもらう必要が
あるんです。ちょっと長いけどこんな感じ」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HPEN hPen = ::CreatePen( PS_SOLID, 10, RGB( 255, 0, 0 ) );
HPEN hOldPen = (HPEN)::SelectObject( hCanvasDC, hPen );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
::SelectObject( hCanvasDC, hOldPen );
::DeleteObject( hPen );
}
『あら長い』
「1行目は分かるね」
『デバイスコンテキストを取ってるだけね』
「2行目はペンを作ってます」
『へー、 HPEN ってハンドルだよね、ってことはペンもウィンドウズのもの
なんだ』
「そう、こういうペンとか、あとブラシとかをウィンドウズシステムとして
用意してあるんです。これを GDI ( Graphics Device Interface ) って言
います」
『じーでぃーあい、ねー』
「 Graphics は絵、 Device は道具、 Interface は接続面とかそういう意
味。まー簡単に言えば、画材道具を使うためのハンドルってことだね」
『ちょっと難しいかも……』
「ま、他の GDI も見てけば少し感覚掴めると思うから」
『で、 CreatePen() でペンを作るわけね、ウィンドウズシステムの中に。
第1引数は?』
「これはペンの種類。あ、第2引数はペンの太さなんだけど」
『あ、そういえば試してなかった。ビルドして実行、楕円の縁が赤い太線に
なった!』
「太いのは第2引数が 10 だから。これを 1 にすると」
『線が細くなった』
「で、第1引数を PS_DASH にすると」
『点線になった!』
「正確には波線だね。点線は PS_DOT の方」
『あ、こっちの方が細かい』
「第3引数は Ver 7.01 ( No.121 ) でやった RGB マクロね」
『これで色を決めるんだね』
「さて、その次」
HPEN hOldPen = (HPEN)::SelectObject( hCanvasDC, hPen );
『これまたよく分からない……』
「まず、デバイスコンテキストは、ペンだったらひとつのペンしか持てませ
ん」
『どゆこと?』
「デバイスコンテキストって画家は、手に持てるペンはたった一種類ってこ
と。ペンは今みたいにいろんな太さ、いろんな色で作ることができるんだけ
ど、これから楕円を描こうって時に持てるのは」
『ひとつだけってことね』
「そこで、 SelectObject() って API で持つペンを交換します。第1引数
は交換相手のデバイスコンテキスト、第2引数は新たに持たせるペン」
『戻り値は……それまで持ってたペン?』
「そのとおり! デバイスコンテキストがそれまで持ってたペンがあるか
ら、それを受け取って、取っておきます。これはあとで戻すから」
『めんどいねー』
「うん、この辺は面倒なとこだね。でもこれはデバイスコンテキストを使う
時のルールだから」
『戻り値を HPEN にキャストしてるのはなんで?』
「 SelectObject() は、ペンだけじゃなく他の GDI にも使うから、受け取
る時に HPEN 以外ので受け取ることもあるんで、こういうふうにキャストし
ないといけないようになってるんです」
『でも、戻り値と第2引数って同じ HGDIOBJ だよ。第2引数に渡す時って
キャストしなくていいの?』
「んー、簡単に言えば、 HPEN から HGDIOBJ には = で渡せるけど、その逆
はダメってこと」
『むつかしいね』
「ここ、ちゃんとやっとこうか。まず、はっきり言っちゃうと、ハンドルは
ポインタの一種だから」
『やっぱそうなのねー』
「でも、完全なポインタって考えない方がいい部分もあるからその辺は憶え
といて」
『はーい』
「で、もうひとつ。ポインタには void ポインタっていうのがあります」
『え? void って型がないってことじゃなかったっけ? 戻り値ないとき
に void 使うし、この前の ASSERT の時も』
「ま、それとは別って考えた方がいいかな。 void ポインタは、どんなポイ
ンタとしても使える、言ってみれば汎用ポインタ」
『なんでもポインタねー』
「だから、この void ポインタにはどのポインタからも代入できます」
void *pv = hPen;
『あ、ってことは HGDIOBJ は void ポインタなんだ!』
「そゆこと。で、今度は逆。 void ポインタは〈どのポインタ〉かは分から
ないから、この void ポインタから、普通のポインタに代入できないように
なってるんです」
『つまりこれが SelectObject() の戻り値の話ね』
「そう。もしこれが何もせずにできちゃうと、本当の型と違う型として入れ
ちゃうことになるから」
『でもキャスト間違えちゃったら』
「もちろん意味なし。はっきり言っちゃえば、こういうのはルール違反。
API はケチだから、こういうとこで関数の数を減らそうとしてるんだよね」
『ホントは SelectPen() とかって感じにそれぞれの関数があればいいって
わけね』
「ま、この辺は API を使う場合には仕方ないことだからね。で」
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
「で楕円を描いて」
::SelectObject( hCanvasDC, hOldPen );
『これはさっきの SelectObject() で、第2引数がさっき SelectObject()
で受け取った、元々デバイスコンテキストが持ってたペンだね』
「こういうふうに、ペンを使ったら、ペンを元に戻しておくわけ。で、最後
に」
::DeleteObject( hPen );
「で作ったペンを削除します」
『作ったら削除しなきゃいけないんだ』
「 CreatePen() でウィンドウズシステムの中にペンを作ると、
DeleteObject() で削除するか、アプリを終了しない限り、そのペンは消え
ないから」
『あ、でもアプリを終了させると消えるんだ』
「でも、アプリをいつ終了させるかっていうのはユーザーが決めること。も
しかしたら何日も何ヶ月も実行し続けるなんてこともあるから」
『? それが何か問題?』
「あーそっか……それはまた次回にってことで」
『はいはい』
「最後に MFC 版を見てもらいましょう」
void CAnimeDlg::OnBDraw()
{
CDC *pcDC = m_cCanvasStatic.GetDC();
CPen cPen( PS_DOT, 1, RGB( 255, 0, 0 ) );
CPen *pcOldPen = pcDC->SelectObject( &cPen );
pcDC->Ellipse( 10, 10, 80, 50 );
pcDC->SelectObject( pcOldPen );
}
『1行目はデバイスコンテキスト取得。2行目はペン作ってるんだね。
CPen ってクラスが用意されてるんだ』
「その CPen クラスを作るときに、 CreatePen() と同じようにペンを作り
ます」
『3行目でデバイスコンテキストが持ってるペンを取り替えるんだね。ポイ
ンタ渡すんだ』
「前回言ったように、ポインタを使わないと自動的に削除されちゃうから
ね。これは関数を渡す時も同じ事」
『この前の逆ってことね』
「戻り値もポインタ。この戻り値が」
『前に持ってたペンね。あ、今度はキャストいらないんだ』
「 SelectObject() が、 GDI の種類分用意されてるから」
『 API みたく手抜きしてないんだね』
「あともほとんど同じ。楕円描いて、古いペンをまた持たせて」
『でも DeleteObject() で削除してないね。あ! CPen が勝手に削除し
ちゃうんだ!』
「そゆこと。 cPen 変数がなくなるときに DeleteObject() を呼んでくれる
から、自動的に削除されるわけ」
『これは便利な例ねー』
「削除しなくてよかったり、キャストしないでいいって事を考えると、この
辺は MFC 使った方がいいかもね」
/*
Preview Next Story!
*/
『デバイスコンテキスト、って長いね』
「 DC って略しちゃうのがいいのかも」
『どっかのゲーム機みたい』
「今はもうないけど」
『あ、そういえば近未来って設定あったね……』
「というわけで次回」
< Version 7.05 ブラシとリソース >
『につづく!』
「っていうか、追い付いちゃいそうだよね……」
『最初に出てきた2人とか、今はいずこ……』
「金菜先輩くらいなら」
『だーめっ!』