- 전형적인 이미지 출력
- 윈도우 프로그래밍에서 비트맵 이미지를 출력하는 전형적인 방법
- 리소스로 등록된 비트맵 이미지를 메모리 DC에 로드하여 화면 DC로 출력(BitBlt())
- 이미지의 크기를 변경(StretchBlt())
- 일부 영역을 투명하게 처리(TransparentBlt())
- 반투명하게 처리(AlphaBlend()) - 비트맵(Bitmap)에 대한 이론
- 장치 의존적인 비트맵(DDB, Device Dependent Bitmap) & 장치 독립적인 비트맵(DIB, Device Independent Bitmap)으로 나뉨
- 우리가 아는 비트맵 파일은 모두 DIB라고 보아도 무방...
- 비트맵을 우리 눈으로 보려면 DDB로 변환이 필요
- 변환 작업은 운영체제가 한다 - 비트맵 리소스 출력
- 화면 DC와 호환되는 메모리 DC
- 가령 현재 모니터가 32비트 트루 컬러라면 메모리 DC도 같게 32비트 트루 컬러로 설정
- BitBlt()
- 메모리 DC에 로드된 비트맵 이미지를 화면 DC로 전송하면 내부적으로 DIB가 DDB로 변환되어 모니터 화면에 출력
- 마지막 인자 : 래스터 연산(Raster Operation)을 어떻게 할지 명시
- SRCCOPY - 원본 그대로 화면 DC에 복사
- SRCAND - 원본과 대상을 AND 연산
- NOTSRCCOPY - 원본 이미지의 색상을 반전
- DSTINVERT - 원본 이미지와는 상관없이 대상 이미지를 반전
- 고급 이미지 출력 함수
- TransparentBlt()
- AlphaBlend()
- CImage 클래스의 활용
- Visual C++ 6.0에선 외부 이미지 파일을 로드하는게 번거로웠다. 비트맵 파일을 제외한 JPEG, PNG, GIF 와 같은 이미지 파일을 로드하려면 별도의 외부 라이브러리를 사용해야 했다.
- CImage는 다양한 형식의 파일을 지원한다.
- CImage 클래스를 사용하려면 반드시 atlimage.h 파일을 인클루드해야 한다.
- CImage 클래스는 MFC가 제공하는 ATL(ActiveX Template Library) 클래스 중 하나이다. 그래서 내부적으로 COM(Component Object Model) 객체로 구현되어 있다.
- C++의 클래스가 논리적인 코드를 객체화한 개념이라면 COM은 실행 바이너리 파일 단위(모듈 단위)로 객체화한 것이라 할 수 있다. 그래서 메서드가 반환하는 값은 HRESULT형이며, FAILED() 매크로나 SUCCEEDED() 매크로를 이용해 오류를 확인한다.
- LoadFromResource() 메서드는 비트맵 리소스를 로드하여 비트맵 이미지를 생성한다.
- 색상(Color)의 변환
- RGB 컬러를 흑백으로 변환
- 바탕화면의 일부를 변환하여 출력
- GetDesktopWindow() 메서드는 바탕화면 윈도우 객체의 포인터 반환
- CWindowDC 클래스는 비클라이언트 영역을 포함한 윈도우 전체에 대한 DC
- CDC 클래스의 GetDeviceCaps() 메서드는 인자로 전달받은 인덱스(BITPIXEL, HORZSIZE, VERTSIZE 등)에 해당하는 DC 정보를 반환
- 바탕화면을 별도의 이미지 파일로 저장
- GetWindowRect()로 바탕화면 윈도우 크기 얻어옴
- CImage 클래스의 Save() 함수는 현재 선택한 비트맵 이미지를 주어진 파일 형식으로 변환하여 저장
- ::ShellExecute() API 함수를 이용하여 이미지 파일 실행
void CBmpDisplayDemoView::OnPaint()
{
CPaintDC dc(this);
CDC MemDC;
BITMAP bmpInfo;
//화면 DC와 호환되는 메모리 DC를 생성
MemDC.CreateCompatibleDC(&dc);
//비트맵 리소스를 로딩
CBitmap bmp;
CBitmap* pOldBmp = NULL;
bmp.LoadBitmap(IDB_Test_Image);//IDB_Test_Image 비트맵 리소스를 미리 등록해야 함.
//로딩된 비트맵의 정보를 알아본다.
bmp.GetBitmap(&bmpInfo);
//메모리 DC에 선택한다.
pOldBmp = MemDC.SelectObject(&bmp);
//메모리 DC에 들어 있는 비트맵을 화면 DC로 복사하여 출력
dc.BitBlt(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, &MemDC, 0, 0, SRCCOPY);
//두 배 확대하여 출력
//dc.StrectchBlt(200, 200, 250 * 2, 300 * 2, &MemDC, 350, 200, 250, 300, SRCCOPY);
MemDC.SelectObject(pOldBmp);
}
void CBmpDisplayDemoView::OnPaint()
{
CPaintDC dc(this);
CDC MemDC;
BITMAP bmpInfo;
MemDC.CreateCompatibleDC(&dc);
CBitmap bmp;
CBitmap* pOldBmp = NULL;
bmp.LoadBitmap(IDB_Test_Image);
bmp.GetBitmap(&bmpInfo);
pOldBmp = MemDC.SelectObject(&bmp);
dc.StrectchBlt(20, 20, bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2, &MemDC, 0, 0, bmpInfo.bmWidth, bmInfo.bmHeight, SRCCOPY);
dc.TransparentBlt(20, 100,
bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2,
&MemDC,
0, 0,
bmpInfo.bmWidth, bmInfo.bmHeight,
RGB(0, 0, 0)); //투명 처리될 색상의 RGB 값
MemDC.SelectObject(pOldBmp);
}
{
CPaintDC dc(this);
CDC MemDC;
BITMAP bmpInfo;
MemDC.CreateCompatibleDC(&dc);
CBitmap bmp;
CBitmap* pOldBmp = NULL;
bmp.LoadBitmap(IDB_Test_Image);
bmp.GetBitmap(&bmpInfo);
pOldBmp = MemDC.SelectObject(&bmp);
dc.StrectchBlt(20, 20, bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2, &MemDC, 0, 0, bmpInfo.bmWidth, bmInfo.bmHeight, SRCCOPY);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 50;
bf.AlphaFormat = 0;
dc.AlphaBlend(20, 100,
bmpInfo.bmWidth * 2, bmInfo.bmHeight * 2,
&MemDC,
0, 0,
bmpInfo.bmWidth, bmInfo.bmHeight,
bf);
MemDC.SelectObject(pOldBmp);
}
BLENDFUNCTION 구조체
BlendOp 멤버 : 원본/대상 이미지를 서로 섞음
BlendFlags : 사용 안 함(반드시 0)
SourceConstantAlpha : 투명도(0~255), 0에 가까울수록 투명
AlphaFormat : 0 or AC_SRC_ALPHA, AC_SRC_ALPHA인 경우는 진정한 32비트 비트맵인 경우
void CImgOutDemoView::OnPaint()
{
CPaintDC dc(this);
//로드할 이미지 파일 경로
CString strImagePath = _T("IMG_0076.jpg");
//이미지 파일 로드
CImage Image;
HRESULT hResult = Image.Load(strImagePath);
if(FAILED(hResult))
{
CString strtmp = _T("ERROR: Failed to load");
strtmp += strImagePath + _T("\n");
TRACE(strtmp);
return;
}
//화면 DC에 출력
Image.BitBlt(dc.m_hDC, 0, 0);
}
void CImgOutDemoView::OnPaint()
{
CPaintDC dc(this);
CImage Image;
//비트맵 리소스를 로드하여 DIB 생성
Image.LoadFromResource(AfxGetInstanceHandle(), IDB_Image_Test);
//비트맵 이미지에 대한 DC 생성
CDC* pDC = CDC::FromHandle(Image.GetDC());
//이 이미지 DC에 문자열 출력
pDC->SetBkMode(TRANSPARENT);
pDC->TextOut(200, 30, ,TEXT("CImage sample!"));
Image.ReleaseDC();
//이미지를 화면 DC에 출력
Image.BitBlt(dc.m_hDC, 0, 0);
//문자열을 화면 DC에 출력
//dc.SetBkMode(TRANSPARENT);
//dc.TextOut(200, 30, ,TEXT("CImage sample!"));
}
inline void RGBtoGray(COLORREF& rgb)
{
BYTE byGray = (GetRValue(rgb) * 30
+ GetGValue(rgb) * 59
+ GetBValue(rgb) * 11) / 100;
rgb = RGB(byGray, byGray, byGray);
}
// CSaveGrayDemoView 메시지 처리기
void CSaveGrayDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
//바탕화면 윈도우 객체에 대한 포인터를 얻음
CWnd* pWndDesktop = GetDesktopWindow();
CWindowDC SrcDC(pWndDesktop);
CClientDC dc(this);
//바탕화면 크기 및 색상수와 동일한 비트맵 이미지를 만든다.
CImage Image;
Image.Create(300, 300, SrcDC.GetDeviceCaps(BITSPIXEL));
//이미지 DC와 화면 DC에 바탕화면 윈도우 DC를 출력한다.
CDC* pDC = CDC::FromHandle(Image.GetDC());
pDC->BitBlt(0, 0, 300, 300, &SrcDC, 0, 0, SRCCOPY);
Image.ReleaseDC();
//일부(200 * 200)를 흑백 이미지로 변환
COLORREF rgb;
for(int x = 0; x < 200; x++)
{
for(int y = 0; y < 200; y++)
{
rgb = Image.GetPixel(x, y);
//Gray RGB 값으로 변환
RGBtoGray(rgb);
Image.SetPixel(x, y, rgb);
}
}
//흑백으로 변환된 이미지를 화면 DC에 출력
Image.BitBlt(dc.m_hDC, 0, 0);
CView::OnLButtonDown(nFlags, point);
}
void CSaveGrayDemoView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
//바탕화면 윈도우 객체에 대한 포인터를 얻음
CWnd* pWndDesktop = GetDesktopWindow();
CWindowDC SrcDC(pWndDesktop);
CClientDC dc(this);
//바탕화면 윈도우의 크기를 알아낸다.
CRect Rect;
pWndDesktop->GetWindowRect(&Rect);
//바탕화면 크기 및 색상수와 동일한 비트맵 이미지를 만든다.
CImage Image;
int cx = Rect.Width();
int cy = Rect.Height();
Image.Create(cx, cy, SrcDC.GetDeviceCaps(BITSPIXEL));
//이미지 DC와 화면 DC에 바탕화면 윈도우 DC를 출력한다.
CDC* pDC = CDC::FromHandle(Image.GetDC());
pDC->BitBlt(0, 0, cx, cy, &SrcDC, 0, 0, SRCCOPY);
dc.BitBlt(0, 0, cx, cy, pDC, 0, 0, SRCCOPY);
Image.ReleaseDC();
//JPEG 형식으로 바탕 화면 이미지를 저장한다.
Image.Save(TEXT("desktop.jpg"), Gdiplus::ImageFormatJPEG);
//저장된 이미지를 뷰어를 실행하여 보여준다.
::ShellExecute(NULL, TEXT("open"), TEXT("desktop.jpg"), NULL, NULL, SW_SHOW);
CView::OnRButtonDown(nFlags, point);
}