#include "stdafx.h"
#include "MyWindowsProject.h"
// Direct2D를 사용하기 위한 파일 포함.
#include <d2d1.h>
#pragma comment(lib, "D2D1.lib")
using namespace D2D1;
// 가로 세로 줄이 19개인 바둑판
#define X_COUNT 19
#define Y_COUNT 19
// 화면에서 바둑판의 거리
#define START_X 50
#define START_Y 50
// 줄 사이의 간격. 바둑 돌 크기
#define INTERVAL 26
// 바둑 돌의 반지름(줄 간격의 반)
#define HALF_INTERVAL INTERVAL / 2
// 바둑판에 돌이 놓일 수 있는 위치 계산
#define XPOS(x) (START_X + (x) * INTERVAL)
#define YPOS(y) (START_Y + (y) * INTERVAL)
// 바둑판에 돌이 놓여져 있는 판단하기 위한 변수 배열(0=없음,1=검은색,2=흰색)
unsigned char g_dol[Y_COUNT][X_COUNT];
// 바둑판에 놓일 돌의 색을 결정하는 변수.(0=검은색,1=흰색)
unsigned char g_step;
// Direct2D를 구성하는 각종 객체를 생성하는 객체
ID2D1Factory* gp_factory;
// Direct2D에서 윈도우의 클라이언트 영역에 그림을 그릴 객체
ID2D1HwndRenderTarget* gp_render_target;
void OnPaint(HWND hWnd)
{
// Direct2D의 render target을 사용하여 그림 그리기 시작.
gp_render_target->BeginDraw();
// Clear 함수를 사용하여 나무색으로 채움.
gp_render_target->Clear(ColorF(0.95686f, 0.69f, 0.3019f));
// brush 객체 생성.
ID2D1SolidColorBrush *p_line_brush, *p_black_brush, *p_white_brush;
gp_render_target->CreateSolidColorBrush(ColorF(0.095686f, 0.069f, 0.03019f), &p_line_brush);
gp_render_target->CreateSolidColorBrush(ColorF(0.0f, 0.0f, 0.0f), &p_black_brush);
gp_render_target->CreateSolidColorBrush(ColorF(1.0f, 1.0f, 1.0f), &p_white_brush);
D2D1_POINT_2F start_pos, end_pos;
// 수직으로 선 그리기
for (int x = 0; x < X_COUNT; x++)
{
start_pos.x = XPOS(x);
start_pos.y = YPOS(0);
end_pos.x = XPOS(x);
end_pos.y = YPOS(Y_COUNT - 1);
// (x, 0)에서 (x, Y_COUNT - 1) 연결하는 선.
gp_render_target->DrawLine(start_pos, end_pos, p_line_brush, (float)1.0f);
}
// 수평으로 선 그리기
for (int y = 0; y < Y_COUNT; y++)
{
start_pos.x = XPOS(0);
start_pos.y = YPOS(y);
end_pos.x = XPOS(X_COUNT - 1);
end_pos.y = YPOS(y);
// (0, y)에서 (X_COUNT - 1, y) 연결하는 선.
gp_render_target->DrawLine(start_pos, end_pos, p_line_brush, (float)1.0f);
}
D2D1_ELLIPSE dol_region;
dol_region.radiusX = (float)HALF_INTERVAL; // 타원의 X축(수평)방향 반지름
dol_region.radiusY = (float)HALF_INTERVAL; // 타원의 Y축(수직)방향 반지름
// 바둑판에 놓여진 돌을 각 위치별로 체크하여 그림.
for (int y = 0; y < Y_COUNT; y++)
{
for (int x = 0; x < X_COUNT; x++)
{
if (g_dol[y][x] > 0) // 바둑이 놓여져 있음(1=검은돌,2=흰돌)
{
dol_region.point.x = XPOS(x);
dol_region.point.y = YPOS(y);
// 바둑돌이 놓여질 위치 계산. 반지름이 HALF_INTERVAL인 원을 그림.
if (g_dol[y][x] == 1)
gp_render_target->FillEllipse(dol_region, p_black_brush);
else
gp_render_target->FillEllipse(dol_region, p_white_brush);
}
}
}
p_line_brush->Release();
p_line_brush = NULL;
p_black_brush->Release();
p_black_brush = NULL;
p_white_brush->Release();
p_white_brush = NULL;
// Render Target을 사용해서 그림 그리기를 중지.
gp_render_target->EndDraw();
}
void OnLButtonDown(HWND hWnd, int a_x, int a_y)
{
// 바둑판 영역에만 돌을 놓을 수 있도록 터치한 위치 체크.
if (a_x > (XPOS(0) - HALF_INTERVAL) && a_y > (YPOS(0) - HALF_INTERVAL)
&& a_x < (XPOS(X_COUNT - 1) + HALF_INTERVAL)
&& a_y < (YPOS(Y_COUNT - 1) + HALF_INTERVAL))
{
// 터치한 위치와 가까운 줄쪽으로 놓임.
int x = (a_x - START_X + HALF_INTERVAL) / INTERVAL;
int y = (a_y - START_Y + HALF_INTERVAL) / INTERVAL;
// 돌이 없는 곳에만 놓을 수 있음.
if (g_dol[y][x] == 0)
{
// g_step + 1하면 g_dol에 놓일 색상이 됨. g_step 0=검|1=흰 == g_dol 1=검|2=흰.
g_dol[y][x] = g_step + 1;
// 놓일 돌의 색상을 변경.
g_step = !g_step;
// 화면을 갱신해서 적용.
InvalidateRect(hWnd, NULL, TRUE);
}
}
}
void OnCreateRenderTarget(HWND hWnd)
{
RECT r;
GetClientRect(hWnd, &r);
gp_factory->CreateHwndRenderTarget(RenderTargetProperties(), HwndRenderTargetProperties(hWnd, SizeU(r.right, r.bottom)), &gp_render_target);
}
void OnDestoryRenderTarget()
{
if (gp_render_target != NULL)
{
gp_render_target->Release();
gp_render_target = NULL;
}
}
// 사용자가 메시지를 처리하는 함수.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// HDC h_screen_dc = ::GetDC(NULL); // 모니터 전체 화면용 DC
HDC h_dc = ::GetDC(hWnd); // 현재 윈도우용 DC
switch (message)
{
case WM_CREATE:
OnCreateRenderTarget(hWnd);
break;
case WM_PAINT:
// WM_PAINT가 다시 발생하지 않게 처리.
ValidateRect(hWnd, NULL);
OnPaint(hWnd);
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hWnd, LOWORD(lParam), HIWORD(lParam));
break;
case WM_DESTROY:
OnDestoryRenderTarget();
// 프로그램 종료
PostQuitMessage(0);
break;
default:
// 자신이 처리하지 않는 메시지들의 기본 작업을 대신 처리해주는 함수.
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, // 프로그램의 instance 핸들 값.
_In_opt_ HINSTANCE hPrevInstance, // 현재 사용안함. 항상 NULL.
_In_ LPWSTR lpCmdLine, // 하나의 문자열로 실행인자가 전달됨.
_In_ int nCmdShow) // 초기 기작 형식이 전달됨.
{
// 컴포넌트를 사용할 수 있도록 프로그램을 초기화.
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
// Direct2D를 위한 factory 객체를 생성.
if(S_OK != D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &gp_factory))
return 0;
// 윈도우 클래스 등록
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(244, 176, 77)); // 바둑판 컬러 설정
wcex.lpszMenuName = IDI_APPLICATION;
wcex.lpszClassName = L"MyWindow";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
// 윈도우 생성
HWND hWnd = CreateWindowW(L"MyWindow", L"scriptplay.tistory.com", WS_OVERLAPPEDWINDOW,
50, 50, 600, 600, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 프로그램에 전달된 메시지를 번역하고 실행하는 작업.
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) // 메시지를 큐에서 읽는 함수.
{
TranslateMessage(&msg); // 가상 키 메시지이면 ASCII 형태의 메시지를 추가로 생성.
DispatchMessage(&msg); // 변환된 메시지를 처리하는 함수.
}
// 사용하던 Factory 객체를 제거.
gp_factory->Release();
// 컴포넌트 사용을 해제.
CoUninitialize();
return (int) msg.wParam;
}
|
cs |
'::public > 윈도우즈 응용 프로그래밍' 카테고리의 다른 글
Direct2D - 렌더타겟 (0) | 2019.09.26 |
---|---|
Direct2D - 그림파일 읽고 출력 (0) | 2019.09.25 |
Direct2D 사용하기 (0) | 2019.09.25 |
Direct2D (0) | 2019.09.25 |
Timer (0) | 2019.09.24 |