Version 7.10
いろんな描画モード
「前回、前々回と C++ の方中心だったけど、今回はデバイスコンテキスト
の話に戻ります」
『難しい? 簡単?』
「簡単な方だと思うよ。まずこれ」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
// 右下に 10 ずらしてもう一度描きます。
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『ん? 前に似たのやったよね』
「ブラシを用意して楕円描くっていうの。今回はペンは特に重要じゃないか
ら使ってないけどね」
『あ、 CreateHatchBrush() 使ってる。編み目のだよね』
「そう、これは〈編み目ブラシ〉を作るためのもの。第1引数で HS_CROSS
を渡してるから、これは縦横の編み目」
『リファレンス見ると斜めなんかもあるみたいだね。でもそれだけ……』
「ま、こういうのはちょっとした作図用だからね。で、この楕円を、ひとつ
めの10ドット右下にもうひとつ描いてます」
『楕円ふたつ重ねて描いてるわけね。でもこれが?』
「試してみて」
『ほい実行。うん、ふたつ楕円が重なってるね。……あれ? でもさ、編み
目の隙間って下が透けて見えないんだね』
「そう、そこがポイント。実は、今は〈背景モード〉が〈透けて見えない
モード〉になってるんです」
『ってことは透けて見えるモードにもなる?』
「そゆこと。それを変える API が SetBkMode() 」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
// ここで背景モード変更。
::SetBkMode( hCanvasDC, TRANSPARENT );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『お、下が透ける!』
transparent は英語で〈透明〉って意味。ちなみにデフォルトの塗り潰し
モードにする時は OPAQUE 。 opaque は不透明って意味」
『オ』
「ボケ不可」
『うっ。あれ……? でもこれ、よく見るとなんか変』
「どの辺が?」
『んー……あ、縞模様が透けてない! っつーか下のと一緒に並んでるみた
い』
「そうなんだよねー。これはデバイスコンテキストの仕様で、描画場所を変
えても、ブラシの模様はずれないんだよね。だから」
::Ellipse( hCanvasDC, 13, 13, 83, 53 );
「ってふたつめの楕円をちょっとだけずらしても」
『編み目がずれない……』
「ま、やっぱりちゃんとした模様を描くなら、こういうの使わない方がい
いってことかも」
『ふーん。あ、あともうひとつ質問。 OPAQUE は透明じゃないんだよね。
ってことは編み目の隙間の色ってどうなってるの? ペンとブラシ以外で指
定するの?』
「そう、これはまた別に SetBkColor() って API で指定します」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
// ここで背景色変更。
::SetBkColor( hCanvasDC, RGB( 255, 0, 0 ) );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『うわ、編み目の隙間が真っ赤!』
「この SetBkColor() は〈背景色〉を変えるもの。単純に楕円だけでも、ペ
ンとブラシとこの背景色の3色を決める必要があるってことかな」
『もしくは透明か、ね』
「そゆこと」
『……んでもさ、 API の名前、なんか混乱しそう』
「ん? どういうこと?」
『だってさ、 SetBkColor() なんて言ったら、普通、キャンバスそのものの
色を変えるって思わない?』
「それはそうかも。 SetBkMode() もキャンバスそのものを透明にしそうだ
よね」
『そうそう。でも、この〈背景〉って、ようするにこれから塗る楕円とかそ
ういうのの背景、ってわけなのね』
「そういうことだね。この辺も、デバイスコンテキストのクセかも」
『こういうの、ひとつひとつ憶えていかなきゃいけないわけね……』
「じゃ、さらに憶えてもらいましょう」
『げげ』
「というわけで、今度は【ラスタオペレーション】ってゆーの」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
// ここでラスタオペレーションを設定します。
::SetROP2( hCanvasDC, R2_BLACK );
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『げ、ふたつめの楕円が真っ黒!』
「 SetROP2() の第2引数で R2_BLACK を渡すと、何色のペンやブラシを
持っていても真っ黒になっちゃうんです」
『げげ。で、そのラスタオペレーションってなんなの?』
「英語で書くと raster operation 。 raster は〈画面の格子〉って意味。
縦横にドットが並んでるものそのものって事かな」
『つまりドット絵ってことね……』
「 operation は操作。つまりラスタオペレーションは〈ピクセルの操作〉
って意味」
『たいそうな名前の割りには……』
「略して ROP 。で、この ROP には簡単なのから複雑なのまであるんだけ
ど、ここで使ってるのは一番簡単な ROP2 ってグループ」
『簡単だから、真っ黒だけ?』
「いや、そういうわけじゃないけど、でもシンプルなのが多いよ。真っ白に
塗り潰す R2_WHITE とか、あとデフォルトの普通に塗り潰す R2_COPYPEN と
か」
『 SetROP2() 見ると他にも結構あるけど、はっきり言って意味分かんな
いの多いかも。どういうのなの? たとえば R2_NOT とか。否定??』
「 R2_NOT は、背景色を〈逆の色〉にするもの。たとえば白は黒、青は黄
色」
『白は黒、は分かるけど、青は黄色って……そうなの?』
「逆の色っていうのは、 RGB 各色で、 0 は 255 、 100 は 155 みたく、
256 の真ん中で逆転させた色って事。青は RGB( 0, 0, 255 ) で、黄色は
RGB( 255, 255, 0 ) 」
『そういうもんなのね』
「ま、こういうのはどっちかっていうと美術の話かも。他には……」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
::SetBkColor( hCanvasDC, RGB( 0, 255, 0 ) );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
::SetROP2( hCanvasDC, R2_MASKPEN );
::SetBkColor( hCanvasDC, RGB( 255, 0, 255 ) );
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『重なってるとこが黒く!』
「 R2_MASKPEN は、色を塗り重ねてさらに濃くします。逆に R2_MERGEPEN
は、塗り重ねるとさらに薄くします」
void CAnimeDlg::OnBDraw()
{
HDC hCanvasDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
HBRUSH hBrush = CreateHatchBrush( HS_CROSS, RGB( 0, 0, 255 ) );
HBRUSH hOldBrush = (HBRUSH)::SelectObject( hCanvasDC, hBrush );
::SetBkColor( hCanvasDC, RGB( 0, 0, 0 ) );
::Ellipse( hCanvasDC, 10, 10, 80, 50 );
::SetROP2( hCanvasDC, R2_MERGEPEN );
::SetBkColor( hCanvasDC, RGB( 0, 0, 255 ) );
::Ellipse( hCanvasDC, 20, 20, 90, 60 );
::SelectObject( hCanvasDC, hOldBrush );
::DeleteObject( hBrush );
}
『ホントだ、黒が薄くなった』
「あと R2_XORPEN かな」
『試すとなんか R2_NOT に似てる……けどちょっと違うね』
「簡単に言うと、 R2_XORPEN は、塗る色と背景の色が離れてれば白に近づ
いて薄くなって、逆に色が近いと黒に近づいて濃くなります」
『ややこしい……』
「ま、これはあんまり使わないでしょ。で、あとはこれらの組み合わせ」
『って簡単に言うけど、はっきり言ってよく分かんないよ?』
「ってゆーか実際にはあまり使わないと思うよ。ちゃんとした CG ソフトは
GetPixel() で色拾って合成して SetPixel() する、みたいなことしてると
思うし」
『むしろその方が分かりやすそう……』
「デバイスコンテキストの機能って色々あるけど、こういった複雑な機能を
憶えるよりは、基本的なことの応用の方がいいかもね」
/*
Preview Next Story!
*/
『ってゆーか、もしかしてデバイスコンテキストって使いにくくない?』
「それは言えるかも。ペンの付け替えとか大変だよね」
『今回の ROP も複雑だし』
「でも、そういう細かいとこを自分でやるか任せるかっていうのは」
『難しいとこよねー。だって、 CG の勉強もしなきゃいけないでしょ?』
「そういうことになるね」
『というわけで次回』
< Version 7.11 クリッピングではみ出さない! >
「につづく!」
『どうしても左向いてる顔って描けないのよねー』
「いや、そういう勉強は必要ないから」