본문 바로가기

Enginius/C / C++

[MFC] BITMAP 으로 그림 그리기

 일반적으로 MFC에서 그림을 그릴 땐 dc, 즉 device context를 얻어온 후 MFC에서 제공하는 api를 사용한다. 이렇게 할 경우 원하는 모양을 비교적 자유롭게 그릴 수 있지만 CPU 오버헤드도 커지고, 그림을 쉽게 변경할 수 없다. 


 이럴때 사용하면 좋은 것이 BITMAP이다. 

typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader;
  RGBQUAD          bmiColors[1];
} BITMAPINFO, *PBITMAPINFO;

  

 비트맵의 자세한 내용은  (http://minimonk.tistory.com/tag/BITMAPINFO)에서 확인할 수 있다. 


 이번 포스팅에선 이 비트맵을 실제로 사용하는 과정을 설명하겠다. 이 포스팅을 따라 하다보면 zoom과 focus이동이 가능한 비트맵을 사용할 수 있을 것이다. 


0. 비트맵을 그릴 장소에 PictureControl을 그린다. 




1. 비트맵을 사용하기 위한 변수를 선언하자. 


BITMAPINFO inputBitmapInfo; //비트맵

unsigned char *inputRGBbuffer; //RGB 이미지 버퍼

int inputStageTop, inputStageBottom, inputStageRight, inputStageLeft;

int inputStageWidth, inputStageHeight;

int inputWidthPerRegion, inputHalfWidthPerRegion;

double inputStageXoffset, inputStageYoffset, inputStageZoom;


2. OnInitDialog()에서 선언한 변수를 초기화 한다. 


inputWidthPerRegion = 그리고자 하는 비트맵의 폭과 너비 

inputHalfWidthPerRegion = inputWidthPerRegion/2;


// 1. 비트맵 정보 초기화 

inputBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

inputBitmapInfo.bmiHeader.biWidth = inputWidthPerRegion;

inputBitmapInfo.bmiHeader.biHeight = inputWidthPerRegion;

inputBitmapInfo.bmiHeader.biPlanes = 1;

inputBitmapInfo.bmiHeader.biBitCount = 32;

inputBitmapInfo.bmiHeader.biCompression = BI_RGB;

inputBitmapInfo.bmiHeader.biSizeImage = (inputWidthPerRegion)*(inputWidthPerRegion)*4;

inputBitmapInfo.bmiHeader.biXPelsPerMeter = 0;

inputBitmapInfo.bmiHeader.biYPelsPerMeter = 0;

inputBitmapInfo.bmiHeader.biClrUsed = 0;

inputBitmapInfo.bmiHeader.biClrImportant = 0;


// 2. 이미지 버퍼에 메모리를 할당한다. 

inputRGBbuffer = (unsigned char *)malloc(inputWidthPerRegion*inputWidthPerRegion*4);

memset(inputRGBbuffer, 255, inputWidthPerRegion*inputWidthPerRegion*4);


// 3. 그림을 그릴 영역의 크기를 받아온다. (IDC_STATIC_INPUT은 picture box이다.)

CStatic *inputPictureBox = (CStatic *)GetDlgItem(IDC_STATIC_INPUT);

RECT stage_input_rect;

inputPictureBox->GetClientRect(&stage_input_rect);


// 4. 마진을 추가한다. 

int input_rect_margin = 10;

stage_input_rect.bottom += input_rect_margin;

stage_input_rect.right += input_rect_margin;

stage_input_rect.top += input_rect_margin;

stage_input_rect.left += input_rect_margin;

// 5. stage의 위치를 저장한다. 이 위치에 실제로 그린다. 

inputStageTop = stage_input_rect.top;

inputStageBottom = stage_input_rect.bottom;

inputStageRight = stage_input_rect.right + 0;

inputStageLeft = stage_input_rect.left + 0;

inputStageWidth = inputStageRight - inputStageLeft;

inputStageHeight = inputStageBottom - inputStageTop;

inputStageXoffset = 0, inputStageYoffset = 0;

inputStageZoom = 1;



3. 그림을 그리기 위한 타이머를 선언하자. 


 타이머는class wizard로 선언한다. 

void CHTMDlg::OnTimer(UINT_PTR nIDEvent)

{

// TODO: Add your message handler code here and/or call default

DrawStuff();

CDialogEx::OnTimer(nIDEvent);

}



4. 타이머 안에서 그림을 그릴 함수를 정의하자. 


void CHTMDlg::DrawStuff()

{

CClientDC dc(this); // device context for painting

for(int i=0;i<inputWidthPerRegion;i++)

{

for(int j=0;j<inputWidthPerRegion;j++)

{

for (int k = 0; k<4; k++)

{

inputRGBbuffer[ i*inputWidthPerRegion*4 + j*4 + k ] = (int)(rand()%255); // 랜덤한 색

}

}

}


::SetStretchBltMode(dc.m_hDC, COLORONCOLOR);

::StretchDIBits(dc.m_hDC

, inputStageLeft, inputStageTop, inputStageWidth, inputStageHeight

, (int)( inputHalfWidthPerRegion*(1-inputStageZoom) + inputStageXoffset + 0.5 )

, (int)( inputHalfWidthPerRegion*(1-inputStageZoom) + inputStageYoffset + 0.5 )

, (int)( (inputWidthPerRegion*inputStageZoom) + inputStageXoffset + 0.5)

, (int)( (inputWidthPerRegion*inputStageZoom) + inputStageYoffset + 0.5 )

, inputRGBbuffer, &inputBitmapInfo, DIB_RGB_COLORS, SRCCOPY); 

}



5. 실행하면 다음과 같이 나온다.