컴퓨터/Win32-API
이제 처음에 목표치에 거의 도달한 것 같습니다.
우선 버튼에 이미지를 삽입하고, 그리기 영역을 제한하도록 합시다.
우선 버튼이미지가 필요합니다.
이미지는 웹상에 무료 라이선스 이미지를 찾아서 구하시면 됩니다.
구하기 힘드시면 위 아이콘을 참조하세요.
우선 리소스로 2개의 아이콘을 추가해줍니다.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++에서 생성한 포함 파일입니다.
// Paint.rc에서 사용되고 있습니다.
//
#define IDC_MYICON 2
#define IDD_PAINT_DIALOG 102
#define IDM_ABOUT 104
#define IDR_MAINFRAME 128
#define IDI_ICON1 129
#define IDI_ICON2 130
#define IDC_STATIC -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 131
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
이 아이콘을 이용해서 버튼에 이미지를 씌우는 함수를 만들어 주도록 합시다.
void CreateButtonImg(const WCHAR* buttonName,int imgName, HWND hWnd, HINSTANCE hInst)
{
HICON hIcon = LoadIcon(hInst, MAKEINTRESOURCE(imgName));
SendMessageW(FindWindowExW(hWnd, NULL, L"button", buttonName), BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
}
추가로 버튼 스타일도 약간의 변경이 필요합니다.
따라서 버튼을 생성하는 함수부분도 수정을 해주도록 합시다.
void CreateButton(const WCHAR* name,LONG x,LONG y,LONG width ,LONG height, HMENU id, HWND hWnd, HINSTANCE hInst)
{
CreateWindowW(L"button", name, WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_PUSHLIKE | BS_ICON, x,y,width,height, hWnd, id, hInst,NULL);
}
이후 버튼을 생성하는 과정에서 같이 호출해 주도록 합니다.
case WM_CREATE:
{
//버튼 생성
CreateButton(L"Pen", 20, 20, 50, 50, (HMENU)1, hWnd, hInst);
CreateButton(L"Erase", 20, 75, 50, 50, (HMENU)2, hWnd, hInst);
//버튼 생성 및 이미지 씌우기
CreateButtonImg(L"Pen", IDI_ICON2, hWnd, hInst);
CreateButtonImg(L"Erase", IDI_ICON1, hWnd, hInst);
//색상 테이블 생성
CreateRGBTable(L"RGBtable", 80, 30, 150, 120, (HMENU)10, hWnd, hInst);
//더블 버퍼링을 위한 메모리 셋팅
CreateBackPage(hWnd, hInst, &memDC, &memBitmap);
break;
}
그리기를 하는 영역을 지정해주는 것은 다양한 방법이 있지만 가장 간단한 방법은 그리기를 시작할 때 포지션을 읽고 그리기 영역이면 그리기를 해주면 됩니다.
따라서 WM_MOUSEMOVE에서 그리기 영역을 추가해주도록 합시다.
간단하게 각종 선택 버튼이 있는 영역을 제한하기 위해서 stPos.y >= 150 조건을 추가해 줍시다.
/*각종 마우스 동작에 따른 컨트롤*/
case WM_MOUSEMOVE:
{
if (wParam == MK_LBUTTON && stPos.y >= 150) {
//그리기 모드
if (GetFunction(wParam, lParam, hWnd) == 1)
{
stPos = Draw(hWnd, memDC, wParam, lParam, stPos);
HDC hdc = GetDC(hWnd);
RECT rect;
GetClientRect(hWnd, &rect);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
}
//지우기 모드
else if (GetFunction(wParam, lParam, hWnd) == 2)
{
stPos = Eraser(hWnd, memDC, wParam, lParam, stPos);
HDC hdc = GetDC(hWnd);
RECT rect;
GetClientRect(hWnd, &rect);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
}
}
break;
}
// Paint.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "Paint.h"
//함수 원형 선언 부분
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// 전역변수
HINSTANCE hInst;
HDC memDC;
HBITMAP memBitmap;
POINT stPos;
// WinMain함수 부분
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.
/*윈도우 설정 및 메인 윈도우를 생성합니다.*/
HWND hWnd = InitMainWindowSet(hInstance, &WndProc, L"Paint");
//정상적으로 생성되었다면 윈도우를 갱신합니다.
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
// 기본 메시지 루프입니다:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
// WndProc 함수부분
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//윈도우 생성 메세지
case WM_CREATE:
{
//버튼 생성
CreateButton(L"Pen", 20, 20, 50, 50, (HMENU)1, hWnd, hInst);
CreateButton(L"Erase", 20, 75, 50, 50, (HMENU)2, hWnd, hInst);
//버튼 생성 및 이미지 씌우기
CreateButtonImg(L"Pen", IDI_ICON2, hWnd, hInst);
CreateButtonImg(L"Erase", IDI_ICON1, hWnd, hInst);
//색상 테이블 생성
CreateRGBTable(L"RGBtable", 80, 30, 150, 120, (HMENU)10, hWnd, hInst);
//더블 버퍼링을 위한 메모리 셋팅
CreateBackPage(hWnd, hInst, &memDC, &memBitmap);
break;
}
//메인 기능 그리기 Or 지우기
case WM_COMMAND:
{
//토글 버튼 처리하기
SetFunction(wParam, lParam, hWnd);
//각종 버튼에 따른 동작
break;
}
//스크롤 기능
case WM_HSCROLL:
{
SetScrollFunction(wParam, lParam);
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
break;
}
/*각종 마우스 동작에 따른 컨트롤*/
case WM_MOUSEMOVE:
{
if (wParam == MK_LBUTTON && stPos.y >= 150) {
//그리기 모드
if (GetFunction(wParam, lParam, hWnd) == 1)
{
stPos = Draw(hWnd, memDC, wParam, lParam, stPos);
HDC hdc = GetDC(hWnd);
RECT rect;
GetClientRect(hWnd, &rect);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
}
//지우기 모드
else if (GetFunction(wParam, lParam, hWnd) == 2)
{
stPos = Eraser(hWnd, memDC, wParam, lParam, stPos);
HDC hdc = GetDC(hWnd);
RECT rect;
GetClientRect(hWnd, &rect);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdc);
}
}
break;
}
case WM_LBUTTONDOWN:
{
//좌 클릭시 초기 좌표 값 저장
stPos.x = GET_X_LPARAM(lParam);
stPos.y = GET_Y_LPARAM(lParam);
break;
}
case WM_LBUTTONUP:
{
break;
}
case WM_RBUTTONDOWN:
{
break;
}
case WM_RBUTTONUP:
{
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
SetColor(hWnd,memDC,memBitmap);
RECT rect;
GetClientRect(hWnd, &rect);
BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
DeleteObject(memBitmap);
DeleteDC(memDC);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
#pragma once
#include <Windowsx.h>
#include "framework.h"
#include "Paint.h"
//초기 구조 적용 함수
HWND InitMainWindowSet(HINSTANCE hInstance, WNDPROC WndProc,const WCHAR* name);
//버튼 생성 함수
void CreateButton(const WCHAR* name, LONG x, LONG y, LONG width, LONG height, HMENU id, HWND hWnd, HINSTANCE hInst);
void CreateButtonImg(const WCHAR* buttonName, int imgName, HWND hWnd, HINSTANCE hInst);
//색상 선택 함수
void CreateRGBTable(const WCHAR* name, LONG left, LONG top, LONG right, LONG bottom, HMENU id, HWND hWnd, HINSTANCE hInst);
void SetColor(HWND hWnd, HDC hdc, HBITMAP memBitmap);
//더블버퍼링 초기 셋팅 함수
void CreateBackPage(HWND hWnd, HINSTANCE hInst, HDC* memDC, HBITMAP* memBitmap);
// 그리기 or 지우기 버튼 토글 함수
void SetFunction(WPARAM wParam, LPARAM lParam, HWND hWnd);
int GetFunction(WPARAM wParam, LPARAM lParam, HWND hWnd);
void SetScrollFunction(WPARAM wParam, LPARAM lParam);
// 그리기 or 지우기 기능
POINT Draw(HWND hWnd, HDC hdc, WPARAM wParam, LPARAM lParam, POINT stPos);
POINT Eraser(HWND hWnd, HDC hdc, WPARAM wParam, LPARAM lParam, POINT stPos);
#include "MySource.h"
//윈도우 창 설정
HWND InitMainWindowSet(HINSTANCE hInstance, WNDPROC WndProc,const WCHAR* name)
{
//윈도우 창 구조체 설정 및 적용하기
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 = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = name;
wcex.hIconSm = NULL;
RegisterClassExW(&wcex);
//해당 윈도우 창을 가지고와서 윈도우 창 생성하기
return CreateWindowW(name, name, WS_MAXIMIZE| WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
100, 100, 750, 750, nullptr, nullptr, hInstance, nullptr);
}
//버튼생성하기
void CreateButton(const WCHAR* name,LONG x,LONG y,LONG width ,LONG height, HMENU id, HWND hWnd, HINSTANCE hInst)
{
CreateWindowW(L"button", name, WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_PUSHLIKE | BS_ICON, x,y,width,height, hWnd, id, hInst,NULL);
}
void CreateButtonImg(const WCHAR* buttonName,int imgName, HWND hWnd, HINSTANCE hInst)
{
HICON hIcon = LoadIcon(hInst, MAKEINTRESOURCE(imgName));
SendMessageW(FindWindowExW(hWnd, NULL, L"button", buttonName), BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon);
}
//색상 선택 도구 만들기
void CreateRGBTable(const WCHAR* name, LONG left, LONG top, LONG right, LONG bottom, HMENU id, HWND hWnd, HINSTANCE hInst)
{
//RGB의 스크롤 바 생성 및 범위 설정
CreateWindowW(L"scrollbar", L"R", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+15, right + 30, 15, hWnd, id, hInst, NULL);
SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL, 0, 255, TRUE);
CreateWindowW(L"scrollbar", L"G", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+40, right + 30, 15, hWnd, id+1, hInst, NULL);
SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL, 0, 255, TRUE);
CreateWindowW(L"scrollbar", L"B", WS_CHILD | WS_VISIBLE | SBS_HORZ, right+20, top+65, right + 30, 15, hWnd, id+2, hInst, NULL);
SetScrollRange(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL, 0, 255, TRUE);
//SetColor(hWnd);
}
//메모리 DC 및 비트맵에 메모리 할당 하기
void CreateBackPage(HWND hWnd, HINSTANCE hInst, HDC* memDC , HBITMAP* memBitmap)
{
HDC hdc = GetDC(hWnd);
*memDC = CreateCompatibleDC(hdc);
RECT rect;
GetClientRect(hWnd, &rect);
*memBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
(HBITMAP)SelectObject(*memDC, *memBitmap);
FillRect(*memDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
ReleaseDC(hWnd, hdc);
}
//색상 선택
void SetColor(HWND hWnd,HDC memdc,HBITMAP memBitmap)
{
//스크롤 Bar의 값 받아오기
int R = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL);
int G = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL);
int B = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL);
HBRUSH newBrush = CreateSolidBrush(RGB(R, G, B));
HBRUSH OldBrush = (HBRUSH)SelectObject(memdc, newBrush);
//선택된 색상 보여주기
Rectangle(memdc, 80, 30, 150, 120);
//스크롤 Bar의 값 표시해주기
WCHAR text[20];
wsprintf(text, L"R : %d ", R );
TextOutW(memdc, 360, 40, text, lstrlenW(text));
wsprintf(text, L"G : %d ", G );
TextOutW(memdc, 360, 70, text, lstrlenW(text));
wsprintf(text, L"B : %d ", B );
TextOutW(memdc, 360, 100, text, lstrlenW(text));
SelectObject(memdc, OldBrush);
DeleteObject(newBrush);
}
//그리기
POINT Draw(HWND hWnd, HDC hdc, WPARAM wParam, LPARAM lParam,POINT stPos)
{
//스크롤 Bar의 값 받아오기
int R = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"R"), SB_CTL);
int G = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"G"), SB_CTL);
int B = GetScrollPos(FindWindowExW(hWnd, NULL, L"scrollbar", L"B"), SB_CTL);
HPEN newPen = CreatePen(PS_SOLID, 10, RGB(R, G, B));
HPEN oldPen = (HPEN)SelectObject(hdc, newPen);
POINT pos;
pos.x = GET_X_LPARAM(lParam);
pos.y = GET_Y_LPARAM(lParam);
MoveToEx(hdc, stPos.x, stPos.y, NULL);
LineTo(hdc, pos.x, pos.y);
SelectObject(hdc, oldPen);
DeleteObject(newPen);
stPos.x = pos.x;
stPos.y = pos.y;
return stPos;
}
//지우기
POINT Eraser(HWND hWnd, HDC hdc, WPARAM wParam, LPARAM lParam, POINT stPos)
{
HPEN newPen = CreatePen(PS_SOLID, 10, RGB(255, 255, 255));
HPEN oldPen = (HPEN)SelectObject(hdc, newPen);
POINT pos;
pos.x = GET_X_LPARAM(lParam);
pos.y = GET_Y_LPARAM(lParam);
MoveToEx(hdc, stPos.x, stPos.y, NULL);
LineTo(hdc, pos.x, pos.y);
SelectObject(hdc, oldPen);
DeleteObject(newPen);
stPos.x = pos.x;
stPos.y = pos.y;
return stPos;
}
//이미지 씌우기
//더블버퍼링
//색상선택
//버튼 제어
//토글 값 셋팅
void SetFunction(WPARAM wParam, LPARAM lParam,HWND hWnd)
{
if (HIWORD(wParam) == BN_CLICKED)
{
switch (LOWORD(wParam))
{
case 1:
{
if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_SETCHECK, BST_UNCHECKED, 0);
return ;
}
else {
SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);
}
}
case 2:
{
if (SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_UNCHECKED) {
SendMessageW((HWND)lParam, BM_SETCHECK, BST_CHECKED, 0);
SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_SETCHECK, BST_UNCHECKED, 0);
return ;
}
else {
SendMessageW((HWND)lParam, BM_SETCHECK, BST_UNCHECKED, 0);
}
}
} // 스위치문 종료
}
return ;
}
//토글 값 받아오기
int GetFunction(WPARAM wParam, LPARAM lParam, HWND hWnd)
{
// 1 == 그리기 2 == 지우기 없을 시 0
if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Pen"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 1;
if (SendMessageW(FindWindowExW(hWnd, NULL, L"button", L"Erase"), BM_GETCHECK, BST_CHECKED, 0) == BST_CHECKED) return 2;
else return 0;
}
//스크롤 동작
void SetScrollFunction(WPARAM wParam, LPARAM lParam)
{
switch (LOWORD(wParam))
{
case SB_LINELEFT:
SetScrollPos((HWND)lParam, SB_CTL, max(0,GetScrollPos((HWND)lParam,SB_CTL)-1), TRUE);
break;
case SB_PAGELEFT:
SetScrollPos((HWND)lParam, SB_CTL, max(0, GetScrollPos((HWND)lParam, SB_CTL) - 5), TRUE);
break;
case SB_LINERIGHT:
SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 1), TRUE);
break;
case SB_PAGERIGHT:
SetScrollPos((HWND)lParam, SB_CTL, min(255, GetScrollPos((HWND)lParam, SB_CTL) + 5), TRUE);
break;
case SB_THUMBTRACK:
SetScrollPos((HWND)lParam, SB_CTL, HIWORD(wParam), TRUE);
break;
}
}
//등등 기능을 쪼개서 하나씩 추가할 예정
다음번에 최종적으로 소스코드를 다듬고, 전체적인 평가를 하며 마무리하겠습니다.
WindowsAPI - Dialog Boxes(대화상자) (0) | 2022.04.27 |
---|---|
WindowsAPI - 실습 그림판 구현하기 종합 (마지막 / 정리) (0) | 2022.04.23 |
WindowsAPI - 실습 - 그림판 구현 5 (기능 구현 / 더블 버퍼링) (0) | 2022.04.19 |
WindowsAPI - 실습 - 그림판 구현하기 4 (버튼 제어 추가 & 오류 수정) (0) | 2022.04.19 |
WindowsAPI - 실습 - 그림판 구현하기 3 ( UI부분 구현 ) (0) | 2022.04.15 |
91년생 공학엔지니어의 개발일지
TODAY :
YESTER DAY :
TOTAL :
Commnet