Clipper의 필요성에 대해선 조금 그림을 통한 부가 설명이 필요 할 것 같아서 제가 이미지를 하나 또 준비했습니다.
하하^^;
자, 위 그림을 한번 보세요.
만약 위처럼 블리팅을 시도했다면, 화면에는 두더지가 반밖에 그려지지 않았겠죠.
그러나 사실 알고보면 메모리상에는 나머지 반의 부분도 존재합니다.
두더지 이미지의 사이즈는 총 11KB입니다.
그런데 딱 절반밖에 보이질 않으므로, 나머지 5.5KB는... 보이지도 않는데 괜히 메모리만 낭비하고 있는 셈이 되겠군요.
문제는 그것뿐이 아닙니다.
자, 두더지의 이미지 크기를 계산하기 쉽게 100*100이라고 생각해 봅시다.
블리팅을 할때, 1 픽셀을 그려주는데 1초가 걸린다고 치면...
100*100의 이미지를 그려주는데는 무려 10000초가 걸리게 됩니다! (물론 이렇게 느린 블리팅은 없겠죠-_-)
그러나, 만약에 화면에 보이지 않는 50*100을 그려주지 않는다면?
우리는 나머지 50*100의 이미지만을 그려주면 되므로,블리팅 시간은 5000초로 단축이 될 수가 있는 것입니다.
어떠세요? 클리핑의 필요성이 확확 와닿지 않나요?
클리핑에는 먼저 크게 나누면 두가지의 클리핑이 있습니다.
Software Clipping과 Hardware Clipping이 바로 그것인데요.
S/W 클리핑이 안 좋고, H/W 클리핑은 좋다. 뭐 이렇게 딱 정해진 것은 없습니다.
물론 S/W 클리핑이 H/W 클리핑에 비해 속도가 느린것은 사실이지만,
클리핑의 필요성이 별로 없는 프로그램에서는 H/W 클리핑을 쓰는 것보다 S/W 클리핑을 사용할때의 이득이 더 많을 때가 있습니다.
뭐, 그런건 프로그래머가 경험으로 판단하는 것이겠구요.
우리는 S/W 클리핑은 다루고 넘어가진 않을 것이지만, 결국엔 다 같으니 S/W 클리핑은 조금만 생각해보시면 다 구현해내실수 있으리라고 믿습니다.
DirectDraw를 이용한 H/W 클리핑.
DirectDraw에는 IDirectDrawClipper라는 인터페이스가 있습니다.
S/W 클리핑의 경우, 픽셀 클리핑, 2D 클리핑, 3D 클리핑 등등...
클리핑 할 객체의 종류에 따라 방법이 나뉘어져 있습니다.
그러나 DirectDraw Clipper를 이용한 클리핑 시에는, 같은 방법으로 2D/3D를 가리지 않고 동일하게 사용될수 있습니다.
좋죠?^^;
DirectDraw 클리핑을 세팅 하기 위해서는 다음과 같은 순서로 코딩하시면 됩니다.
1. DirectDraw Clipper 객체를 생성한다.
2. Clipping List를 작성한다.
3. IDIRECTDRAWCLIPPER::SetClipList()를 이용하여 작성한 Clipping List를 클리퍼에게 넘겨준다.
4. IDIRECTDRAWCLIPPER::SetClipper)를 이용하여 클리퍼를 Windows나 특정 표면에 적용시킨다.
간단하죠?
DirectDraw Clipper는 고맙게도 매개변수가 Direct Draw 함수 중 가장 쉽기때문에 쉽게 적용시킬수 있습니다.
그럼 1번 단계부터 살펴보도록 할까요?
IDIRECTDRAW7::CreateClipper() 함수의 원형을 보면,
HRESULT CreateClipper(DWORD dwFlags, LPDIRECTDRAWCLIPPER* lplpDDClipper, IUnknown* pUnkOuter);
와 같이 되어 있습니다.
dwFlags에는 플래그 값이 들어가는 필드인데, 아직 우리는 사용하지 않으므로 단순히 0으로 지정해주시면 됩니다.
lplpDDClipper는 당연히 LPDIRECTDRAWCLIPPER의 주소값을 넣어주시면 되겠구요.
마지막 Unknown은 이전과 마찬가지로 NULL을 넘겨주시면 됩니다^^
쉽죠! 쉽죠?
그런데 DDClipper를 이해하는데 조금 걸림돌이 되는 녀석이 바로 2번입니다.
2번 과정은 바로 Clipping List를 작성하는 것이라고 말씀 드렸습니다.
왜 Clipping List란, 클리핑을 할 영역의 리스트를 의미하는 것이 당연하겠죠.
이 말은 바꿔 말하면, 클리핑을 한군데만 하는 것이 아니라, 여러군데를 클리핑 할수도 있다는 의미가 되겠죠.
어쨋든 Clipping List를 작성하려면 조금 이상한 형태의 구조체에 정보를 기입해주어야 하는데요.
typedef struct _RGNDATA
{
RGNDATAHEADER rdh;
char Buffer[1];
} RGNDATA;
먼저 RGNDATAHEADER를 살펴보면,
typedef struct _RGNDATAHEADER
{
DWORD dwSize;
DWORD iType;
DWORD nCount;
DWORD nRgnSize;
RECT rcBound;
} RGNDATAHEADER;
dwSize는 당연히 RGNDATAHEADER의 크기를 넣어주시면 되고,
iType은 RGNDATA의 Type을 넣어주시는 건데,
사각형 형식으로 클리핑을 할것이므로 보통 RDH_RECTANGLES를 넣어줍니다.
nCount는 클리핑 영역이 몇개나 되는지를 기록해 주시면 되구요.
rcBound는 클리핑 영역의 경계 라인을 의미합니다. rcBound에 대해서는 다음시간에 자세히 알아볼 것입니다.
그런데 char Buffer[1]부분은,
C/C++에 익숙하신 분들도 약간은 어리둥절 할 만한 형태를 띄고 있습니다.
Buffer라는 기본적으로 길이에 제한이 없는 가변 크기의 변수 입니다.
아마 대부분의 분들은
char Buffer[1]이 문자 배열 1개를 선언한거지, 무슨 가변 크기의 구조체야!
라고 생각하시며 의아해 하실 겁니다.
그러나 char Buffer[1]을 다르게 써보면...
char* Buffer;
와 같다는 사실을 염두해 두셔야 합니다.
그렇다면 char Buffer가 char Buffer[1]과 같을까요? 정답은 No 입니다.
그러나 char* Buffer는 char Buffer[1]의 시작 주소를 의미하는 것이므로,
정확히 같지는 않지만, 구현시, 결과는 동일하게 됩니다.
그래서 char Buffer[1]과 같이 변수를 선언 한 것이구요.
그렇다면, char Buffer[1]과 같이 char형 변수로 선언한 것은 어떤 이유가 있을까요?
바로, char 형 변수가 1Byte 크기의 변수이기 때문입니다.
예를 들어 5바이트를 메모리 할당하고 싶다면, char형 변수 5개를 만들면 되므로,
계산이 편하겠죠^^ 그래서 char형 변수로 선언을 한 것입니다.
만약 char Buffer[1]부분이 이해가 안가신다면 그냥 건너 뛰시고 설명을 들으셔도 상관이 없습니다.
쉽긴 하지만, 나름대로 고급 개념이기 때문에, 다음 기회에 자세히 알아보도록 하겠구요.
어쨋든 중요한 것은 char Buffer[1]이 아니라, RGNDATA라는 구조체 그 자체이므로 아래 그림을 한번 보시길 바랍니다.
RECT #1, RECT #2, RECT #3, .... ,RECT #X 와 같이 메모리상에 할당만하면, 얼마든지 늘어날 수 있다는 걸 볼수가 있습니다.
Buffer를 다루는 건 다음시간에 알아보도록 하죠.
이제 다시 본론으로 돌아와서 아까 3번 과정을 기억하시나요?
Clipping List를 설정했다면, 어떤 서페이스에 클리핑 영역을 추가 시킬 것인지,
SetClipper() 함수를 이용해서 서페이스에 클리핑 영역을 추가시키면 됩니다.
SetClipper의 원형은 아래와 같구요.
HRESULT SetClipper(LPDIRECTDRAWCLIPPER lpDDClipper);
그냥 단순히, 이런식으로 설정해 주시면 되겠죠.
if( FAILED(lpddSuface->SetClipper( &lpddClipper )) )
return ERR;
이런식으로 해주시면 DDClipper의 세팅이 끝나는 거죠^^
어떠세요? 사실 어려운 것 같지만, 별것도 아닙니다.
다음시간에 직접 이번시간에 배운 내용을 토대로 소스를 작성해 볼 것인데요.
DDClipper는 Video Mode 세팅과 함께DDraw의 꽃이라고 불리는 녀석이므로, 반드시 알고 넘어가야 합니다.
'MS > DirectX' 카테고리의 다른 글
D3DFont 성능 (0) | 2012.07.10 |
---|---|
DX Sprite 사용법 (0) | 2012.06.26 |
D3DXFont::DrawText 완전 느림... (0) | 2012.06.26 |
DirectDraw - SetClipper (2) (0) | 2012.06.01 |