C++/MFC

[MFC] 그림 그리기(그림판)

balabala 2021. 8. 24. 21:55
728x90
SMALL
- 설명 - 
프로젝트 명 : Paint(단일 문서)
① 자유곡선, 직선, 사각형, 타원을 선택해서 그리기 가능
② 선 색 및 도형 채우기 색 설정 가능
③ 툴바와 메뉴바 이용

 

1. MFC 애플리케이션 옵션 설정
애플리케이션 종류 : 단일 문서
프로젝트 스타일 : MFC standard
비주얼 스타일 및 색 : Windows Native/Default

그림1. MFC 애플리케이션 옵션 설정

 

2. 메뉴바 재설정 및 툴바 적용
해당 프로젝트 "res" 폴더에 툴바(Toolbar.bmp) 파일 변경
기존 메뉴바를 삭제 및 재생성(ID : IDR_MAINFRAME)

그림2. 기존 메뉴바 삭제
그림3. 메뉴바 재생성
그림4. 메뉴바 설정 및 ID명

3. 툴바 적용
각 툴바 아이콘 ID 변경
Resource.h 및 Paint.rc 코드 열어서 ID 코드값 부분 수정하기
  • 툴바 파일

그림5. 툴바 아이콘 ID 수정 방법
그림6. 각 툴바 아이콘의 ID
그림7. Resource.h 파일 코드 수정
그림8. Paint.rc 파일 코드 수정

4. PaintView.h 작업
메시지 함수 선언
 - OnChangeTool() : 툴바 항목이 선택될 때 호출
 - OnUpdateChangeTool() : 현재 선택한 툴바 항목과 일치 여부에 따라 체크 표시

멤버 변수 선언
 - COLORREF m_colLine, m_colFill, m_colLineXor : 색 관련 변수
 - int m_nType : 그리기 타입
 - CPoint m_pntOld, m_pntCur : 그리기 좌표

그림9. 메세지 함수 선언
그림10. 멤버 변수 선언

  • PaintView.h 코드 보기
더보기
    // 툴바와 메뉴바를 라디오 버튼처럼 다루기
    // '범위'를 지정해서 각종 컨트롤을 사용할 수 있도록 함
    afx_msg void OnChangeTool(UINT wParam);
    afx_msg void OnUpdateChangeTool(CCmdUI* pCmdUI);

    COLORREF m_colLine, m_colFill;    // 색
    COLORREF m_colLineXor;            
    int m_nType;                    // 그리기 타입
    CPoint m_pntOld, m_pntCur;        // 그리기 좌표
 

 

5. PaintView.cpp 작업
추가한 메시지 함수 MESSAGE_MAP에 추가
메세지 함수 구현

그림11. 메세지 함수 MESSAGE_MAP에 추가
그림12. 메세지 함수 구현

  • PaintView.cpp 코드 보기
더보기
ON_COMMAND_RANGE(ID_FREELINE, ID_ELLIPSE, OnChangeTool)
ON_UPDATE_COMMAND_UI_RANGE(ID_FREELINE, ID_ELLIPSE, OnUpdateChangeTool)

// 툴바 항목이 선택될 때 호출
// GetCurrentMessage()->wParam 구문은 클릭한 툴바항목의 아이디를 리턴
void CPaintView::OnChangeTool(UINT wParam) {
    m_nType = GetCurrentMessage()->wParam;
}

// 현재 선택한 툴바항목과 일치여부에 따라 체크 표시
void CPaintView::OnUpdateChangeTool(CCmdUI* pCmdUI) {
    pCmdUI->SetCheck(m_nType == (int)pCmdUI->m_nID);
}
 
SMALL
6. 색깔 결정 메세지 함수 및 그리는 작업 메세지 함수 추가
OnLinecolor() : 선 색
OnFillcolor() : 도형 색 채우기
OnLButtonDown() : 마우스 포인터가 눌린 좌표를 저장
OnLButtonUp() : 마우스가 움직이는 동안 그려지던 고무줄 도형은 마우스 왼쪽 버튼을 때는 순간 실제 브러시를 생성시켜 내부 색이 채워진 도형을 그림
OnMouseMove() : WM_MOUSEMOVE에서 사각형을 그려주며 WM_LBUTTONUP에서 마지막에 그려진 사각형의 내부 색을 채워줌

그림13. ID_LINECOLOR 메세지 함수

  • OnLinecolor() 소스 보기
더보기
// 선 색
void CPaintView::OnLinecolor()
{
    // TODO: 여기에 명령 처리기 코드를 추가합니다.
    CColorDialog dlg;
    if (dlg.DoModal() == IDOK) {
        m_colLine = dlg.GetColor();                                                                                    // 선택한 색을 COLORREF라는 구조체 형식으로 리턴
        m_colLineXor = (RGB(GetRValue(m_colLine) ^ 255, GetGValue(m_colLine) ^ 255, GetBValue(m_colLine) ^ 255));    // 고무줄 효과를 위해 Xor 연산 처리
    }
}
 

 

그림14. ID_FILLCOLOR 메세지 함수

  • OnFillcolor() 소스 보기
더보기
// 도형 색 채우기
void CPaintView::OnFillcolor()
{
    // TODO: 여기에 명령 처리기 코드를 추가합니다.
    CColorDialog dlg;
    if (dlg.DoModal() == IDOK) {
        m_colFill = dlg.GetColor();
    }
}
 

그림15. WM_LBUTTONDOWN 및 WM_LBUTTONUP 메세지 함수

  • OnLButtonDown() 소스 보기
더보기
// 마우스 포인터가 눌린 좌표를 저장
void CPaintView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    // 그리기 좌표를 마우스 클릭한 좌표로 초기화
    m_pntOld = point;
    m_pntCur = point;

    SetCapture();

    CView::OnLButtonDown(nFlags, point);
}
 
  • OnLButtonUp() 소스 보기
더보기
// 마우스가 움직이는 동안 그려지던 고무줄 도형은 마우스 왼쪽 버튼을 때는 순간 실제 브러시를 생성시켜 내부 색이 채워진 도형을 그림
void CPaintView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    ReleaseCapture();

    CClientDC dc(this);

    CBrush brush, * pOldBrush;
    if (m_nType == ID_RECTANGLE) {
        brush.CreateSolidBrush(m_colFill);

        pOldBrush = (CBrush*)dc.SelectObject(brush);

        dc.Rectangle(m_pntOld.x, m_pntOld.y, point.x, point.y);
    }
    else if (m_nType == ID_ELLIPSE) {
        brush.CreateSolidBrush(m_colFill);
        pOldBrush = (CBrush*)dc.SelectObject(brush);
        dc.Ellipse(m_pntOld.x, m_pntOld.y, point.x, point.y);
    }

    CView::OnLButtonUp(nFlags, point);
}
 

그림16. WM_MOUSEMOVE 메세지 함수

  • OnMouseMove() 소스 보기
더보기
// 고무줄 효과 및 채우기 색 고려
// WM_MOUSEMOVE에서 사각형을 그려주며 WM_LBUTTONUP에서 마지막에 그려진 사각형의 내부 색을 채워줌
void CPaintView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    // 왼쪽 버튼을 누른 상태일 때
    if (nFlags & MK_LBUTTON) {    
        CClientDC dc(this);

        // 펜과 브러쉬 속성을 변경
        CPen pen, * pOldPen;
        CBrush brush, * pOldBrush;

        // 자유곡선 그리기
        if (m_nType == ID_FREELINE) {
            // 선택한 선색으로 펜을 생성
            pen.CreatePen(PS_SOLID, 1, m_colLine);
            // 생성한 펜을 선택
            pOldPen = (CPen*)dc.SelectObject(&pen);

            // 직전 좌표에서 현재 좌표까지 선을 그림
            dc.MoveTo(m_pntOld.x, m_pntOld.y);
            dc.LineTo(point.x, point.y);

            // 현재 좌표를 저장
            m_pntOld = point;
            dc.SelectObject(pOldPen);
        }
        // 직선 그리기
        else if (m_nType == ID_LINE) {
            // Xor 된 색으로 펜 생성
            pen.CreatePen(PS_SOLID, 1, m_colLineXor);

            // NULL 브러쉬(투명 브러쉬) 선택
            dc.SelectObject(GetStockObject(NULL_BRUSH));
            
            // 그리기 모드를 XOR 모드로 변경
            dc.SetROP2(R2_XORPEN);
            
            // 선색으로 펜 생성
            pOldPen = (CPen*)dc.SelectObject(&pen);
            
            // 이전 그림 지우기
            dc.MoveTo(m_pntOld.x, m_pntOld.y);
            dc.LineTo(m_pntCur.x, m_pntCur.y);

            // 새 그림 그리기
            dc.MoveTo(m_pntOld.x, m_pntOld.y);
            dc.LineTo(point.x, point.y);

            dc.SelectObject(pOldPen);
        }
        // 사각형 그리기
        else if (m_nType == ID_RECTANGLE) {
            // Xor 된 색으로 펜 생성
            pen.CreatePen(PS_SOLID, 1, m_colLineXor);

            // NULL 브러쉬(투명 브러쉬) 선택
            dc.SelectObject(GetStockObject(NULL_BRUSH));

            // 그리기 모드를 XOR 모드로 변경
            dc.SetROP2(R2_XORPEN);

            // 선색으로 펜 생성
            pOldPen = (CPen*)dc.SelectObject(&pen);

            // 이전 그림 지우기
            dc.Rectangle(m_pntOld.x, m_pntOld.y, m_pntCur.x, m_pntCur.y);

            // 새 그림 그리기
            dc.Rectangle(m_pntOld.x, m_pntOld.y, point.x, point.y);

            dc.SelectObject(pOldPen);
        }
        // 원 그리기
        else if (m_nType == ID_ELLIPSE) {
            pen.CreatePen(PS_SOLID, 1, m_colLineXor);
            dc.SelectObject(GetStockObject(NULL_BRUSH));
            dc.SetROP2(R2_XORPEN);
            pOldPen = (CPen*)dc.SelectObject(&pen);

            dc.Ellipse(m_pntOld.x, m_pntOld.y, m_pntCur.x, m_pntCur.y);

            dc.Ellipse(m_pntOld.x, m_pntOld.y, point.x, point.y);

            dc.SelectObject(pOldPen);
        }
        m_pntCur = point;
    }
    CView::OnMouseMove(nFlags, point);
}
 

 

7. 실행화면

728x90
LIST