API-BitBlt, PatBlt,StretchBlt,TransparentBlt 정리

1. BitBlt

 

프로그램에서 비트맵을 사용하는 가장 일반적인 방법은 비트맵을 리소스로 정의한 후 LoadBitmap으로 읽어와 출력하는 것이다. 리소스에 작성되는 비트맵은 DIB이지만 LoadBitmap 함수에 의해 화면과 호환되는 DDB로 변환된다. 그래서 이 비트맵을 메모리 DC에 선택하여 곧바로 출력할 수 있다. 비트맵을 출력하는 함수는 여러 종류가 있지만 가장 기본이 되는 함수는 BitBlt(빗 블릿이라고 읽는다) 함수이다.

 

BOOL BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,

                                         HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop);

 

메모리 DC에 리소스의 비트맵을 선택해 좋고 화면 DC로 전송하면 비트맵이 출력된다. 다음은 비트맵을 출력하는 가장 일반적인 예제인데 이 예제를 통해 BitBlt 함수의 여러 가지 사용예를 연구해 보자. 첨부한 BitBlt.cpp 파일을 참고하도록 하자.

 

리소스에 작성되어 있는 IDB_BITMAP1 비트맵을 읽어와 메모리 DC에 선택하였다. 그리고 이 비트맵의 크기를 GetObject 함수로 조사한 후 BitBlt으로 화면에 비트맵 크기대로 출력하였다. 비트맵의 크기를 조사할 때는 GetObject 함수를 사용한다. 출력 결과는 다음과 같다.

 

 

조사된 비트맵 크기대로 hdc에 비트맵을 출력하였다. 비트맵의 일부만 출력할 수 있는데 이때도 BitBlt 함수를 사용하되 출력 좌표만 잘 조정하면 된다. 다음 출력문은 비트맵의 아래 왼쪽 부분만 출력한다.

 

BitBlt(hdc,0,0,bx/2,by/2,MemDC,0,by/2,SRCCOPY);

 

MemDC의 0,by/2가 출력 시작점이므로 수직으로 절반쯤 되는 부분에서부터 출력을 시작하고 hdc의 bx/2, by/2까지만 출력하므로 비트맵의 3/4분면만 출력되었다.

 

 

BitBlt은 주로 메모리 DC와 화면 DC간의 비트맵 전송에 사용되지만 같은 DC끼리도 비트맵을 전송할 수 있다. 이때 두 DC는 반드시 호환되는 DC이거나 아니면 한쪽이 흑백이어야 한다. 완전히 다른 색상 포맷을 가지는 DC끼리는 이미지를 전송할 수 없되 흑백은 예외적으로 임의의 DC로 전송할 수 있다. 다음 출력문은 MemDC의 비트맵을 일단 화면으로 출력한 후 화면에 출력된 비트맵의 아래쪽 부분을 다시 화면에 복사한 것이다.

 

BitBlt(hdc,0,0,bx,by,MemDC,0,0,SRCCOPY);

BitBlt(hdc,0,by+10,bx,by/2,hdc,0,by/2,SRCCOPY);

 

출력 결과는 다음과 같다. 아래쪽의 비트맵은 메모리 DC에서 전송된 것이 아니고 화면에 이미 출력된 이미지를 복사하여 다시 출력한 것이다.

 

 

BitBlt 함수는 비교적 사용하기 쉽지만 비트맵 출력을 위해 메모리 DC를 만들어야 하고 비트맵 크기를 조사해야 한다는 점에 있어 번거로운 면이 있다. 전체 비트맵을 화면의 특정 위치로 출력하고 싶다면 별도의 비트맵 출력 유틸리티 함수를 만들어 사용하는 것이 편리하다.

 

ROP 모드

 

BitBlt의 마지막 인수인 dwRop는 출력하고자 하는 비트맵의 비트와 화면에 이미 출력되어 있는 비트, 그리고 현재 DC에 선택된 브러시의 비트를 논리 조합하는 연산을 지정한다. SRCCOPY로 지정하면 비트맵의 이미지가 그대로 화면 DC로 전송되지만 이 값을 바꾸면 다양한 효과로 비트맵을 출력할 수 있다. 가능한 dwRop값은 256가지나 있지만 자주 사용되는 것은 다음 16가지뿐이다. 이 표는 설명을 보는 것보다는 연산식을 보는 것이 더 이해하기 빠를 것이다. 연산식의 S는 비트맵, D는 화면, P는 브러시이다.

 

 

 BLACKNESS (0)

무조건 검정색으로 칠한다.

 DSTINVERT (~D)

화면색을 반전시킨다.

 MERGECOPY (S&P)

브러시와 비트맵 색을 AND 연산한다.

 MERGEPAINT (~S|D)

비트맵을 반전한 후 화면색과 OR 연산한다.

 NOTSRCCOPY (~S)

비트맵을 반전시킨다.

 NOTSRCERASE (~(S|D))

화면색과 비트맵 색을 OR 연산한 후 반전시킨다.

 PATCOPY (P)

현재 선택된 브러시로 칠한다.

 PATINVERT (P^D)

브러시와 화면색을 XOR 연산한다.

 PATPAINT (P|~(S|D))

NOTSRCERASE의 결과를 브러시와 OR 연산한다.

 SRCAND (S&D)

비트맵과 화면색을 AND 연산한다.

 SRCCOPY (S)

비트맵을 그대로 화면으로 출력한다.

 SRCERASE (S&~D)

비트맵과 화면의 반전색을 AND 연산한다.

 SRCINVERT (S^D)

비트맵과 화면을 XOR 연산한다.

 SRCPAINT (S|D)

비트맵과 화면을 OR 연산한다.

 WHITENESS (1)

무조건 흰색으로 칠한다.

 

이 중 실제로 가장 많이 사용되는 연산 방법은 SRCCOPY이며 그 외에 자주 사용되는 것은 몇 가지 되지 않는다. 다음 BitmapRop 예제는 ROP 모드를 테스트해 보기 위해 의도적으로 만들어 본 것이다. 비트맵 출력 후에 원래 화면이 어떻게 변하는가를 보기 위해 회색 브러시로 화면에 타원을 하나 그려 두었다. BitBlt의 마지막 인수를 다양하게 바꾸어 보면 어떤 효과가 있는지 직접 살펴보기 바란다. 첨부한 BitmapRop.cpp 파일을 참고하도록 하자.

 

다음은 자주 사용되는 ROP 모드에 대한 실행 예이다.

 

        

SRCCOPY            SRCINVERT            SRCPAINT             SRCAND            SRCERASE

 

어째서 저런 출력이 나오는지 얼른 이해가 안되겠지만 ROP 모드의 연산식대로 화면과 비트맵의 각 비트를 이진수 차원에서 연산해 보면 이해할 수 있다.

 

PatBlt

 

다음 함수는 직접적으로 비트맵을 출력하는 함수는 아니며 패턴을 출력한다.

 

BOOL PatBlt(HDC hdc, int nXLeft, int nYLeft, int nWidth, int nHeight,

                                                                                       DWORD dwRop);

 

여기서 말하는 패턴이란 쉽게 말해서 브러시인데 지정한 사각영역을 현재 DC에 선택된 브러시로 채운다. 채색할 영역을 지정하는 4개의 인수들은 LTRB가 아니라 XYWH방식임을 유의 하자. 브러시에 비트맵을 적용할 수 있으므로 이 함수를 사용하면 간접적으로 비트맵을 출력할 수 있다. FillRect 함수와 형식만 다를 뿐이지 동작은 같다고 할 수 있다. 단 FillRect는 사각영역을 무조건 브러시로 채우지만 PatBlt는 마지막 인수 dwRop로 브러시 색상과 화면 색상의 논리 연산방법을 지정할 수 있다. 간단하게 예제를 만들어 보았다. 첨부한 PatBlt.cpp 파일을 참고하도록 하자.

 

파란색 타원을 하나 그리고 그 위에 회색으로 패턴을 칠하되 ROP 모드르 PATINVERT로 지정하여 화면 색상과 브러시 색상이 OR 연산되도록 하였다. 또 오른쪽에는 검정색 사각형도 하나 그려 보았다. 실행 결과는 다음과 같다.

 

 

이 외에 dwRop 인수에 쓸 수 있는 값에는 무조건 복사하는 PATCOPY와 BLACKNESS, WHITENESS가 있고 화면 색상을 반전시키는 DSTINVERT가 있다. 아주 간단한 출력 함수인데 사각영역을 흰색이나 검정색으로 채울 때는 이 함수가 가장 간편하다. 좌표와 폭, 높이를 인수로 직접 전달받으므로 RECT 구조체를 선언할 필요가 없으며 브러시를 생성, 선택, 파괴하는 처리도 할 필요없이 단 한 줄로 원하는 영역을 흰색이나 검정색으로 채울 수 있다.

 

2. 확대 및 축소

 

BitBlt은 비트맵의 전체나 일부를 다양한 ROP 모드로 전송하되 이때 복사원과 복사 대상간의 픽셀끼리는 1:1로 대응된다. 즉 복사원의 픽셀 하나가 복사 대상의 픽셀 하나로 복사되기 때문에 일부분만 복사할 수는 있어도 확대나 축소를 할 수는 없다. 반면 다음 함수는 BitBlt과 유사하되 비트맵을 확대하거나 축소하여 출력할 수 있다.

 

BOOL StretchBlt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest,

       int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc,

                                                                   int nHeightSrc, DWORD dwRop);

 

원형은 BitBlt과 유사하되 복사원의 넓이와 높이를 추가로 지정할 수 있다는 점이 다르다. 그래서 두 DC간의 면적비에 따라 이미지가 확대되거나 축소된다. 예를 들어 복사원은 100, 200의 크기를 가지되 복사대상은 200, 400의 크기를 가진다면 이미지는 두 배로 확대되며 복사대상이 50,100의 크기를 가진다면 절반으로 축소된다. 또한 만약 복사 대상의 높이, 넓이 부호가 음수일 경우 이미지를 반사된 모양으로 출력할 수도 있는데 이런 기능을 미러링(mirroring)이라고 한다. 마치 거울에 반사된 것 처럼 이미지를 출력한다.

 

 

확대/축소 과정은 이미지의 픽셀끼리 1:1로 대응되는 것이 아니기 때문에 추가로 픽셀이 삽입되기도 하고 또는 픽셀이 생략되거나 합쳐지기도 한다. 아주 작은 흑백 이미지를 가정하고 확대, 축소시의 이미지 변화를 상상해 보자.

 

 

확대될 때는 원본의 픽셀을 여러 번 반복하면 되므로 별 문제가 없다. 하지만 축소될 때는 불가피하게 원본의 픽셀중 일부를 생략해야 하는데 이때 어떤 픽셀이 삽입되고 어떤 픽셀이 생략될 것인가를 계산하는 방법을 스크레칭 모드라고 하며 합쳐질 픽셀들의 논리 방법을 지정한다.

 

int SetStretchBltMode(HDC hdc, int iStretchMode);

 

스트레칭 모드는 DC의 속성 중 하나이므로 DC핸들과 모드값을 전달한다. 가능한 스트레칭 모드는 다음과 같다.

 

 

 BLACKONWHITE = STRETCH_ANDSCANS 

AND 논리 연산을 사용한다. 흰색을 생략하고 검정색을 보존한다. 검정색이 우선 보존된다.

 WHITEONBLACK = STRETCH_ORSCANS

OR 논리 연산을 사용한다. 검정색을 생략하고 흰색을 보존한다. 흰색이 우선 보존된다.

 COLORONCOLOR = STRETCH_DELETESCANS

생략되는 픽셀을 별도의 논리 연산없이 삭제한다.

 HALFTONE = STRETCH_HALFTONE

복사대상과 복사원의 사각 블록끼리 대입하여 평균 색상을 구한다. 95/98에서 이 모드는 지원되지 않는다.

 

디폴트 스트레칭 모드는 BLACKONWHITE이며 흰바탕에 검정색으로 이미지가 그려진 경우 이 스트레칭 모드가 적합하다. 그러나 반대인 경우 즉, 검정색 바탕에 흰색 이미지가 그려져 있는 경우는 스트레칭 모드를 WHITEONBLACK으로 바꿔야 가급적 흰색을 보존하므로 축소된 모양이 제대로 보인다. 다음 예제는 StretchBlt 함수로 모드를 바꿔가며 여러 번 출력해 본 것이다. 첨부한 StretchBlt.cpp 파일을 참고하도록 하자.

 

리소스에는 IDB_BITMAP1이라는 흑백 비트맵을 그려놓았다. 스트레칭 모드의 효과를 명확하게 살펴보기 위해서는 흑백 비트맵이 이행하기 쉽기 때문이다. 실행 결과를 보고 각 함수 호출문이 어떻게 이미지를 출력하는지 보자.

 

 

첫 번째 호출문은 소스와 같은 비율로 비트맵을 출력하였다. 조사된 비트맵의 높이 bx, by를 복사대상과 복사원에 동일하게 지정하므로써 비트맵의 크기대로 출력했는데 이 호출문은 BitBlt 호출문과 동일하다. 두 번쨰 호출문은 복사 대상의 넓이인 bx의 부호를 음수로 바꿈으로써 좌우가 바뀐 모양으로 이미지를 출력한다. by의 부호도 바꾸면 상하가 바뀐 모양으로도 출력할 수 있다.

 

세 번째 호출문은 복사대상의 높이와 폭을 복사원의 두 배로 지정함으로써 2배로 확대하였다. bx, by크기의 이미지를 bx*2, by*2크기에 출력하기 때문에 비트맵이 2배 확대된다. 4배나 8배 또는 실수배로도 확대할 수 있으며 가로, 세로 종횡비가 틀려도 상관없다. 어쨌든 StretchBlt 함수는 복사원 이미지를 복사 대상 면적에 맞게 출력한다.

 

아래쪽 4개의 이미지는 스트레칭 모드를 바꿔가며 이미지를 축소해서 출력해 본 것인데 이미지의 위쪽에 있는 두 개의 원이 어떻게 출력되는지 잘 살펴보아라. 디폴트 스트레칭 모드인 BLACKONWHITE로 이미지를 출력하면 가급적 검정색을 보존하며 그래서 흰 바탕에 검정색으로 그려진 원은 축소해도 제 모양으로 보이지만 검정 바탕에 흰색 원은 획이 생략되어 버리기 때문에 보이지 않는다. 반대로 WHITEONBLACK으로 출력하면 가급적 흰색을 보존하기 때문에 검정 바탕에 흰색 원은 보이지만 흰 바탕에 검정색 원은 보이지 않는다. 이미지가 검정색 위주인지 흰색 위주인지에 따라 적당한 스트레칭 모드를 선택해야 하는 이유가 바로 여기에 있다.

 

COLORONCOLOR로 출력하면 특정 색상을 보존하지 않고 축소 비율에 따라 생략되는 줄이나 열 전체를 지워버리기 때문에 전체적으로 이미지가 많이 찌그러진다. 예를 들어 절반으로 축소한다면 매 짝수줄을 모조리 생략해 버리는 식이다. HALFTONE은 내부적으로 복잡한 연산에 의해 이미지의 어떤 부분이 획인지를 자동으로 판단하여 가장 좋은 축소 결과를 만들어낸다. 블록 단위의 이미지 판별을 하기 때문에 실행 속도가 다소 느리며 95/98은 이 모드를 지원하지 않아 호환성에 불리하다.

 

3. 투명 비트맵 출력

 

비트맵은 예외없이 사각형 모양을 가진다. 타원 모양이나 다각형 모양의 비트맵은 존재하지 않는다. 비트맵 안의 그림은 어떤 모양이든지 가능하지만 비트맵 자체는 꼭 사각형이어야만 하며 출력할 때도 화면상의 사각영역에만 출력된다. 그런데 경우에 따라서 사각형이 아닌 형태로 비트맵을 출력해야 할 경우가 있는데 특히 게임의 경우 캐릭터들이 사각형이 아닌 경우가 많다. 이때는 비트맵의 일부를 투명하게 처리하여 투명색 부분은 출력에서 제외하는 방법을 사용한다.

 

BOOL TransparentBlt(HDC hdcDest, ....., UINT crTransparent);

 

이 함수가 투명 비트맵을 출력하는데 원형 StretchBlt와 거의 유사하되 제일 마지막의 인수가 ROP 모드를 지정하지 않고 투명색으로 사용할 마스크 색상을 지정한다는 점이 다르다. 이 함수를 사용하려면 먼저 출력할 비트맵부터 만들어야 한다. 비트맵을 처음 만들 때부터 투명하게 처리할 부분은 특별한 다른 색(마스크 색상)으로 주어 그림 자체와는 구분되게 처리한다. 그리고 마스크 색은 출력되지 않도록 하면 원하는 효과를 달성할 수 있다. 이때 마스크로 사용할 색상은 이미지 내에는 존재하지 않는 특수한 색상이어야 하는데 주로 분홍생이 많이 사용된다. 다음은 투명 비트맵 처리를 위해 만든 이미지인데 바깥쪽을 온통 빨강색으로 칠해 놓았다. 이 빨간색 영역이 투명색으로 처리된다.

 

 

준비한 이미지를 리소스에 임포트해 넣고 TransprentBlt 함수를 호출하되 마스크 색상을 빨간색인 RGB(255,0,0)로 지정하면 바깥쪽 부분이 투명하게 출력된다. 첨부한 TransparentBlt.cpp 파일을 참고하도록 하자.

 

투명 효과를 확실하게 볼 수 있도록 하기 위해 가로로 여러 줄의 수평선을 그어두었다. 실행 결과는 다음과 같다.

 

 

과연 빨간색 부분이 뚜명하게 처리되었으며 자동차 바깥쪽 부분의 수평선은 지워지지 않고 그대로 유지된다. StretchBlt 함수와 마찬가지로 확대나 축소를 할 수도 있지만 미러링은 지원하지 않는다. 이 함수는 윈도우즈 95에서는 사용할 수 없으며 Msigm32.dll에 정의된 함수이므로 프로젝트에 Msimg32.lib를 링크해야만 사용할 수 있다. 이후 몇 가지 예제에 대해서도 Msimg32.lib를 링크하는 예제가 있는데 주로 98이후에 추가된 함수들이 이 DLL에 의해 제공된다.

 

이 외에 투명 비트맵을 출력하는 또 다른 방법들이 있다. 마스크 비트맵과 이미지 비트맵 두 장을 준비하여 ROP 모드를 바꿔 가며 두 번 출력하는 고전적인 방법도 흔히 사용되는데 디자인시에 미리 투명 영역을 지정하므로 속도상으로는 가장 빠르다. 크기가 작고 종류가 많은 비트맵을 출력할 때는 비트맵을 직접 사용하는 것보다 이미지 리스트 컨트롤을 쓰는 것이 유리하다. 게임의 캐릭터들은 보통 크기가 작은데 이미지 리스트에 이런 이미지들을 미리 디자인해 놓고 인덱스로 이미지를 관리하면 여러 모로 편리하다. 



참고자료: www.winapi.com

이 글을 공유하기

댓글

Designed by JB FACTORY