閉じた図形
長方形や円を描こう
さて、前回に続いて今回は長方形などの閉じた図形を描きましょう
もちろん、複数の線を組み合わせて描くこともできます
しかし、長方形や円を描くファンクションが用意されているので
専用のファンクションによって描画したほうがはるかに効率が良いです
これらのファンクションは線の描画命令と異なりカレントポジションの影響を受けません
一見すると線を描画しているように思えますが、これは背景を塗りつぶします
これらのファンクションが受ける影響は、ブラシとペンいう属性です
ファンクションはペンで外枠を描画し、内部をブラシで塗りつぶします
ブラシの属性はデフォルトで白、ペンは黒なので、デフォルトでは線描画のように見えます
ただし、あくまで白で中を塗りつぶしていることを忘れないようにしましょう
ブラシやペンの属性の設定方法は、後ほど説明しましょう
長方形を描画するにはRectangle()ファンクションを用います
BOOL Rectangle(
HDC hdc ,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect
);
hdcは、デバイスコンテキストのハンドルを指定します
nLiftRectには長方形の左上X座標、nTopRectは左上Y座標
nRightRectは右下X座標、nBottomRectは右下のY座標を指定します
関数が成功すれば0以外、失敗すれば0が返ります
また、円を描く場合はEllipse()を用います
戻り値と引数の内容は、Rectangle()と同じです
このファンクションは楕円に外接する長方形を指定しています
グラフィックスシステムは、右と下の1ピクセルが重要になります
右下端を図形に含めるシステムもありますが、Windowsファンクションはこれを含めません
つまり、長方形を指定した時右と下の外枠は実際に指定した値より-1ピクセルの位置にあります
#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 , 200 , 200);
Rectangle(hdc , 30 , 30 , 180 , 180);
Ellipse(hdc , 30 , 30 , 180 , 180);
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;
}
まず最初にLineTo()で線を描画していますが
その後、長方形を描画して線が消されています
このことから、Rectangle()などは背景を白で塗りつぶしていることが確認できます
また、角が丸い矩形を描画することもできます
これにはRoundRect()ファンクションを用います
BOOL RoundRect(
HDC hdc,
int nLeftRect , int nTopRect,
int nRightRect , int nBottomRect,
int nWidth , int nHeight
);
hdcには、デバイスコンテキストのハンドルを
第二引数から第五引数まではRectangle()と同じです
nWidthには、角の横の丸みをつける幅、hHeightは丸みをつける縦幅です
成功すれば0以外、失敗した時は0が返ります
#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);
RoundRect(hdc , 10 , 10 , 200 , 200 , 50 , 50);
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;
}
角が縦と横にそれぞれ50pxの長さで丸くなっています
柔らかいレイアウトが必要な時などに、非常に重要な描画命令です
高度な図形
これまでは、四角や円、線などの簡単な図形でした
ここではより柔軟的な図を描く為のファンクションを覚えましょう
これまでのものでも十分高度な図を表現することができますが
Windowsは、より複雑な描画処理もOSレベルで実装しています
楕円を描くのにEllipse()を使いましたが、これだけでは不十分の場合があります
そこで、円の指定枠までの外枠を描いたりそれを閉じたりするファンクションがあります
円の指定域までの外枠を描画するにはArc()ファンクションを使います
BOOL Arc(
HDC hdc,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect ,
int nXStartArc , int nYStartArc ,
int nXEndArc , int nYEndArc
);
hdcには、デバイスコンテキストのハンドルを指定します
第二引数から第五引数までは、例によって描画する矩形を指定します
問題はその次からですが、nXStartArcには外枠の始点のX座標
nYStartArcには、始点のY座標を指定します
楕円の中心から指定座標への半直線との交点が楕円弧の始点です
同様にnXEndArcとnYEndArcには、それぞれ終点のX座標とY座標を指定します
狐が描画されれば0以外、描画されなければ0が返ります
これとまったく同じ意味で、同じ引数を受け取る関数がまだあります
扇形を描画するPie()と弓形のChord()関数です
Arc()の場合は、線描画のみで内面は塗りつぶされません
上の図の始点と終点こそが第6引数から指定する座標です
ご覧の通り、一般的には半時計回りで描画されます
NTでは方向を逆にすることも可能ですが、デフォルトの状態は左回りです
よほどのメリットがない限り、逆にするような場面には出くわさないでしょう
#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);
Arc(hdc , 10 , 10 , 200 , 200 , 0 , 100 , 110 , 0);
Pie(hdc , 210 , 10 , 400 , 200 , 0 , 100 , 310 , 0);
Chord(hdc , 410 , 10 , 600 , 200 , 0 , 100 , 510 , 0);
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;
}
ちゃんと描画されましたね
始点と終点の考え方が少し難しかったですが、理解できればなんてことないです
Rectangle()
BOOL Rectangle(
HDC hdc ,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect
);
長方形を描画します
長方形は、現在のペンで描かれ、現在のブラシで塗りつぶされます
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
戻り値 - 関数が成功すると0以外、失敗すると0
Ellipse()
BOOL Ellipse(
HDC hdc,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect
);
楕円形を描画します
楕円形は、現在のペンで描かれ、現在のブラシで塗りつぶされます
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
戻り値 - 関数が成功すると0以外、失敗すると0
RoundRect()
BOOL RoundRect(
HDC hdc,
int nLeftRect , int nTopRect,
int nRightRect , int nBottomRect,
int nWidth , int nHeight
);
角の丸い長方形を描画します
角の丸い長方形は、現在のペンで描かれ、現在のブラシで塗りつぶされます
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
nWidth - 長方形の角の丸みの幅を指定します
nHeight - 長方形の角の丸みの高さを指定します
戻り値 - 関数が成功すると0以外、失敗すると0
Arc()
BOOL Arc(
HDC hdc,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect ,
int nXStartArc , int nYStartArc ,
int nXEndArc , int nYEndArc
);
始点から終点まで半時計回りの楕円弧を描画します
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
nXStartArc - 楕円弧の始点のX座標を指定します
nYStartArc - 楕円弧の始点のY座標を指定します
nXEndArc - 楕円弧の終点のX座標を指定します
nYEndArc - 楕円弧の終点のY座標を指定します
戻り値 - 関数が成功すると0以外、失敗すると0
Pie()
BOOL Pie(
HDC hdc,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect ,
int nXRadial1, int nYRadial1,
int nXRadial2 , int nYRadial2
);
扇形を描画します
扇形は、現在のペンで描かれ、現在のブラシで塗りつぶされます
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
nXRadia1 - 楕円弧の始点のX座標を指定します
nYRadia1 - 楕円弧の始点のY座標を指定します
nXRadia2 - 楕円弧の終点のX座標を指定します
nYRadia2 - 楕円弧の終点のY座標を指定します
戻り値 - 関数が成功すると0以外、失敗すると0
Chord()
BOOL Chord(
HDC hdc,
int nLeftRect , int nTopRect ,
int nRightRect , int nBottomRect ,
int nXRadial1, int nYRadial1,
int nXRadial2 , int nYRadial2
);
弓形を描画します
弓形は、現在のペンで描かれ、現在のブラシで塗りつぶされます
hdc - デバイスコンテキストのハンドルを指定します
nLiftRect - 左上X座標を指定します
nTopRect - 左上Y座標を指定します
nRightRect - 右下X座標を指定します
nBottomRect - 右下のY座標を指定します
nXRadia1 - 楕円弧の始点のX座標を指定します
nYRadia1 - 楕円弧の始点のY座標を指定します
nXRadia2 - 楕円弧の終点のX座標を指定します
nYRadia2 - 楕円弧の終点のY座標を指定します
戻り値 - 関数が成功すると0以外、失敗すると0