DC (2), DC의 pen 변경하기

Yama·2024년 1월 23일
0

어소트락 수업

목록 보기
53/55

DC (2)

화면에 그리는 Pen

void CEngine::progress()
{
	HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
};

  • 첫번쨰 인자로는 펜의 형태의 매크로를 준다

    • PS_SOLID 직선을 긋는 가장 기본적인 펜이다. 펜스타일 솔리드
  • 두번쨰 인자로는 펜의 두께 설정하는 인자

    • 1이면 1픽셀이다.
  • 세번쨰 인자로는 펜의 색깔

    • RGB 색상의 가짓수는 천육백만 가짓수다.(256256256)
    	// 매크로
    	((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
      	// 컴파일 되면 입력된 인자로 바뀐다.
      	((COLORREF)(((BYTE)(255) | ((WORD)((BYTE)(0)) << 8)) | (((DWORD)(BYTE)(0)) << 16)))
    • RGB는 컴파일될떄 저런식으로 컴파일되서 바뀐다.

      • R은 8바이트에 채워넣고 G은 8칸을 비트 쉬프트 연산을 해서 채우고 B는 16칸을 비트 쉬프트 연산을해서 채운다.

      • R->G->B 역순으로 잡았다.

      • |(합연산)을 통해 합쳐진다.

  • 펜도 커널 오브젝트이다.

	CreateCompatibleBitmap(/*인자 생략*/);
  • 비트맵을 걍 만들수있지만 원도우 화면에 매칭이 안되어 있어서 화면에 보이지 않는다.
    • 메모리 상에는 존재하겟지만 화면에 보이지않는다.
	m_hDC = GetDC(m_hMainWnd);
  • 생성되는 DC의 렌더링 타켓이 원도우 비트맵이다.

DC의 보유 정보

  • GetDC 로 생성되는 DC 의 정보

    • 목적지 비트맵 - 입력 원도우의 비트맵

      • DC란 그림가방이라고 생각하면 된다(그림 그릴떄 재료).
      • BlackPen(Default)
    • 브러쉬

      • White Brush(Default)
      • 블랙펜과 흰색브러쉬가 기본이라 색깔 지정안하면 검은색 테두리에 안은 흰색으로 채워졌던것.
  • 필요한 정보가 DC안에 있다.

    • 원도우는 인자값으로 가진 DC는 목적지가 원도우 비트맵이고 저 사각형이다.

Ren펜만들기.

	void CEngine::progress()
	{
		//Ellipse(m_hDC, 50, 50, 150, 150);

		HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

		// 1
		//HPEN hPrevPen = (SelectObject)(m_hDC, hRedPen);
        // 1.1
       	HPEN hPrevPen = (HPEN)(SelectObject)(m_hDC, hRedPen);
        
		Rectangle(m_hDC, 50, 50, 150, 150);
	}
  • 1번과 1.1처럼 SelectObject함수로 오브젝트를 DC가 선택한다.
    • DC가 선택을 해서 그 오브젝트에 렌더링 가능해졌다.

      • 첫번째 인자로는 DC값

      • 두번째 인자는 펜을 적어준다.(지금은 빨간펜)

    • 기본의 검은펜을 빨간펜으로 바꾸면 원래 들고있던 검은색 펜을 반환한다.

    • 1번은 오류가 나서 1.1번처럼 핸들값으로 강제 캐스팅해서 오류 안나게 한다.

      • SelectObject는 펜과 브러쉬 둘다 넣을수 있다 펜을 브러쉬랑 전부다 핸들이긴 하지만 종류가 달라서 핸들타입이 다르다.

      • SelectObject함수는 입력으로 펜을 넣으면 펜을 줘야되고 브러쉬를 넣으면 브러쉬를 줘야한다.

      • 반환타입을 통합으로 써야해서 HGDIOBJ를 쓰지만 그냥 무시하고 펜을 넣었으니 HPEN으로 캐스팅해서 쓴다.

코드의 문제점

  1. 지속적인 실행
  • 프로세스는 단순히 한 번 실행되고 끝나는 것이 아니라, 계속해서 실행됩니다.
    • 특히 게임과 같은 애플리케이션에서 중요한 사항입니다.
    • 게임은 지속적으로 화면을 갱신하고 사용자의 입력을 처리해야 하므로, 코드는 지속적으로 반복 실행됩니다.
  1. 펜 생성 문제
  • 코드에서는 매번 도형을 그릴 때마다 새로운 펜을 생성할 수 있습니다.
  • 이는 펜이나 다른 그래픽 리소스와 같은 커널 오브젝트를 계속해서 만들게 됩니다.
    • 만약 이러한 오브젝트를 적절히 관리하고 해제하지 않는다면, 메모리 누수가 발생할 수 있습니다.
  1. 메모리 문제
  • 반복적인 오브젝트 생성은 결국 메모리 문제로 이어질 수 있습니다.
  • 생성된 커널 오브젝트가 지워지지 않고 계속 쌓이게 되면, 시스템의 메모리 사용량이 급격히 증가하고, 이는 성능 저하 또는 시스템의 불안정성을 초래할 수 있습니다.

문제 해결 코드

	void CEngine::progress()
	{
		// RedPen 생성
		HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

		// DC 에 RedPen 입력, 원래 Black Pen 되돌려받아둠
		HPEN hPrevPen = (HPEN)SelectObject(m_hDC, hRedPen);

		// 사각형 그림
		Rectangle(m_hDC, 50, 50, 150, 150);
		
        // 1
		// BlackPen 입력
		SelectObject(m_hDC, hPrevPen);
		
        // 2
		// RedPen 삭제
		DeleteObject(hRedPen);
	}
  • 1번처럼 블랙펜을 입력을 받는다.

    • 원래 펜을 지어주고
  • 2번처럼 레드펜을 삭제요청을 한다.(커널오브젝트라 요청)

  • 레드펜 생성 -> 블랙펜받아두고 레드펜 입력 -> 사각형 그림 -> 레드펜에 블랙펜 덧씌우고 -> 레드펜 삭제

    • 엄청난 속도로 덧 그리고 있는 것이다.
  • 비효율적이다.

    • 빨간펜을 자주쓸거면 한번만 만들어 놓고 쓰다가 프로그램 종료할떄 일관적으로 종료하는 구조가 더 좋을 것이다.
// CEngine.h의 CEngine클래스
private:
	HWND		m_hMainWnd;		// 메인 윈도우 핸들
	POINT		m_Resoulution;	// 메인 윈도우 해상도
	HDC			m_hDC;			// 메인 윈도우 DC
	// 1
	HPEN		m_arrPen[];
  • 1번처럼 자주 사용할것 같은 펜을 배열로 관리 할 것이다.
    • 배열의 갯수를 정하지 않았으므로 enum.h파일로 만든다.
//enum.h 내부
enum PEN_TYPE
{
	PEN_RED,	// 0
	PEN_GREEN,	// 1
	PEN_BLUE,	// 2
	
    // 1
	PEN_END,	.// 3
};
  • 열거형은 4바이트로 정수로 취급하지만 enum_type을 이용하면 특정 케이스별로 원래라면 숫자를 줘야하는데 enum값에 명시된 타입명으로 숫자를 특정 문자로 전환해서 사용할수 있다.

  • #define이랑 다른점 별도의 타입처럼 취급한다.

    • 펜타입으로 열거해놓는다면 열거되어있는 펜타입에 해당하는 애들만 입력되게 제한할 수 있다.
  • 1번처럼 END를 끝에 선언해 놓으면 배열의 갯수로 야무지게 사용가능하다.

	HPEN		m_arrPen[/*PEN_TYPE::*/PEN_END];
  • 요렇게 사용가능해짐.
// pch.h 내부
#include "define.h"
#include "enum.h"
  • 디파인 헤더보다 아래에 두어야한다.
    • enum.h에서 define.h의 정의해둔 매크로 기능을 순서상 사용할수 있을 수도 있어성.
// CEngine.cpp 의 내부 
	m_arrPen[PEN_RED] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    // 1
    //m_arrPen[0] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
	m_arrPen[PEN_GREEN] = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
	m_arrPen[PEN_BLUE] = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
  • 초기화를 해둔다.

  • 3개짜리 배열이니까 첫번쨰 배열에는 빨간색 두번째는 초록색 세번째는 파란색 펜을 넣을것이다.

    • 이름이자 배열내에 인덱스가 된다.
  • 배열에다가 이니셜 라이저 할떄 미리 펜을 생성해둘수 있다.

  • 1번처럼 0으로 적어두면 0이 무슨 의미인줄 모르기 때문에 가독성 좋게 RED_PEN으로 적어둔다.

	// 레드펜 
	HPEN hPrevPen = (HPEN)SelectObject(m_hDC, m_arrPen[PEN_RED]);

	// 사각형그림
	Rectangle(m_hDC, 50, 50, 150, 150);

	// 블랙펜 입력
	(SelectObject)(m_hDC, hPrevPen);
  • 미리 펜을 만들었으니 펜을 코드마다 일일이 만들필요없이 가져오기만 하면된다.

  • 프로그램 돌아갈때는 이제 삭제할 필요가 없다.

// CEngine.cpp 내부
CEngine::~CEngine()
{
	// DC 삭제
	ReleaseDC(m_hMainWnd, m_hDC);
	// 1
	// Pen 삭제
	for (int i = 0; i < PEN_END; ++i)
	{
		DeleteObject(m_arrPen[i]);
	}
}
  • 1번처럼 이제 프로그램이 끝날떄 소멸자에서 모든펜을 제거해주면 된다.
    • 좋은점 새로운 색상의 펜을 늘려도 전체를 삭제하는 코드는 변경할 필요가 없다.
// CEngine.h의 CEngine 클래스 내부
public:
		HPEN GetPen(PEN_TYPE _type) { return m_arrPen[_type]; }
  • CEngine.cpp파일말고 다른데에서 Pen이 필요하면 Pen을 달라고해야되서 펜달라는 GetPen인라인 함수를 만들었다.

    • PEN_TYPE으로 명시해두면 enum.h안에 PEN_TYPE에 명시된 아이들만 들어올 수 있다.

    • 자료형이랑 비슷하다 컴파일러는 4바이트 정수로 취급을 하지만 여기에다가 20(enum안에 없다)을 이렇게 숫자를 넣을 수는 없다.

    • 펜타입으로 선언했기 떄문에 정수로 타입으로 보이긴하지만 문법상 enum값으로 타입을 선언하면 변수안에는 enum값만 넣을수 있다.

// main.cpp
    // Engine 초기화
    if (FAILED(CEngine::GetInst()->init(g_hWnd, POINT{ 1280, 768 })))
    {
        // Engine 초기화 실패 ==> 프로그램 종료
        MessageBox(nullptr, L"엔진 초기화 실패", L"에러 발생", MB_OK);
        return 0;
    }

    // 1
    HPEN Green = CEngine::GetInst()->GetPen(PEN_GREEN);
  • 엔진 초기화 이후에 1번처럼 사용 가능하다.
  • 1번에서처럼 엔진으로부터 그린펜 핸들을 얻어낼수 있다.

더 편한 방법

  • 펜을 어쨋건 가져다 써야한다. 갖다 쓰고 렌더링하고 원래 핸들로 돌려놓아야한다.

    • 필요한 순간에만 펜을 얻어다 쓰고 다시 펜을 날려야한다.
  • 이런것도 하기 귀찮다.

  • 까먹지 않고 안돌려놓으면 내가 썻던 사각형을 DC가 들고있으니까 다른 물체가 렌더링할떄 당연히 검은색 물체가 있을줄 알고 렌더링하는데 저 DC를 갖다 썻는데 파란색으로 써졌다면 아 파란색 안돌려놨네 하고 왔다갔다하면서 코드 수정해야한다.(귀찮다는 소리)

  • 원래 꼭 해줘야하는것은 실수의 여지가 생기고 귀찮아 진다.

08. Module 필터 추가.

CSelectObj 클래스

class CSelectObj
{
private:
	HDC			m_DC;
	HGDIOBJ		m_hPrev;

public:
	// 1
	CSelectObj(HDC _dc, HGDIOBJ _SelectedObj);
	~CSelectObj();


};
  • 모듈이라 모든 클래스는 CEntity를 상속받아야하지만 애는 간단하게 쓰일 클래스라 상속받지 않겠다.

  • CSelectObj클래스 맴버 변수

    1. DC
    2. m_hPrev
      • 핸들 변수다.
      • HGDIOBJ 범용적으로 모든 핸들 종류를 다 받을수 있는놈이다.
      • 포인터로 치면 void같은놈이다.
  • 기본 생성자는 아무것도 없으면 생성되지만 오버로딩된 생성자가 하나라도 잇으면 기본 생성자 안만들어 준다.

    • 역발상으로 기본생성자를 호출을 불가능하게 막을 수 있다.

    • 클래스의 객체를 생성할떄 실수로라도 기본 생성자를 만들어지는것 막는다.

      • 1번처럼이 객체가 만들어질떄 반드시 인자를 2개를 무조건 넣어주면서 생성되어야 한다면 인자 2개받는 경우의 생성자 만들고 기본 생성자 안만들면 된다.
  • 두가지 인자는

    1. DC
    2. HGDIOBJ오브젝트로 여기다가 넣어줄 선택된 오브젝트의 핸들값을 넣어줄것이다.
// CSelectObj.cpp 
#include "pch.h"
#include "CSelectObj.h"


CSelectObj::CSelectObj(HDC _dc, HGDIOBJ _SelectedObj)
	// 1
	: m_DC(_dc)
	, m_hPrev(nullptr)
{
	// 2
	m_hPrev = SelectObject(m_DC, _SelectedObj);
}
// 3
CSelectObj::~CSelectObj()
{
	SelectObject(m_DC, m_hPrev);
}
  • 1번처럼 초기화는 입력으로 들어온 _dc는 넣어주고 m_hPrev에는 아무것도 안넣을것이다.

  • 2번 대입은 SelectObject함수는 입력으로 들어온 m_DC와 _SelectedObj의 입력으로 들어온 dc를 선택하게 한다.

    • 너 이거 골라 동일한 타입의 반대 버전의 애가 뛰쳐나온다.
    • 레드펜이 들어왔다면 dc한테 펜을 줬더니 원래 들고잇던 펜을 준다.
    • 그걸 프리뷰에 저장을 시킨다.
  • 3번의 소멸자에서는 SelectObject함수로 m_DC한테 예전에 들고잇던걸 돌려준다.

// pch.h 내부
	#include "CSelectObj.h"
  • 미리 컴파일 헤더에 추가.
// CEngine.cpp 내부
	void CEngine::progress()
	{
    	// 1
		//CSelectObj select(m_hDC, m_arrPen[BLUE_PEN]);
        // 2
		//CSelectObj (m_hDC, m_arrPen[PEN_BLUE]);
		// 3
		USE_PEN(m_hDC, PEN_BLUE);
		// 4
		Rectangle(m_hDC, 50, 50, 150, 150);
	}
  • CSelectObj가 dc가 선택할 블루펜을 넣어줘서 생성자에서 SelectObject함수를 호출해서 DC가 입력으로 들어간 펜을 고른다.

    • 입력으로 넣어준 DC가 입력으로 넣어준 애(PEN_BLUE)를 선택을 한다.
    • m_hPrev에는 전에 DC가 들고있던 대응되는 같은 타입의 원래 물체가 들어온다.
    • 1번만 작성하고 4번에서 사각형 그리면 끝난다.
  • 중단 걸고 보면 입력으로 들어가서 입력으로 들어온 DC로 입력으로 들어온 블루펜을 선택하면서 원래 DC가 나오고 사각형 그리고 소멸자 와서 이전에 받아뒀던 걸로 다시 선택을 한다.(사진 요약)

    • 클래스의 객체를 만들어두면 생성자가 호출이 되고 소멸자는 지역변수일 경우 알아서 호출해주니까 그 방법을 차감해서 모듈화를 했다.
    • 귀찮게 저런 위의 방법들을 기억할 필요가 없고 저 구문자체도 귀찮다 CSelectObj클래스를 알고있어야하고 변수를 선언하면서 ()인자를 넣어줘야하니까 귀찮다.
      • 2번방법으로 가능했다.
  • 2번은 임시객체를 해서 하면 경고를 준다.

    • 최근 컴파일러에서 막음
    • 이름없는 객체를 주면 경고를 주면서 하지말라고 함.
  • 2번보다는 3번처럼 변수명을 주긴주데 자주 반복 사용될거같은 구문이니까 매크로로 바꾼다.

//define.h 의 내부
	#define USE_PEN(DC, PEN_TYPE) CSelectObj select(DC, CEngine::GetInst()->GetPen(PEN_TYPE))
  • USE_PEN매크로를 만든다.

    • DC랑 PEN_TYPE타입 명시해주면을 인자로 주고했다.

    • 펜의 타입이라 생각을 하고 두번쨰 인자는 PEN_TYPE으로 주고 그 펜타입을 받아오게

    • 엔진 밖에서도 써야해서 요런식으로 매크로.

  • 펜사용하는게 간단해 졌다. 펜타입만 알려주면 된다.

브러쉬(BRUSH) 만들깅

	void CEngine::progress()
	{
		USE_PEN(m_hDC, BLUE_PEN);
        
		// 1
		HBRUSH hRedBrusg = CreateSolidBrush(RGB(255, 0, 0));
	
		// 2
		HBRUSH hPrevBrush = (HBRUSH)SelectObject(m_hDC, hRedBrusg);

		// 3
		Rectangle(m_hDC, 50, 50, 150, 150);
		
		// 4
		SelectObject(m_hDC, hPrevBrush);
		// 5
		DeleteObject(hRedBrusg);
	}

  • 펜처럼 브러쉬도 1번처럼 CreateSolidBrush함수를 통해 만들수 있다.
    • 인자로는 색깔만 주면 된다.
  • 2번처럼 SelectObject함수를 통해 현재 빨간색브러쉬 dc주고 이전 dc를 hPrevBrush변수에 받아둔다.
  • 3번 사각형 그리고
  • 4번 SelectObject함수로 오브젝트해서 dc가 원래 브러쉬 선택하고 한다.
  • 5번 만든 브러쉬를 지운다.
    • 테두리는 파란색에 안은 빨간색이 된다.
	void CEngine::progress()
	{
		Ellipse(m_hDC, 50, 50, 150, 150);

		USE_PEN(m_hDC, BLUE_PEN);
		
        // 1
		//HBRUSH hHollowBrush = GetStockObject(HOLLOW_BRUSH);
        // 2
		HBRUSH hHollowBrush = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
		HBRUSH hPrevBrush = (HBRUSH)SelectObject(m_hDC, hRedBrusg);

		HBRUSH hPrevBrush = (HBRUSH)SelectObject(m_hDC, hHollowBrush);

		// 사각형 그림
		Rectangle(m_hDC, 50, 50, 150, 150);
		
		SelectObject(m_hDC, hPrevBrush);
        // 3
		//DeleteObject(hRedBrusg);
	}
  • 1번은 오류나서 HBRUSH로 강제 캐스팅한 2번은 오류 안난다.

  • 정말로 내부가 비어있는 브러쉬다. 색칠하지 않는 브러쉬

    • 색상을 주면 칠하기때문에 그런 브러쉬는 OS차원에서 미리 만들어둔다.
  • GetStockObject함수는 이미 저장되어 있는 물체를 가져온다는 함수다.

    • OS가 미리미리 만들어둔 가져와서 쓸수 있다.

    • 보이드 포인터처럼 아무핸들이나 받는다. 정확히 브러쉬를 캐스팅해서 가져온다.

    • 3번은 이제 원래 OS가 만들어둔 브러쉬를 사용하니까 내가 직접 지울 필요가 없다.

      • 이전걸로 되돌려 놓데 지우면 안된다는 소리다.
  • 저게 안이 정말로 내부가 비어있는 건지 아님 흰색으로 칠한지 몰르니까 Ellipse(m_hDC, 50, 50, 150, 150); 추가해서 테스트해서 깜빡깜빡 안하면 HOLLOW_BRUSH를 사용한것이다.

    • 안깜빡거림.

USE_BRUSH 매크로를 이용한 개선하기 위한 노력

	void CEngine::progress()
	{
		USE_PEN(m_hDC, PEN_RED);
		USE_BRUSH(m_hDC, BRUSH_BLACK);

		Rectangle(m_hDC, 50, 50, 150, 150);
	}
  • 이게 가능할려면
// enum.h 내부이다.
enum PEN_TYPE
{
	// 생략
};

enum BRUSH_TYPE
{
	BRUSH_RED,
	BRUSH_GREEN,
	BRUSH_BLUE,

	BRUSH_HOLLOW,
	BRUSH_BLACK,

	BRUSH_END,
};
  • 브러쉬의 종류를 열거형에 나열해준다.
// CEngine.h CEngine클래스 내부
private:
	HWND		m_hMainWnd;		// 메인 윈도우 핸들
	POINT		m_Resoulution;	// 메인 윈도우 해상도
	HDC			m_hDC;			// 메인 윈도우 DC
	HPEN		m_arrPen[PEN_END];
	// 1
	HBRUSH		m_arrBrush[BRUSH_END];
  • 브러쉬 배열을 만둔다.
// CEngine.cpp 내부
 CEngine::CEngine()
	: m_hMainWnd(nullptr)
	, m_Resoulution{}
	, m_hDC(nullptr)
    // 1
	, m_arrPen{}
    // 2
	, m_arrBrush{}
{}
  • 1번과 2번에 배열 만들떄 쓰레기 값 안들어가게 생성자 초기화를 해준다.
// CEngine.cpp 내부
	// 자주 사용할 펜 생성
	m_arrPen[RED_PEN] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
	m_arrPen[GREEN_PEN] = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
	m_arrPen[BLUE_PEN] = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));


	// 5.6 자주 사용할 브러쉬 생성.
	m_arrBrush[BRUSH_RED] = CreateSolidBrush(RGB(255, 0, 0));
	m_arrBrush[BRUSH_GREEN] = CreateSolidBrush(RGB(0, 255, 0));;
	m_arrBrush[BRUSH_BLUE] = CreateSolidBrush(RGB(0, 0, 255));;
	m_arrBrush[BRUSH_HOLLOW] = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
	m_arrBrush[BRUSH_BLACK] = (HBRUSH)GetStockObject(BLACK_BRUSH);
  • 자주 사용할 펜과 브러쉬를 미리 생성과 OS에서 만든걸 받아왔다.
  • BRUSH_HOLLOW와 BRUSH_BLACK브러쉬는 얻어다가 쓰는거라 내가 직접 해제할 필요 없다.
// CEngine.cpp의 소멸자 안
	for (int i = 0; i < 3; ++i)
	{
		DeleteObject(m_arrBrush[i]);
	}
  • 만든 3개의 브러쉬만 지워주면 된다.
  • 소멸자에서 만든 브러쉬를 다 돌면서 제거한다.
// CEngine.h
	HBRUSH GetBrush(BRUSH_TYPE _type) { return m_arrBrush[_type]; }
  • 사용할때도 GetBrush함수가 다른곳에서 브러쉬 가져갈수있게 인라인 함수를 만들어둔다.
	{
		USE_PEN(m_hDC, PEN_RED);
        // 1
		CSelectObj select (m_hDC, CEngine::GetInst()->GetBrush(BRUSH_BLACK));
        // 2
        USE_BRUSH(m_hDC, BRUSH_BLACK);
		Rectangle(m_hDC, 50, 50, 150, 150);
	}
  • 1번처럼 이제 클래스를 이용해서 미리 만들어두거나 가져온 브러쉬를 사용할수 있당.
// 1
#define USE_PEN(DC, PEN_TYPE) CSelectObj select(DC, CEngine::GetInst()->GetPen(PEN_TYPE))
// 2
#define USE_PEN(DC, TYPE) CSelectObj SelectPen(DC, CEngine::GetInst()->GetPen(TYPE))
// 3
#define USE_BRUSH(DC, TYPE) CSelectObj select(DC, CEngine::GetInst()->GetBrush(TYPE))
// 4
#define USE_BRUSH(DC, TYPE) CSelectObj selectBrush(DC, CEngine::GetInst()->GetBrush(TYPE))
  • 1번과 3번의 변수명을 select 동일한 지역변수라고 하면 1번과 3번 둘다 select라는 객체를 만들고 클래스의 생성자를 쓸것이다. 근데 또 select를 쓰면 select지역변수가 2개라 안된다. 그래서 구분을 해야함
    • 2번과 4번처럼 객체명을 다르게 해서 객체명이 겹치는것도 막았다.
  • 1번처럼 작성하면 매크로에서 입력된 치환 타입(PEN_TYPE)과 enum.h에 잇는 거가 일치해서 모호성이 생겨서 걍 애매하게 TYPE라고 적는다.
	{
		USE_PEN(m_hDC, PEN_RED);
        USE_BRUSH(m_hDC, BRUSH_BLACK);
		Rectangle(m_hDC, 50, 50, 150, 150);
	}
  • 매크로를 이용해 간단하게 만들었다.

구조는 이해는 가지만 바로바로 안 나온다.

  • 이런식이니까 쓰기 편하겠다 하면 최종적으로는 쉬워졌다.
  • 펜이나 브러쉬필요하면 저렇게 쓸것이다.
  • 나중에 익숙해져서 처음에 한것은 기억이 안난다. 그러니까 정리를 잘하자.

강의코드

main.cpp

// 위 생략
    // Engine 초기화
    if (FAILED(CEngine::GetInst()->init(g_hWnd, POINT{1280, 768})))
    {
        // Engine 초기화 실패 ==> 프로그램 종료
        MessageBox(nullptr, L"엔진 초기화 실패", L"에러 발생", MB_OK);        
        return 0;
    }
	// 추가
    HPEN Green = CEngine::GetInst()->GetPen(PEN_GREEN);
 // 아래 생략
 
 // 위 생략
 	while (true)
	{
		// Peek 엿보다
		// 메세지가 있던 없던, 리턴된다.
		// 메세지가 있었으면 true, 메세지가 없었으면 false
		if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			// 꺼내온 메세지가 WM_QUIT 이면 프로그램 종료
			if (msg.message == WM_QUIT)
				break;

			// 꺼내온 메세지 처리
			if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{	
        	// 추기
			// 메세지가 큐에 없을 때에는 게임 코드 실행
            CEngine::GetInst()->progress();
		}
	}
 // 아래 생략

pch.h

#pragma once

// 위의 생략
// 추가된 놈들
#include "define.h"
#include "enum.h"

#include "CSelectObj.h"

define.h

#pragma once


#define SINGLE(type) public:\
						static type* GetInst()\
						{\
							static type mgr;\
							return &mgr;\
						}\
						private:\
							type();\
							type(const type& _other) = delete;\
						public:\
							~type();

#define USE_PEN(DC, TYPE) CSelectObj SelectPen(DC, CEngine::GetInst()->GetPen(TYPE))
#define USE_BRUSH(DC, TYPE) CSelectObj SelectBrush(DC, CEngine::GetInst()->GetBrush(TYPE))

enum.h

#pragma once


enum PEN_TYPE
{
	PEN_RED,
	PEN_GREEN,
	PEN_BLUE,	

	PEN_END,
};

enum BRUSH_TYPE
{
	BRUSH_RED,
	BRUSH_GREEN,
	BRUSH_BLUE,

	BRUSH_HOLLOW,
	BRUSH_BLACK,

	BRUSH_END,
};

CEngine.h

#pragma once

// 게임 최고 관리자
class CEngine
{
	SINGLE(CEngine)
private:
	HWND		m_hMainWnd;		// 메인 윈도우 핸들
	POINT		m_Resoulution;	// 메인 윈도우 해상도
	HDC			m_hDC;			// 메인 윈도우 DC


	HPEN		m_arrPen[PEN_END];
	HBRUSH		m_arrBrush[BRUSH_END];

public:
	int init(HWND _hWnd, POINT _Resolution);
	void progress();

	HPEN GetPen(PEN_TYPE _type) { return m_arrPen[_type]; }
	HBRUSH GetBrush(BRUSH_TYPE _type) { return m_arrBrush[_type]; }
};


CEngine.cpp

#include "pch.h"
#include "CEngine.h"

CEngine::CEngine()
	: m_hMainWnd(nullptr)	
	, m_Resoulution{}
	, m_hDC(nullptr)
	, m_arrPen{}
	, m_arrBrush{}
{

}

CEngine::~CEngine()
{
	// DC 삭제
	ReleaseDC(m_hMainWnd, m_hDC);

	// Pen 삭제
	for (int i = 0; i < PEN_END; ++i)
	{
		DeleteObject(m_arrPen[i]);
	}

	// Brush 삭제
	for (int i = 0; i < 3; ++i)
	{
		DeleteObject(m_arrBrush[i]);
	}
}

int CEngine::init(HWND _hWnd, POINT _Resolution)
{
	m_hMainWnd = _hWnd;
	m_Resoulution = _Resolution;

	// 윈도우 해상도 변경
	SetWindowPos(m_hMainWnd, nullptr, 0, 0, m_Resoulution.x, m_Resoulution.y, 0);

	// DC(Device Context) 생성
	// DC 란? 렌더링과 관련
	// 비트맵에 렌더링하기 위해 필요한 필수 정보 집합체
	m_hDC = GetDC(m_hMainWnd); 
	// DC 보유 정보
	// GetDC 로 생성되는 DC 의 정보
	// 목적지 비트맵 - 입력 윈도우의 비트맵
	// 펜 - BlackPen(Default)
	// 브러쉬 - White Brush(Default)


	// 자주 사용할 펜 생성
	m_arrPen[PEN_RED] = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
	m_arrPen[PEN_GREEN] = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
	m_arrPen[PEN_BLUE] = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

	// 자주 사용할 브러쉬 생성
	m_arrBrush[BRUSH_RED] = CreateSolidBrush(RGB(255, 0, 0));
	m_arrBrush[BRUSH_GREEN] = CreateSolidBrush(RGB(0, 255, 0));;
	m_arrBrush[BRUSH_BLUE] = CreateSolidBrush(RGB(0, 0, 255));;
	m_arrBrush[BRUSH_HOLLOW] = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
	m_arrBrush[BRUSH_BLACK] = (HBRUSH)GetStockObject(BLACK_BRUSH);

	return S_OK;
}

void CEngine::progress()
{
	//{
	//	// RedPen 생성
	//	HPEN hRedPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));

	//	// DC 에 RedPen 입력, 원래 Black Pen 되돌려받아둠
	//	HPEN hPrevPen = (HPEN)SelectObject(m_hDC, hRedPen);

	//	// 사각형 그림
	//	Rectangle(m_hDC, 50, 50, 150, 150);

	//	// BlackPen 입력
	//	SelectObject(m_hDC, hPrevPen);

	//	// RedPen 삭제
	//	DeleteObject(hRedPen);
	//}

	//{		
	//	// DC 에 RedPen 입력, 원래 Black Pen 되돌려받아둠
	//	HPEN hPrevPen = (HPEN)SelectObject(m_hDC, m_arrPen[BLUE_PEN]);

	//	// 사각형 그림
	//	Rectangle(m_hDC, 50, 50, 150, 150);

	//	// BlackPen 입력
	//	SelectObject(m_hDC, hPrevPen);
	//}	


	//{
	//	Ellipse(m_hDC, 50, 50, 150, 150);

	//	USE_PEN(m_hDC, BLUE_PEN);

	//	//HBRUSH hRedBrush = CreateSolidBrush(RGB(255, 0, 0));
	//	HBRUSH hHollowBrush = (HBRUSH)GetStockObject(HOLLOW_BRUSH);

	//	HBRUSH hPrevBrush = (HBRUSH)SelectObject(m_hDC, hHollowBrush);

	//	// 사각형 그림
	//	Rectangle(m_hDC, 50, 50, 150, 150);

	//	SelectObject(m_hDC, hPrevBrush);

	//	//DeleteObject(hRedBrush);
	//}

	{
		USE_PEN(m_hDC, PEN_RED);
		USE_BRUSH(m_hDC, BRUSH_BLACK);

		Rectangle(m_hDC, 50, 50, 150, 150);
	}
}

CSelectObj.h

#pragma once


class CSelectObj
{
private:
	HDC			m_DC;
	HGDIOBJ		m_hPrev;

public:
	CSelectObj(HDC _dc, HGDIOBJ _SelectedObj);
	~CSelectObj();
};

CSelectObj.cpp

#include "pch.h"
#include "CSelectObj.h"

CSelectObj::CSelectObj(HDC _dc, HGDIOBJ _SelectedObj)
	: m_DC(_dc)
	, m_hPrev(nullptr)
{
	m_hPrev = SelectObject(m_DC, _SelectedObj);	
}

CSelectObj::~CSelectObj()
{
	SelectObject(m_DC, m_hPrev);
}

1차 24.01.23
2차 24.01.24
3차 24.01.25
4차 24.01.26

0개의 댓글