[Win32] 4. OS가 그림을 출력하는 방법

Sireal·2022년 3월 30일
0

C++/Win32/MFC

목록 보기
9/12
post-thumbnail

비트맵

  • 비트맵 : 비트로 된 맵(지도)
  • 비트맵으로 그림을 표현함

DDB, DIB

  • 2차원 형식의 메모리로 비트맵을 표현하지만, 물리적으로는 1차원 형태를 가짐 이런 비트들을 어떻게 쓰고 관리할까

DDB(Device Dependent Bitmap) : 장치에 별다른 변환없이 바로 사용가능한 형태의 비트맵

  • 어느환경이던 무조건 똑같은 형태로 보여줌
  • 환경이 바뀌면 제대로 안보임

DIB(Device-Independent Bitmap) : 장치에 종속되지 않는 비트맵

  • 비트맵 헤더(Bitmap Header) 를 사용하여 비트맵 패턴을 추가로 제공함.
  • 그렇게 함으로써 어떠한 장치에도 맞도록 변환해서 사용 가능하다.

32비트 비트맵 메모리 구성

  • 투명도, R, G, B 순으로 명시됨.
    unsigned int pixel_color = 0xFF507090; // 투명도, R, G, B 순서
  • 근데 CPU는 리틀엔디안이어서 거꾸로 읽음. 그래서 사실상 B를 먼저 읽는다는 점 주의해야함
unsigned int pixel_color = 0xFF507080; // 투명도, R, G, B
unsigned char *p = (unsigned char *)&pixel_color;
unsigned char blue = *p; // 선두 바이트는 파란값이 들어가있음
unsigned char green = *(p+1); //  다음 바이트에 녹색 값이 들어있음
unsigned char red = *(p+2); // 그 다음 바이트엔 붉은 값이 들어있음.
  • 반대로 바이트 정렬 개념이 그대로 적용됨.
  • 시프트 연산자인 경우 RGB 순서로도 사용가능함
unsigned int pixel_color = 0xFF507090;
unsigned char red = (unsigned char)(pixel_color >> 16); // 16~23번 비트에 붉은색
unsigned char green = (unsigned char)(pixel_color >> 8); // 8~15번 비트 녹색
unsigned char blue = (unsigned char)pixel_color; // 0~7번 비트 파란색.

비트맵 생성하기

CreateBitmap - 비트맵 직접 생성하기

HBITMAP CreateBitmap(int nWidth int nHeight, UINT nPlanes, UINT nBitCount, CONSTvoid *lpBits)
비트맵을 생성하려면 '폭','높이','색상' 에 대해 정해야함
이함수의 5가지 매개변수

  • nWidth : 생성할 비트맵의 폭
  • nHeight : 높이
  • nPlanes : 16색상을 사용하지 않으면 그냥 '1'이라고 명시하면됨.
  • nBitCount : 색상을 표현하는데 필요한 비트수. 16, 24, 32 를 명시하면 됨
  • lpBits : 비트맵이 사용할 메모리크기.
    • 즉 폭16,높이8에 32비트 색상 비트맵일 경우
      • 16*8*32/8 = 512 bytes 의 크기를 가져야함.
    • 특별한 초기값이 없으면 검은색 RGB로 표현됨(0,0,0)
  • 반환값 : 생성된 비트맵에 대한 핸들값을 반환한다. 이걸루 작업하면 됨

CreateBitmap 사용예시

HBITMAP h_my_bmp = CreateBitmap(16, 8, 1, 32, NULL)

  • 폭 16, 높이 8, 색상수 32비트, 초기값 없음

혹시 파란색으로 된 비트맵을 사용하고싶은가?

unsigned int *p = new unsgined int[16*8]; // 16*8*4크기로 메모리 생성
for(int i -= 0; i<16*8;i++) *(p+i) = 0xFF0000FF; // 0xAARRGGbb순서임.
HBITMAP h_my_bmp = CreateBitmap(16,8,1,32,p);
delete[] p; // 초기값으로 사용했던 메모리 삭제

비트맵을 제거하려면

CreateBitmap 함수를 사용해서 비트맵 사용을 하다가 제거하려면 DeleteObject 함수를 사용해야함.
근데 DeleteObject는 GDI Object를 제거할 때 사용함.
BOOL DeleteObject(GDIOBJ ho);

  • DeleteObject함수의 원형이다.
 // 폭 16, 높이8, 색상수 32비트맵 생성
 HBITMAP h_my_bmp = CreateBitmap(16,8,1,32,NULL);
 // 비트맵 제거
 DeleteObject(h_my_bmp);

CreateBitmap 함수사용시 주의사항

컴퓨터 환경마다 색상수차가 다름.
CreateBitmap(16,8,1,32,NULL); 에서 4번째 친구(32) 처럼 특정값을 고정하면 프로그램 호환성이 떨어지게 됨.

  • CreateBitmap 함수는 DDB형식의 비트맵을 생성함.

DC(Device Context)

// 화면의 색상 수를 얻는다. h_dc는 현재 윈도우의 DC 핸들값이라고 가정한다.
int color_depth = ::GetDeviceCaps(h_dc, BITSPIXEL);

DC라는 곳에서 현재 장치의 속성을 얻을 수 있다.
위의 코드는 현재 DC에서 색상수차를 확인하는 코드이다.

그래서 이렇게 코드짜는게 좋음

// 화면의 색상 수를 얻는다. h_dc가 현재 윈도위의 DC 핸들값
int color_depth = ::GetDeviceCaps(h_dc, BITSPIXEL);
// 폭16, 높이8, 현재장치의 색상수를 이용해서 비트생성
HBITMAP h_my_bmp = CreateBitmap(16,8,1,color_depth,NULL);
// 비트맵 제거
DeleteObject(h_my_bmp);

좀더 편하게 비트맵 생성하는 방법

DDB 형식 비트맵은 너무 종속적임.
DC와 호환되는 안전하고 편리한 비트맵을 생성하고싶음.

CreateCompatibleBitmap(HDC hdc, int nWidth, int nHeight);

  • 얘를 쓰자.
  • 비트플랜수, 색상수 이런거 안해도 되서 간편함
  • hdc 에 전달된 DC를 기준으로 비트맵을 생성한다.
  • 현재 DC와 호환되는 비트맵을 만들고싶다면 hdc 부분을 NULL로 하면 됨.
// HBITMAP h_my_bmp = CreateCompatibleBitmap(NULL,16,8); 
HBITMAP h_my_bmp = CreateCompatibleBitmpa(h_dc, 16, 8);

DeleteObject(h_my_bmp); // 비트맵 제거

그냥 킹갓제너럴 CreateCompatibleBitmap 함수를 쓰자.

BITMAP 구조체

typedef struct tagBITMAP
{
	LONG	bmType;			// 비트맵 형식. 항상 0값 대입
    LONG	bmWidth;		// 비트맵 폭(pixel단위)
    LONG	bmHeight;		// 비트맵 높이(pixel단위)
    LONG	bmWidthBytes;	// 가로방향으로 한줄에 사용되는 바이트수
    WORD	bmplanes;		// 그냥 1 쓰자
    WORD	bmBitsPixel;	// 비트 색상수
    LPVOID	bmBits;			// 이미지 패턴이 저장된 메모리시작주소
}	BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP

Bitmap과 GDI 그리고 DC

변수 접두어 규칙

변수를 사용하기 전 접두에 대해 알아보자

  • 변수 종류에 관한 접두어
    • 전역변수 (g_)
    • 멤버변수 (m_)
    • 매개변수 (a_)
    • 지역변수 (없음)
  • 변수의 성격을 구분하는 추가 접두어
    • 핸들변수 (h_)
    • 포인터변수 (p_)
    • 참조변수 (r_) : 참조가 레퍼런스니까 r.
      • int mr_data; // 멤버변수이면서 참조변수

비트맵에 사각형을 그리는 함수

GDI 그려보면 네모하나 그리는데 엄청난 시간과 공이 든다.

GDI

GDI(Gravpics Device Interface)
Window 운영체제가 어느환경이든 사용할 수 있는 그리기 모델하나를 추상화시켜둠.

GDI Object

Bitmap : 비트패턴을 추상화시키기위해 제공함
Pen : 선그리기에 사용됨
Brush : 도형 내부영역을 채울 때 사용됨

DC

GDI Object 핸들값을 저장하고있는 객체가 필요한데 DC(Device Context)가 그 객체임

그림을 그리기전 GetDC()로 DC의 핸들값(HDC)를 받아와야함

// m_hWnd에 해당하는 윈도우가 사용하는 DC정보를 얻는다.
HDC h_dc = ::GetDC(mhWnd);

그리고 이렇게 하면 사각형 하나 뚝딱

// h_dc를 사용하여 (50,50)에서 (220, 150)을 연결하는 사각형을 그림
Rectangle(h_dc, 50, 50, 220, 150);

클라이언트 영역의 출력형식

클라이언트 영역의 기본출력형식

  • 이 노란네모가 클라이언트 영역
  • 실제 모든작업은 저기서 일어남

  • x,y 좌표는 이런식임

그렇게 사각형 하나를 그리면

// (20,20) ~ (100,80) 에 네모그리기 (WinMain에 적으세용)
	HDC h_dc = GetDC(hWnd);
	Rectangle(h_dc, 20, 20, 100, 80);
	ReleaseDC(hWnd, h_dc);

  • 좌표를 (20,20)~(100,80) 으로 해뒀지만, 실제 좌표는 (20,20)~(99.79) 임
    • 폭(20 ~ 100)은 우리가 생각하기엔 80임.
    • 그래서 폭 80을 만들기 위해서 각 끝좌표는 1을 빼버림
    • 20 부터 시작해서 99 까지 세야 80이 나오자너. 암턴 그럼.
profile
🚄계속 앞으로🚄

0개의 댓글