Win32_API - GDI로 선그리기2 (마우스 동작을 이용해봅시다.)

컴퓨터/Win32-API

728x90
반응형

서론

지난 글에서 MoveToEx와 LineTo 2개의 함수를 이용해서 선을 그려봤습니다. 

본문에서는 이 두 함수와 마우스 동작에 따른 처리를 하면서 마우스 동작에 따른 처리방법을 알아보고, 다양한 형태로 선을 그려보도록 합시다.

 

참조 - 이전 글

 

Win32_API - GDI로 선그리기1

서론 GDI에서는 다양한 방법으로 선 및 곡석을 그릴 수 있도록 지원해 주고 있습니다. 본문에서는 가장 기본적인 선 그리기를 위한 방법에 대해서 알아보고 사용해 보도록 합시다. 대표적으로 사

blog-of-gon.tistory.com

 

우선 마우스 메시지에 대하여 알아보자.

마우스의 동작에 따라 윈도에서는 다양한 알 림메 세지가 발생하게 됩니다. 자세한 내용은 MSDN의 마우스 내용을 살펴보시면 좋을 것 같습니다. 본문에서는 대표적으로 사용할 몇 가지 내용에 알림 메시지에 대하여 알아보겠습니다.

 

  • WM_MOUSEMOVE - 마우스가 움직일 때 발생하는 메세지
    • wparam - 가상 키 입력에 대한 동작 여부
    • lparam - x, y의 좌표값
  • WM_LBUTTONDOWN - 마우스 좌클릭을 눌렀을 때 발생하는 메시지
    • wparam - 가상 키 입력에 대한 동작 여부
    • lparam - x, y의 좌표값
  • WM_LBUTTONUP - 마우스 좌클릭을 해제할 때 발생하는 메시지
    • wparam - 가상 키 입력에 대한 동작 여부
    • lparam - x, y 좌표값

 

콜백 함수에 메시지 처리 루프 만들기

우선 콜백함수 안에 마우스 동작에 따른 메세지 처리하기 위한 소스코드를 작성합시다.

    //마우스 동작에 따른 처리
        //움직일 때
    case WM_MOUSEMOVE:
        break;
        //좌클릭 눌렀을 때
    case WM_LBUTTONDOWN:
        break;
        //좌클릭 해제할 때
    case WM_LBUTTONUP:
        break;

이제 해당 메시지에서 알맞게 선을 그리는 작업을 처리한다면 다양한 방법으로 선을 그려볼 수 있습니다.

몇 가지 예제를 작성해보며 실습해보도록 합시다.

 

예제 1 - 클릭한 위치에 선을 그려봅시다. 

좌클릭을 하면 클릭한 위치에 가로 100픽셀의 선을 그려보도록 합시다.

    case WM_LBUTTONDOWN:
        //예제 1을 위한 소스코드
        //WM_PAINT가 아닌 곳에서 DC를 불러오는 방법
        hdc = GetDC(hWnd);
        MoveToEx(hdc, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),NULL);
        LineTo(hdc, (GET_X_LPARAM(lParam)+100), GET_Y_LPARAM(lParam));
        ReleaseDC(hWnd, hdc);
        break;

간단하게 WM_LBUTTONDOWN 알림 메시지 발생 시 DC핸들을 얻어와 처리할 수 있습니다. 하지만 무효화 영역이 발생하여 WM_PAINT 알 림메 세지가 발생하면 그림을 그렸던 데이터는 사라지게 됩니다.

이런 문제는 다음 글에서 다시 극복해 보도록 합시다. 본문에서는 마우스 동작에 따른 그리기 처리를 하는 응용을 이해하는데 중점을 두었으면 좋겠습니다.

 

GET_X_LPARAM, GET_Y_LPARAM은 windowsx.h에 포함되어 있는 안전하게 마우스 좌표를 얻어오는 함수입니다.

 

 

예제 2 - 클릭하여 드래그 형태로 직선 그려보기.

이제 마우스 좌클릭을 하고 클릭을 때는 위치에 두 좌표를 얻어와서 직선을 그려보도록 합시다.

두 좌표를 기억하기 위해서 전역 변수를 통해 좌표를 기억하도록 하겠습니다.

//전역변수
POINT start_pos;
POINT end_pos;

POINT는 구조체의 형태로 x, y좌표를 담을 수 있습니다.

 

이제 알림 메시지에서 처리를 해보도록 합시다.

    case WM_LBUTTONDOWN:
        //예제 2를 위한 소스코드
        start_pos.x = GET_X_LPARAM(lParam);
        start_pos.y = GET_Y_LPARAM(lParam);
        break;
        //좌클릭 해제할 때
    case WM_LBUTTONUP:
        //예제 2를 위한 소스코드
        end_pos.x = GET_X_LPARAM(lParam);
        end_pos.y = GET_Y_LPARAM(lParam);
        hdc = GetDC(hWnd);
        MoveToEx(hdc, start_pos.x, start_pos.y, NULL);
        LineTo(hdc, end_pos.x, end_pos.y);
        ReleaseDC(hWnd, hdc);
        break;

이제 드래그의 처럼 이용해서 직선을 그릴 수 있게 됐습니다.

 

예제 3 - 그리기 과정을 볼 수 있게 하자 

예제 2의 경우에는 드래그를 하는 동안 그리기 과정을 볼 수 없습니다. 마우스가 움직이는 동작의 알 림메 세지에 약간의 소스코드를 추가하면 그리기 과정을 눈으로 볼 수 있습니다.

 

우선, 마우스 클릭을 한 시점부터 그리기를 해야 되니 클릭을 한 시점을 알 수 있도록 전역 변수를 하나 설정합니다.

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
POINT start_pos,end_pos;
    //그리기를 상태를 파악하기 위한변수
int status;

이후 status변수를 가지고 제어하도록 하면 됩니다.

//마우스 동작에 따른 처리
        //움직일 때
    case WM_MOUSEMOVE:
        //예제 3을 위한 소스코드
        if (status == 1)
        {
            end_pos.x = GET_X_LPARAM(lParam);
            end_pos.y = GET_Y_LPARAM(lParam);
            hdc = GetDC(hWnd);
            MoveToEx(hdc, start_pos.x, start_pos.y, NULL);
            LineTo(hdc, end_pos.x, end_pos.y);
            ReleaseDC(hWnd, hdc);
        }
        break;
        //좌클릭 눌렀을 때
    case WM_LBUTTONDOWN:
        /*
        //예제 1을 위한 소스코드
        //WM_PAINT가 아닌 곳에서 DC를 불러오는 방법
        hdc = GetDC(hWnd);
        MoveToEx(hdc, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam),NULL);
        LineTo(hdc, (GET_X_LPARAM(lParam)+100), GET_Y_LPARAM(lParam));
        ReleaseDC(hWnd, hdc);
        */

        //예제 2,3를 위한 소스코드
        start_pos.x = GET_X_LPARAM(lParam);
        start_pos.y = GET_Y_LPARAM(lParam);
        //예제 3을 위한 소스코드
        status = 1;
        break;
        //좌클릭 해제할 때
    case WM_LBUTTONUP:
        //예제 2,3를 위한 소스코드
        end_pos.x = GET_X_LPARAM(lParam);
        end_pos.y = GET_Y_LPARAM(lParam);
        hdc = GetDC(hWnd);
        MoveToEx(hdc, start_pos.x, start_pos.y, NULL);
        LineTo(hdc, end_pos.x, end_pos.y);
        ReleaseDC(hWnd, hdc);
        //예제 3을 위한 소스코드
        status = 0;
        break;

여기서 몇 가지 문제점이 발생하게 됩니다 예제 1과는 상반되는 문제가 발생하여 계속해서 그리기를 진행하기 때문에 아래와 같은 현상이 생깁니다.

계속해서 그림을 그리기 때문입니다. 

 

예제 4 - 문제 해결하기 

문제를 해결하기 위해서는 어떻게 해야 될까요? 

이번 예제에서는 지난 글들에서 서술한 내용들과 같이 무효화 영역과 WM_PAINT를 활용하여 극복해보도록 하겠습니다.

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            hdc = BeginPaint(hWnd, &ps);
            //예제 4를 위한 소스코드
            if (status == 1)
            {
                MoveToEx(hdc, start_pos.x, start_pos.y, NULL);
                LineTo(hdc, end_pos.x, end_pos.y);
            }
            EndPaint(hWnd, &ps);
        }
        break;
    //마우스 동작에 따른 처리
        //움직일 때
    case WM_MOUSEMOVE:
        //예제 4를 위한 소스코드
        if (status == 1)
        {
            end_pos.x = GET_X_LPARAM(lParam);
            end_pos.y = GET_Y_LPARAM(lParam);
            InvalidateRect(hWnd, NULL, TRUE);
            UpdateWindow(hWnd);
        }
        break;
        //좌클릭 눌렀을 때
    case WM_LBUTTONDOWN:

        //예제 4를 위한 소스코드
        start_pos.x = GET_X_LPARAM(lParam);
        start_pos.y = GET_Y_LPARAM(lParam);
        status = 1;
        break;
        //좌클릭 해제할 때
    case WM_LBUTTONUP:
        //예제 4를 위한 소스코드
        end_pos.x = GET_X_LPARAM(lParam);
        end_pos.y = GET_Y_LPARAM(lParam);
        status = 0;
        break;

이처럼 동작을 하면 마우스가 움직일 때마다 무효화 영역을 발생시키고, WM_PAINT에서 마우스 좌표를 확인하여 계속해서 그리는 동작을 하는 것을 확인할 수 있습니다.

 

사용자가 직선이 그러지는 모습을 실시간으로 확인할 수 있죠.

 

하지만 다시 클릭을 이용해 다른 직선을 그리면 기존에 그렸던 직선을 사라지는 문제가 발생하게 되었습니다.

 

이 해결방법은 다음 글에서 다시 서술하도록 하겠습니다.

 

728x90
반응형

Commnet

G91개발일지

Gon91(지구일)

91년생 공학엔지니어의 개발일지

TODAY :

YESTER DAY :

TOTAL :