[Image] 이미지 압축 클래스 제작
이미지 압축에서 기본은 RLE(Run Length Encoding)라는 압축입니다. 이는 비손실 압축방식이며
복잡한 압축 알고리즘이 많지만 보다 적용하기 간단하다는 장점이 있습니다.
그렇지만 여전히 그 조금 부족한 부분이 있는데요.
그건 바로 복잡한 패턴을 가진 이미지일경우
오히려 압축한 데이터가 원본보다 더 커진다는데 있습니다.
게임 프로그래밍에서는 엄밀히 말해서 2D에서는 이방식을 많이 사용하고 있습니다.
왜냐하면 많은 공간이 키컬러(Key Color;투명색)이 차지하는 이미지가 많기 때문이죠.
따라서 RLE 방식을 조금 개선하여 만들어본 이미지 압축루틴을 소개할까 합니다.
아울러서 고속 블렌딩 기법도 팁으로 소개도 해드리죠... 오늘은 늦은 관계로
일단 클래스 해더만 보도록 하겠습니다.
다음 이미지 해더 정보는 보통 비트맵 해더와 비슷합니다. 간단히 매직번호로
이미지데이터를 식별하도록 하며 버저정보도 지원하도록 하죠. 이때 DWORD로 하였으므로
비트연산으로 한꺼번에 매직과 버전을 dwSignature 에 넣을 수 있도록 매크로도 만들었습니다.
// JR-Image Header
#define JRI_MAGIC (0x01<<24|'I'<<16|'R'<<8|'J')
#define JRI_VERSION(x) ((x&0xFF000000)>>24)
struct JRI_HEADER
{
DWORD dwSignature; // 파일식별코드
DWORD dwWidth; // 이미지의 넓이
DWORD dwHeight; // 이미지의 높이
DWORD dwRGBBitCount; // Used RGB Bit Count - 15, 16, 24
DWORD dwSize; // Compressed size
DWORD dwBitmapSize; // Uncompressed size
DWORD dwColorKey; // 투명색
};
/**
* JRI 압축형식의 이미지 클래스
* @author 장지락
* @date 2004-03-03 1:50오전
* @version 1.0
*/
class CImageEx
{
public:
// JRI static member data
JRI_HEADER * m_lpHeader;
WORD * m_lpImage;
// JRI dynamic member data(runtime)
BOOL m_bIndirected;
BOOL m_bAutoColorKey;
RECT m_ClipRect;
public:
CImageEx();
virtual ~CImageEx();
/**
* JRI 생성
*/
HRESULT Create(const char * cszFilename );
HRESULT Create(const BYTE * lpCache, DWORD lSize, BOOL bIndirected=FALSE);
HRESULT Change(const BYTE * lpCache, DWORD lSize);
/**
* Image Validate
*/
static
HRESULT Validate(const BYTE * lpHead);
DWORD GetWidth(){ return m_lpHeader->dwWidth; }
DWORD GetHeight(){ return m_lpHeader->dwHeight; }
/**
* Clipper
*/
BOOL ClipRect(RECT * Rect);
BOOL ClipRect(RECT * srcRect, RECT * destRect);
BOOL ValidateBlt(RECT * tRect, LONG * lDestX, LONG *lDestY, RECT * srcRect);
/**
* Display
*/
HRESULT Blt(WORD * lpTarget, LONG x, LONG y, LONG dPitch, RECT *dstRect=NULL, RECT *srcRect=NULL);
HRESULT BltHFlip(WORD * lpTarget, LONG x, LONG y, LONG dPitch, RECT *dstRect=NULL, RECT *srcRect=NULL);
/**
* Set AutoColorKey
*/
void AutoColorKey(void){ m_bAutoColorKey=FALSE; }
/**
* Image convert
*/
HRESULT RGB555To565(void);
HRESULT RGB565To555(void);
HRESULT BMP2JRI(LPCSTR szSrcBMP, LPCSTR szDstJRI, BYTE *pHeader=NULL);
HRESULT JRI2BMP(LPCSTR szSrcJRI, LPCSTR szDstBMP);
/**
* JRI or BMP File I/O
*/
HRESULT Open(const char * cszFilename ); // JRI Open
HRESULT Save(const char * cszFilename ); // JRI Save
HRESULT OpenAsBMP(const char * cszFilename );
HRESULT SaveAsBMP(const char * cszFilename );
/**
* Save routine for AFSprite!
*/
HRESULT Appendix(HANDLE hFile);
protected:
/**
* DECODE 관련
*/
HRESULT DecodeBlt1( const WORD * lpSource, WORD * lpTarget,
LONG x, LONG y, LONG dPitch, RECT *dstRect=NULL, RECT *srcRect=NULL );
HRESULT DecodeBlt2( const WORD * lpSource, WORD * lpTarget,
LONG x, LONG y, LONG dPitch, RECT *dstRect=NULL, RECT *srcRect=NULL );
HRESULT DecodeJRI( const WORD * lpSource, WORD * lpTarget );
/**
* ENCODE 관련
*/
WORD * Encode( const BYTE * lpSrc, UINT nWidth, UINT nHeight, DWORD& dwRefSize );
UINT ProcessLn( BYTE* lpSrc, WORD* lpDst, WORD nWidth );
WORD CountRuns( BYTE * lpSrc, UINT nLength );
WORD CountSkip( BYTE * lpSrc, UINT nLength );
WORD CountDraw( BYTE * lpSrc, UINT nLength );
};
기본적인건 이렇구요... 좀더 객체지향적으로 꾸밀 수도 있겠죠... 인터페이스 클래스를
정의해서 상속받는것도 좋은 방법이 되겠지요...
그러나 여기선 그런건 일단 넘어가기로 하고... 위의 클래스 구조가 잡혔으니 다음엔 구현을 하나씩 하나씩 집어 가면서 예기하겠습니다.
좀 부족한 감이 있지만 일단 오늘은 이까지하고 혹시 질문 있으시면 댓글을 이용해주시길...
[P.S. 1] 게임 프로그래밍에 관심있으신 분은 한번쯤은 보셔야할 부분입니다.
[P.S. 2] 앗~ 빨간색으로 된건 아주 위험한 코드입니다. public이므로 외부에서 멋도 모르고
호출했다면 segmentation fault가 나겠죠... 아무 생각없이 한 코드고 별로 이쁘지도
않고... 쩜 옛날엔 이렇게나 생각없는 코드와 클래스 구성을 했네욥 ㅠ.ㅠ