カレントポジションと線


線を描く

論理的には、全ての描画操作はピクセル操作につながるので
前回のピクセル操作ができれば、あらゆるグラフィックプログラムができますが
図の描画などの操作をSetPixel()で行うというのはあまりにも不合理です

当然、デバイスドライバレベルでの線の描画のほうが効率的ですし
複雑なグラフィック操作を一括して行うコードに置き換えたほうが保守性もあります

線を描画するには、まずMoveToEx()カレントポジションを決定します
線などの一部の描画命令は、このカレントポジションの影響を受けます

BOOL MoveToEx(HDC hdc , int X , int Y , LPPOINT lpPoint);

hdcには、デバイスコンテキストのハンドルを
XとYには、カレントポジションとなる座標を表すX座標とY座標を指定します
lpPointは、以前設定されていたカレントポジションが格納されます
以前のカレントポジションを得る必要がないならば、NULLを指定します
戻り値は、関数が失敗すると0、成功すれば0以外の値が返ります

線の始点はカレントポジションからはじめます
線を描画するにはLineTo()ファンクションを呼び出します

BOOL LineTo(HDC hdc , int nXEnd , int nYEnd);

hdcは、デバイスコンテキストのハンドルです
nXEndは、線の終点となるX座標、nYEndは終点となるY座標を指定します
//ただし、終点自体は描画されません
関数が成功すると0以外、失敗すると0が返ります

LineTo()ファンクションは、現在のカレントポジションから終点までの線を描き
さらにカレントポジションを終点に移動させる性質を持ちます
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		MoveToEx(hdc , 10 , 10 , NULL);
		LineTo(hdc , 110 , 10);
		LineTo(hdc , 110 , 110);
		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style			= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);


	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}


MoveToで指定したカレントポジション 10 , 10 の位置から線が描かれています
さらにLineTo()は終点にカレントポジションを移動させるので
次のLineTo()の時には、前のLineTo()の終点から始まっているのがわかりますね


連続した線の描画

さらに、連続した線の描画も可能です
5角形以上の連続した線が数多く繋がっている状態であれば
LineTo()よりもPolylineTo()ファンクションが便利です

BOOL PolylineTo(HDC hdc , CONST POINT *lppt , DWORD cCount);

このファンクションは、カレントポジションを始点として
連続したPOINT構造体の各変数の座標に線を描画します

hdcは、お馴染みのデバイスコンテキストへのハンドルです
lpptは、POINT構造体変数の配列へのポインタです
この構造体の配列の順番に、変数が指す座標を終点とした線が描画されます

cCountは、描画する線の数を指定します
lpptの長さにかかわらず、ここで指定した値を情報に
ファンクションはlpptポインタにアクセスして描画しようとします

戻り値は、関数が成功すると0以外、失敗すると0が返ります
#include <windows.h>
#define LENGTH 4

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;

	static POINT lpPoint[LENGTH];

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		lpPoint[0].x = 100;
		lpPoint[0].y = 10;
		lpPoint[1].x = 130;
		lpPoint[1].y = 100;
		lpPoint[2].x = 40;
		lpPoint[2].y = 100;
		lpPoint[3].x = lpPoint[3].y = 10;
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		MoveToEx(hdc , 10 , 10 , NULL);
		PolylineTo(hdc , lpPoint , LENGTH);
		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style			= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);


	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}


各POINT構造体変数が指す座標に連続して線を描画しています
さらに、この関数は最後に描画した線の終点をカレントポジションに設定します
ただし、カレントポジションを指定しないPolyline()ファンクションも存在します

さらに、連続した線を複数に分けて描画することもできます
連続した線を複数に分けるにはPolyPolyline()ファンクションを使います
BOOL PolyPolyline(
	HDC hdc ,
	CONST POINT *lppt ,
	CONST DWORD *lpdwPolyPoints ,
	DWORD cCount
);
最初の二つの引数はPolylineTo()と同じです
lpdwPolyPointsは、lpptの先頭から連続する線の頂点の数を指定したDWORDの配列へのポインタとなります
lpptの添え字0〜2と3〜6に分けたい場合は、3と4を格納した配列になります

cCountは、連続線分の総数を指定します
2つの連続線に分けるとしたら、2を指定すればよいのです
関数が成功すると0以外、失敗すると0が返ります

たとえば、 少しややこしかったかもしれませんね
実際のプログラムをみたほうが理解は早いでしょう
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;

	static POINT po1[5];
	static CONST DWORD dw[] = { 3 , 2 };

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		po1[0].x = 10;
		po1[0].y = 10;
		po1[1].x = 100;
		po1[1].y = 10;
		po1[2].x = 100;
		po1[2].y = 100;

		po1[3].x = 200;
		po1[3].y = 10;
		po1[4].x = 200;
		po1[4].y = 200;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		PolyPolyline(hdc , po1 , dw , 2);
		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}


いかがでしょうか
ちゃんと、POINT構造体に格納されている頂点が分裂していますね
これを利用すれば、複数の連続線を一度に描画することができます


MoveTo()

BOOL MoveToEx(HDC hdc , int X , int Y , LPPOINT lpPoint);

カレントポジションを設定します

hdc - デバイスコンテキストへのハンドルを指定します
X - 設定するカレントポジションのX座標を指定します
Y - 設定するカレントポジションのY座標を指定します
lpPoint - 以前のカレントポジションを格納します。必要ない場合はNULL

戻り値 - 成功すると0以外、失敗すると0

LineTo()

BOOL LineTo(HDC hdc , int nXEnd , int nYEnd);

カレントポジションから指定位置まで線を描画し
終点を新しいカレントポジションとして設定します

hdc - デバイスコンテキストへのハンドルを指定します
nXEnd - 終点となるX座標を指定します
nYEnd - 終点となるY座標を指定します

戻り値 - 成功すると0以外、失敗すると0

PolylineTo()

BOOL PolylineTo(HDC hdc , CONST POINT *lppt , DWORD cCount);

カレントポジションを始点として、lpptの各座標へ線を描画します
最後の線の終点を新しいカレントポジションとして設定します

hdc - デバイスコンテキストへのハンドルを指定します
lppt - 線の座標を表すPOINT構造体へのポインタを指定します
cCount - lpptの点の数を指定します

戻り値 - 成功すると0以外、失敗すると0

Polyline()

BOOL Polyline(HDC hdc , CONST POINT *lppt , int cPoints);

カレントポジションを使用しないで
連続した線を一括して描画します

hdc - デバイスコンテキストへのハンドルを指定します
lppt - 線の座標を表すPOINT構造体へのポインタを指定します
cPoints - lpptの点の数を指定します。必ず2以上です

戻り値 - 成功すると0以外、失敗すると0

PolyPolyline()

BOOL PolyPolyline(
	HDC hdc ,
	CONST POINT *lppt ,
	CONST DWORD *lpdwPolyPoints ,
	DWORD cCount
);
複数の連続線を描画します

hdc - デバイスコンテキストへのハンドルを指定します
lppt - 線の座標を表すPOINT構造体へのポインタを指定します
lpdwPolyPoints - 連続した線分が持つ点の数が格納されている配列のポインタを指定します
cCount - 連続した線分の総数を指定します

戻り値 - 成功すると0以外、失敗すると0



前のページへ戻る次のページへ