구조분석2 - GImage

JUSTICE_DER·2024년 1월 29일

WinAPI

목록 보기
3/9

1. 주요 변수들 분석

#pragma once
class GImage
{
public:
    enum IMAGE_LOAD_KIND
    {
        LOAD_RESOURCE = 0,
        LOAD_FILE,
        LOAD_EMPTY,
        LOAD_END
    };
    
    typedef struct tagImage
    {
        DWORD resID;
        HDC hMemDC;
        HBITMAP hBit;
        HBITMAP hOBit;
        int width;
        int height;
        BYTE loadType;

        tagImage()
        {
            resID = 0;
            hMemDC = NULL;
            hBit = NULL;
            hOBit = NULL;
            width = 0;
            height = 0;
            loadType = LOAD_RESOURCE;
        }
    } IMAGE_INFO, * LPIMAGE_INFO;

GImage 클래스 내부에 enum을 정의했다.
GImage 클래스 내부에 struct를 정의했다.
ㄴ왜 외부에 하지 않았을까?

1-1. IMAGE_LOAD_KIND

enum : 파일을 로드하는 방식을 정의.

  • LOAD_RESOURCE = 0,
    ㄴ리소스를 사용하여 로드
  • LOAD_FILE,
    ㄴ파일경로 로드
  • LOAD_EMPTY,
    ㄴ그냥 비워두기
  • LOAD_END
    ㄴEnd임을 명시.

1-2. tagImage - 이미지 하나

struct : 이미지의 내용 정의

  • 1 DWORD resID
    ㄴ리소스를 사용하여 로드하는 경우, 리소스ID
  • 2 HDC hMemDC;
    ㄴ메모리DC
  • 3 HBITMAP hBit;
    ㄴ이전비트?
  • 4 HBITMAP hOBit;
    ㄴ현재비트?
  • 5 int width;
  • 6 int height;
    ㄴ이미지의 너비높이
  • 7 BYTE loadType;
    ㄴ파일을 로드하는 방식을 정의
        tagImage()
        {
            resID = 0;
            hMemDC = NULL;
            hBit = NULL;
            hOBit = NULL;
            width = 0;
            height = 0;
            loadType = LOAD_RESOURCE;
        } IMAGE_INFO, * LPIMAGE_INFO;

여기서 중요한건 생성자가 존재한다는 것.
구조체도 클래스와 같다.
그래서 어떤 값도 정하지 않을 시, 기본 값은 위와 같다는 것.

추가로, IMAGE_INFO로 구조체의 이름을 정의해 두었지만,
IMAGE_INFO A 라고 하면, A는 그냥 구조체 변수이고,
LPIMAGE_INFO B라고 하면, B는 포인터 변수가 된다.

따라서 포인터로 선언된 B는 동적할당이 가능해진다.
B = new ImageInfo; 이런식으로도 줄 수 있다는 말.

1-3. GImage의 멤버

1-3-1. 일반 BMP

private:
    LPIMAGE_INFO _imageInfo;
    char* _fileName;
    bool _isTrans;
    COLORREF _transColor;

실제 멤버는 위와 같다.

  • 1 LPIMAGE_INFO _imageInfo
    ㄴ포인터 구조체 변수 - 이미지의 정보를 담고있다.
    ㄴ포인터인 이유는, 이미지의 크기가 커서 힙에 관리 위함.
  • 2 char* _fileName
    ㄴ파일이름
  • 3 bool _isTrans
    ㄴ특정 색을 뺄 것인가.
  • 4 COLORREF _transColor
    ㄴ특정 색

1-3-2. 투명성이 존재하는 BMP

private:
    BLENDFUNCTION _blendFunction;
    LPIMAGE_INFO _blendImage;

일반 이미지에 더하여,
투명값인 알파값을 넣기 위한 변수를 추가한다.

_blendImage 라는 이미지구조체를 하나 더 추가한다.
ㄴ Blend연산은 두 이미지를 섞는 것을 기본으로 한다.

- BlendImage 구조체 (BYTE == unsigned char)

  • BYTE BlendOp;
    ㄴ옵션을 정함. 주로 AC_SRC_OVER = 소스를 대상에 덮어 씀.
  • BYTE BlendFlags;
    ㄴ현재는 사용되지 않으므로 항상 0으로 설정.
  • BYTE SourceConstantAlpha;
    ㄴ알파 값을 정함.
  • BYTE AlphaFormat;
    ㄴ원본을 어떻게 할지. 주로 AC_SRC_ALPHA = 소스의 알파값을 사용

추가로, 해당 BLENDFUNCTION을 사용하기 위해선,
lib를 추가해야만 한다.
#pragma comment (lib, "msimg32.lib")

1-4. 각 기능들

public:
	//init관련
    HRESULT init(int width, int height);
    HRESULT init(const DWORD resID, int width, int height, bool isTrans = false, COLORREF transColor = RGB(0, 0, 0));
    HRESULT init(const char* fileName, int width, int height, bool isTrans = false, COLORREF transColor = RGB(0, 0, 0));
    HRESULT initForAlphaBlend(void);
	void release(void);


    void setTransColor(bool isTrans, COLORREF transColor);
    
    //render관련
    void render(HDC hdc);
    void render(HDC hdc, int destX, int destY);
    void render(HDC hdc, int destX, int destY, int sourX, int sourY, int sourWidth, int sourHeight);
    void alphaRender(HDC hdc, BYTE alpha);
    void alphaRender(HDC hdc, int destX, int destY, BYTE alpha);
    void alphaRender(HDC hdc, int destX, int destY, int sourX, int sourY, int sourWidth, int sourHeight, BYTE alpha);

    inline HDC getMemDC(void) { return _imageInfo->hMemDC; }

    GImage();
    ~GImage() { };
};

1-4-1. init관련

우선 GImage 자체는 생성자에 기능이 없다.
따라서, 무조건 생성을 하고서 init을 하고 사용해야만 한다.

기본적인 init은 아래와 같다.

HRESULT GImage::init(int width, int height)
{
	// 1 
	if (_imageInfo != nullptr) this->release(); 

	// 2
	HDC hdc = GetDC(_hWnd);

	// 3
	_imageInfo = new IMAGE_INFO;
	_imageInfo->loadType = LOAD_EMPTY;
	_imageInfo->resID = 0;
	_imageInfo->hMemDC = CreateCompatibleDC(hdc);
	_imageInfo->hBit = (HBITMAP)CreateCompatibleBitmap(hdc, width, height);
	_imageInfo->hOBit = (HBITMAP)SelectObject(_imageInfo->hMemDC, _imageInfo->hBit);
	_imageInfo->width = width;
	_imageInfo->height = height;

	// 4
	_fileName = nullptr;
	_isTrans = false;
	_transColor = RGB(0, 0, 0);

	// 5
	if (_imageInfo->hBit == 0)
	{
		release();
		return E_FAIL;
	}

	// 6
	ReleaseDC(_hWnd, hdc);

	return S_OK;
}

동작방식은 크게 보자면,
1. 재할당에 대한 예외처리
2. hdc가져오기
3. hdc바탕으로 _imageInfo 채우기
4. _imageInfo 외에 다른 3개의 변수 채우기
5. hBit에 대한 예외처리
6. getDC로 얻어온 HDC를 다시 releaseDC로 해제하기.
ㄴHDC를 잠시 가져올 수 있게 하는 함수였고, 사용하면 해제해야했다.

이 중에서, 3. hdc바탕으로 _imageInfo 채우기
이게 상당히 중요해보인다.

	_imageInfo = new IMAGE_INFO;
	_imageInfo->loadType = LOAD_EMPTY;
	_imageInfo->resID = 0;
    
	_imageInfo->hMemDC 
    = CreateCompatibleDC(hdc);
    
	_imageInfo->hBit 
     = (HBITMAP)CreateCompatibleBitmap(hdc, width, height);
     
	_imageInfo->hOBit 
   	 = (HBITMAP)SelectObject(_imageInfo->hMemDC, _imageInfo->hBit);
     
	_imageInfo->width = width;
	_imageInfo->height = height;

생성하면, 어짜피 아래처럼 기본값이 존재하지만,
일일이 다 설정한다

  • CreateCompatibleDC
    ㄴ지정된 DC와 호환되는 DC(메모리 디바이스 컨텍스트)를 생성
    ㄴDC는 출력에 필요한 모든 정보를 가진 구조체를 의미.
    ㄴHDC는 DC를 가리키는 핸들인데,
    여기서는 실제 출력할 화면이 들어오고,
    최종적으로 해당 화면과 호환되는 DC를 생성하게 된다.
  • CreateCompatibleBitmap
    ㄴ지정된 DC와 호환되는 비트맵 생성.
  • SelectObject
    ㄴ지정된 DC에 개체를 선택
    ㄴ여기서는 hMemDC에 hBit를 넣는다.
    ㄴ그리고, SelectObject의 반환값으로 기존 비트맵은
    hOBit에 저장된다.

hdc와 호환성만 따졌다.
그러면 생각해본다. mem을 출력하느건가??

1-4-2. render관련

destX, destY가 있는 render를 본다.

void GImage::render(HDC hdc, 
int destX, int destY, int sourX, int sourY, 
int sourWidth, int soutHeight)
{
	if (_isTrans)
	{
		GdiTransparentBlt
		(
			hdc,
			destX,					
			destY,
			sourWidth,
			sourHeight,
			_imageInfo->hMemDC,
			sourX,
			sourY,
			sourWidth,
			soutHeight,
			_transColor
		);
	}
	else
	{
		BitBlt(hdc, destX, destY, sourWidth, soutHeight,
			_imageInfo->hMemDC, sourX, sourY, SRCCOPY);
	}
}

동작은 크게,
isTrans가 true면, GdiTransparentBlt
false면, BitBlt 를 수행한다.

GdiTransparentBlt면 투명처리할 색을 지정하여 원본DC에서 대상DC로 전송.
여기서 대상 DC는, GameNode의 WM_PAINT에서
hdc = BeginPaint(hWnd, &ps); 로 가져온 HDC이고,
게임을 실제 출력하는 DC를 의미하게 된다.
(MainGame의 hdc와 같음)

원본 DC는 init에서 생성한, hMemDC가 되겠다.

hMemDC -> HDC 로의 복사이고,
HDC의 destX, destY의 위치에서 이미지의 크기만큼의 영역을
hMemDC의 sourX, sourY 위치에서 이미지의 크기만큼의 영역으로 복사하여 대체하겠다.는 의미.

BitBlt는 그냥 투명 없이 영역 복사. 빠르다.

1-5. 사용 예시

profile
Time Waits for No One

0개의 댓글