#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를 정의했다.
ㄴ왜 외부에 하지 않았을까?
enum : 파일을 로드하는 방식을 정의.
struct : 이미지의 내용 정의
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; 이런식으로도 줄 수 있다는 말.
private:
LPIMAGE_INFO _imageInfo;
char* _fileName;
bool _isTrans;
COLORREF _transColor;
실제 멤버는 위와 같다.
private:
BLENDFUNCTION _blendFunction;
LPIMAGE_INFO _blendImage;
일반 이미지에 더하여,
투명값인 알파값을 넣기 위한 변수를 추가한다.
_blendImage 라는 이미지구조체를 하나 더 추가한다.
ㄴ Blend연산은 두 이미지를 섞는 것을 기본으로 한다.

추가로, 해당 BLENDFUNCTION을 사용하기 위해선,
lib를 추가해야만 한다.
#pragma comment (lib, "msimg32.lib")
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() { };
};
우선 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을 출력하느건가??
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는 그냥 투명 없이 영역 복사. 빠르다.