「Windows API/図形の描画」の版間の差分

出典: フリー教科書『ウィキブックス(Wikibooks)』
削除された内容 追加された内容
編集の要約なし
編集の要約なし
84 行 84 行


polygon命令の使用のさい、その多角形の頂点それぞれの座標位置の格納のために事前に POINT 配列という専用の配列を宣言します。
polygon命令の使用のさい、その多角形の頂点それぞれの座標位置の格納のために事前に POINT 配列という専用の配列を宣言します。

コード例は、下記のようになります。

;コード例
<source lang=c>
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: HDC を使用する描画コードをここに追加してください...

HBRUSH brasi_buhin_1;
brasi_buhin_1 = CreateSolidBrush(RGB(170, 170, 255)); // うすい青色のブラシを作成
SelectObject(hdc, brasi_buhin_1); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
SetPolyFillMode(hdc, WINDING);


static POINT tenretu[2]; // ここでPOINT配列を宣言している。配列名はtenretuにした。

tenretu[0].x = 100; tenretu[0].y = 70;

tenretu[1].x = 100 +25 ; tenretu[1].y = 70 ;

tenretu[2].x = 100 + 35; tenretu[2].y = 70 + 30;

Polygon(hdc, tenretu, 3);


EndPaint(hWnd, &ps);
}
break;
</source>

POINT配列は、配列なので配列の順番を0番目から数えるので、三角形を書きたい場合は <code>tenretu[2]</code> のようになります。





2019年10月5日 (土) 09:11時点における版

塗りつぶし

長方形を塗りつぶすには?

図形を塗りつぶしたい場合には、図形描画命令の直前に、下記のように描画のモードをブラシに設定します。

塗りつぶし専用の命令は、無いのです。

Windows API でいう「ブラシ」とは、図形の塗りつぶしの色を設定するためのツールのことです。画像作成ソフトで例えるなら、Windows API でいうブラシとは、画像作成ソフトの「バケツ」ツールに近いです。世間一般の画材でいう「ブラシ」とは、意味が違います。

			HBRUSH brasi_buhin_1;
			brasi_buhin_1 = CreateSolidBrush(RGB(255, 100, 100)); // ピンク色のブラシを作成
			SelectObject(hdc, brasi_buhin_1); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
			Rectangle(hdc, 20, 30, 50, 70); // 図形の描画

上記の2段階のブラシ作成の作業は、CreateSolidBrush はブラシの色作成のためのアドレス確保を意味し、もう片方 SelectObject は、どのウィンドウでどのブラシ色を使うかを定義するための情報を保存するアドレスの確保を意味しています。

「Rectangle」は、単に、四角形を描くための命令です。これが塗りつぶしか、それとも輪郭線だけをペンで描くのかは、このRectangle命令だけでは指定できず、この命令が来る前の設定に依存します。


当然、これらの命令は、WM_PAINT ケースにある

「// TODO: HDC を使用する描画コードをここに追加してください...」

のあるブロックで書く必要があります。


つまり、下記のコードのようになります。

case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...

			HBRUSH brasi_buhin_1;
			brasi_buhin_1 = CreateSolidBrush(RGB(255, 100, 100)); // ピンク色のブラシを作成
			SelectObject(hdc, brasi_buhin_1); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
			Rectangle(hdc, 20, 30, 50, 70); // 図形の描画

            EndPaint(hWnd, &ps);
        }
        break;


さて、上記コードを実際に描画すると、四角形の輪郭が黒色で描画されます。これは、ペンの設定が黒色になってるからです。

図形をピンク色の長方形だけにしたい場合、ペンの色もピンク色にします。

Rectangle 命令の前に、あらかじめ

			HPEN pen_buhin_1;
			pen_buhin_1 = CreatePen (PS_SOLID, 0, RGB(255, 100, 100));
			SelectObject(hdc, pen_buhin_1);

のように、ペンを指定します。 CreatePen 命令の第2引数の「0」は、ペン幅の追加の線幅です。「0」に指定しても、最低でも1ドットぶんの線幅はあります。

よって、今までの話をまとめると、

			HPEN pen_buhin_1; // ペンの設定用
			pen_buhin_1 = CreatePen (PS_SOLID, 0, RGB(255, 100, 100));
			SelectObject(hdc, pen_buhin_1);

			HBRUSH brasi_buhin_1; // ブラシの設定用
			brasi_buhin_1 = CreateSolidBrush(RGB(255, 100, 100));
			SelectObject(hdc, brasi_buhin_1);

			Rectangle(hdc, 20, 30, 50, 70); // 図形の描画

のように、なります。


典型的な画像描画の命令の手順

上記のプログラムにあるような手順の、

Create〇〇
SelectObject
実際の描画命令

とった順序は、長方形以外のほかの図形の描画などでも同様の手順になります。特別な理由の無いかぎり、この順序で命令を宣言することになるでしょう。


polygon関数とPOINT配列

Win32 API では、polygon() という命令で、任意の多角形を塗りつぶしできる。

polygon命令の使用のさい、その多角形の頂点それぞれの座標位置の格納のために事前に POINT 配列という専用の配列を宣言します。

コード例は、下記のようになります。

コード例
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...

			HBRUSH brasi_buhin_1;
			brasi_buhin_1 = CreateSolidBrush(RGB(170, 170, 255)); // うすい青色のブラシを作成
			SelectObject(hdc, brasi_buhin_1); // ウィンドウhdcと、さきほど作成したブラシを関連づけ
			SetPolyFillMode(hdc, WINDING);


			static POINT tenretu[2]; // ここでPOINT配列を宣言している。配列名はtenretuにした。

			tenretu[0].x = 100; tenretu[0].y = 70;

			tenretu[1].x = 100 +25 ; tenretu[1].y = 70 ;

			tenretu[2].x = 100 + 35; tenretu[2].y = 70 + 30;
		

			Polygon(hdc, tenretu, 3);


            EndPaint(hWnd, &ps);
        }
        break;

POINT配列は、配列なので配列の順番を0番目から数えるので、三角形を書きたい場合は tenretu[2] のようになります。


ただし、長方形と正方形については、これらの四角形を書ける Rectangle という命令でも塗りつぶしできます。


任意の図形の塗りつぶし

ExtFloodFill 命令で、たとえるならペイントソフトのバケツ塗りのような、線などで囲まれた領域の塗りつぶしをできる。詳しくは外部サイトを参照せよ。

無い機能

画面クリアの専用の命令は無い

Windows32API には、画面を初期状態に戻すようなクリア機能の専用の関数は、無いようです。(※ 参考文献:林晴彦『明快入門 visual C++ 2010』、ソフトバンククリエイティブ(出版社)、2011年3月8日 初版 第1刷、179ページ。 )

※ 参考文献のソフトバンクの本の説明対象は、正確にはMFCプログラミングの場合である。だが、 Win32API専用でも同様であろう。

Win32APIで もし画面をクリアしたい場合、さきほどの節で紹介した長方形の描画の機能などを用いて、単にウィンドウ内を塗りつぶせばいい。


画面のファイル出力の機能は無い

Win32APIに無い機能として、画面の画像をビットマップファイルなどに出力する機能は無い。

よって、どうしても、そういう機能の(あなたの制作している)アプリへの組み込みが必要なら、自作するか、ネット上で出回ってる無料ライブラリを使用することになる。

もし .NET Framework ならば SaveImage メソッドというのがある。しかし Win32API には、そういうのは無い。

Win32APIでも、画面の画像をキャプチャしてメモリなどに蓄える命令( GetShellWindow 命令 )はあるが、しかし、そうやって蓄えた画像情報を外部ファイルに出力して書き込む専用の命令は無い。

なので、C言語のバイナリ書き込み命令などを利用して、自分でビットマップ形式ファイルへの画像エンコーダーを自作することになる。このため、BITMAP構造体の仕様などを把握しておく必要がある。

関数的な処理の活用

複数個の描画を関数にしたい場合は

ソースの冒頭で関数を宣言する際、通常の関数と同じように、

void kansuu(HDC hdc){
    // ここに描画命令を書く
}

のように宣言する必要があります。引数の型と変数名が、単にハンドル型(HDC)とハンドル変数(hdc)になっただけです。通常のC言語の関数と同じです。


また、呼び出し側(親側)で、

kansuu(hdc);

のように、hdcの値を渡す必要があります(通常のC言語の関数と同じです)。

もし、上述の引数定義の手間を面倒くさがって、hdc の定義を static に変更すると、コードの他の部分の動作結果がバグります。なので、hdc は static に変更せず、ウィザードの初期設定のままにしておいてください。


当然、呼び出し側(親側)の関数のコードのある位置は、

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...

// TODO: HDC を使用する描画コードをここに追加してください...

より下の部分です。


つまり、

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: HDC を使用する描画コードをここに追加してください...

            (中略)
            kansuu(hdc);
            (中略)

のように書かれます。

For文による代用

WindowsAPIで関数を使う場合、関数の内容を記述する場所が、呼び出し元の場所から、大きく離れた場所に記述しなければならない場合があります。

このような場合、いっそ関数をつかわずに、For文で記述するという方法もあります。

WindowsAPI でも、標準C言語と同様にFor文を使えます。

For文の繰り返しの数(たとえば for (int j = 1; j <= 20; ++j) の20の部分)を十分に大きな自然数にしておいて、使い終わったら break でそのブロックを脱出すればいいだけです。

たとえば、5種類の違う作業の前に、共通する前段階の作業を5回呼び出したい場合、

for (int j = 1; j <= 20; ++j) {

	ここに共通する前段階の作業を記述 ;

	if (j == 1) { 1回目にしたい処理内容 ; }
	if (j == 2) { 2回目にしたい処理内容 ; }
	if (j == 3) { 3回目にしたい処理内容 ; }
	if (j == 4) { 4回目にしたい処理内容 ; }
	if (j == 5) { 5回目にしたい処理内容 ; 
					 break;}
}

のように書くのです。

このFor文による代用の方法は、有限の回数しか呼び出しをしない処理にしか使えません。何度使うのか不特定な場合や、ずっとループ的に処理させたい場合には、むいてない方法です。

しかし、有限の回数しか呼び出さない処理ならば、この方法でも充分なのです。


また、もし関数を使う場合でも、For文で書いた処理を関数に置き換えるのはラクです。なので、関数を作る前に、とりあえず、For文で試しに似たような処理を書くという方法もあります。


構造体は無印C言語とやや異なる

WindowsAPIでは、構造体の宣言場所(いわゆる「グローバル領域」、コード冒頭の変数宣言などの場所)と、構造体の代入場所(wWinMain関数ブロック内など)とは、離れざるを得ない。

なぜなら (WindowsAPIの)wWinMain 関数と (無印C言語の)main関数との仕組みが、違うからである。

wWinMain 関数ブロックで構造体の型を宣言しても、wWinMain 関数はアプリ起動イベント専用のローカルな関数なので、他のイベント(たとえば画面描画の関数など)の関数ブロックからは原則的に、その構造体の型を呼び出せなくなってしまうので、まったく実用的では無くなる。

しかし、一方でもしもグローバル領域で宣言した構造体ならば、その構造体は画像描画の関数などの全てのイベントで(構造体を)呼び出せる。


しかし、グローバル領域では、代入などの操作が出来ないので、構造体の各要素の初期値の代入すら出来ない。

このため、代入などは wWinMain 関数ブロック内で行う必要があるという制約がある。


そして、この制約は、WinodowsAPIにおいては、wWinMain関数周辺にコードが膨大にある状況では、構造体の宣言と初期値代入が離れるのは、とても構造体のコードが一覧しづらくなり不便である。

しかし、不便であるが、他に簡便な方法が少ないので、しかたなく、構造体の宣言は、グローバル領域で行い、構造体の各要素への初期値代入などはwWinMain関数ブロックなどで行うことになる。


これらの離れた場所にあるコードを同時に見たいなら、Visual Stuido で作業中のコードを開きつつ、メモ帳などで別のアプリでコードをテキストファイルなふどとして開いて、メモ帳のほうを離れた場所の側のコードにあわせて閲覧しよう。ただし、メモ帳側で上書き保存などをしないように。

安全のため、作業が終わったら、メモ帳を閉じてから、Visual Stuido 側でセーブ保存すると安全だろう。