본문 바로가기

API

캡쳐 화면 BMP로 저장하기

728x90
반응형

오늘은 요 몇일 동안 열심히 공부했던 화면 캡쳐, 메뉴생성 등을 이용해서 캡쳐한 화면을 BMP 파일로 저장하는 프로그램을 만들어 보려고합니다. 중간을 빼먹고 공부해서 그런지 파일 저장 부분이 상당히 어렵게 느껴지더군요 ㅜ_ㅜ
그래도 여차여차 책과 인터넷을 뒤지며-_-;;  납득이 갈 정도의 이해를 하게 되었죠...이걸 몇시간이나 했다니 ㅜ_ㅜ 역시 점점 둔화되가는 머리를 느낄수 있었던 부분이었습니다. 에궁

네. 혹시 화면 캡쳐나 메뉴만드는 방법을 모르시는 분들을 위해서 해당 링크를 달아 드리겠습니다.
전체 화면 캡쳐 : http://darkangelus.tistory.com/entry/API로-구현한-전체화면-캡쳐-소스
해당 포커스 윈도우 화면 캡쳐 : http://darkangelus.tistory.com/entry/포커스가-있는-윈도우-캡쳐
메뉴 등록하기 : http://darkangelus.tistory.com/entry/메뉴-만들기
단축키 등록 : http://darkangelus.tistory.com/entry/메뉴의-단축키-액셀러레이터-생성

위에 링크로 들어가시면 자세한 내용을 보실수 있으니 이 부분에 대해 모르신다면 한번 봐보시기를 바랍니다~

그럼 준비되어 있는 파일 저장 소스를 보면서 분석을 해보도록 하겠습니다.

void SaveBitmap(HBITMAP hbit, char *Path)

{

  BITMAPFILEHEADER bFile;                  //비트맵 파일 해더부분을 저장해둘 구조체

  BITMAPINFOHEADER bInfo;          //비트맵 정보 저장을 위한 구조체

  BITMAP bit;

  BITMAPINFO *pih;          //BITMAPINFOHEADER 구조체를 DIB의 크기, 컬러값을 받음. BITMAPINFOHEADER값을 포함하고 있음

  int PalSize;

  HANDLE hFile;

  DWORD dwWritten,Size;

  HDC hdc;

 

  hdc = GetDC(NULL);

 

  //비트맵 정보로부터 정보 구조체 초기화

  GetObject(hbit, sizeof(BITMAP), &bit);   //GDI 오브젝트에 대한 정보를 구함. &bit에 정보를 채움.

  bInfo.biSize=sizeof(BITMAPINFOHEADER);   //비트맵 정보를 저장.

  bInfo.biWidth=bit.bmWidth;

  bInfo.biHeight=bit.bmHeight;

  bInfo.biPlanes=1;

  bInfo.biBitCount=bit.bmPlanes*bit.bmBitsPixel;

  if(bInfo.biBitCount>8) bInfo.biBitCount=24;

  bInfo.biCompression=BI_RGB;

  bInfo.biSizeImage=0;

  bInfo.biXPelsPerMeter=0;

  bInfo.biYPelsPerMeter=0;

  bInfo.biClrUsed=0;

  bInfo.biClrImportant=0;

 

  //정보 구조체 + 팔레트 크기만큼 메모리 활당 정보 구조체를 복사

  PalSize=(bInfo.biBitCount==24?0:1 << bInfo.biBitCount)*sizeof(RGBQUAD);

  pih=(BITMAPINFO *)malloc(bInfo.biSize+PalSize);

  pih->bmiHeader=bInfo;

 

  //비트맵의 크기 구함

  GetDIBits(hdc,hbit,0,bit.bmHeight,NULL,pih,DIB_RGB_COLORS);    //버터에 지정된 형식의 비트맵의 비트를 가져옴.

  bInfo=pih->bmiHeader;

 

  //비트맵 크기가 구해지지 않았을 때 수작업으로 직접 계산

  if(bInfo.biSizeImage==0)

  {

    bInfo.biSizeImage=((((bInfo.biWidth*bInfo.biBitCount)+31) & ~31) >> 3) * bInfo.biHeight;

  }

 

  //래스터 데이터를 읽기 위해 메모리 재할당

  Size = bInfo.biSize+PalSize+bInfo.biSizeImage;

  pih=(BITMAPINFO *)realloc(pih, Size);

 

  //래스터 데이터 읽기.

  GetDIBits(hdc,hbit,0,bit.bmHeight,(PBYTE)pih+bInfo.biSize+PalSize,pih,DIB_RGB_COLORS);

 

  //파일 헤더

  bFile.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PalSize;

  bFile.bfReserved1=0;

  bFile.bfReserved2=0;

  bFile.bfSize=Size+sizeof(BITMAPFILEHEADER);

  bFile.bfType=0x4d42;

 

  //파일을 생성하고 파일 헤더와 정보 구조체, 팔레트, 래스터 데이터를 출력

  hFile=CreateFile(Path,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);    //파일 생성

  WriteFile(hFile,&bFile,sizeof(bFile),&dwWritten,NULL);  //파일에 데이터를 기록

  WriteFile(hFile,pih,Size,&dwWritten,NULL);              //파일에 데이터를 기록

 

  ReleaseDC(NULL, hdc);

  CloseHandle(hFile);

}

이번에는 좀 열심히 한 척좀 해보려고 주석도 많이 달아 났습니다. 뭐 화면캡쳐나 메뉴 생성등은 이전 포스트에 다 나와 있고 메뉴 형식 또한 똑같이(액셀러레이터가 틀리지도 모르겠네요) 해두었으니 그 부분은 이전 포스팅에서 봐주시면 감사하겠습니다.

비트맵으로 저장하기 위해서 BITMAPFILEHEADER, BITMAPINFOHEADER 구조체를 선언해줍니다. 받아온 hbit를 이용해서 오브젝트 정보를 구해 오고 그 내용을 토데로 비트맵 정보를 먼저 초기화 시켜주는 작업을 합니다. 해당 비트맵 정보를 토데로 비트맵을 메모리에 복사를 하게 됩니다.

그리고 파일을 만들 준비를 하게 되는데요. 파일헤더를 초기화 하는 작업을 하게 되는거죠. 그다음 아까 복사해둔 비트맵정보헤더와 파일헤더를 통해서 파일을 생성하게 되는 것입니다.

저는 이해하는데 참 힘들었는데...설명을 써놓고 보니 그렇게 어려운 부분은 없는거 같이 느껴지는군요-_-;
암튼 이 코드대로 하면 BMP 파일을 만들 수 있습니다. 프로그램 전체 소스를 보면 다음과 같습니다.

#include <windows.h>

#include "resource.h"

 

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hInst;

LPSTR lpszClass="SaveBitMapFile";

void RegistHotKey(HWND, UINT, UINT);  //핫키 등록 함수

void SaveBitmap(HBITMAP, char *);    //캡쳐된 화면 BMP 파일로 저장하는 함수

HBITMAP FullScreenCapture(HWND);      //전체화면 캡쳐 함수

HBITMAP FocusScreenCapture(HWND);    //포커스 있는 윈도우 캡쳐 함수

HBITMAP g_hBit;

 

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdParam, int nCmdShow)

{

  HWND hWnd;

  MSG Message;

  WNDCLASS WndClass;

  g_hInst = hInstance;

 

  WndClass.cbClsExtra=0;

  WndClass.cbWndExtra=0;

  WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

  WndClass.hCursor=LoadCursor(NULL, IDC_ARROW);

  WndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);

  WndClass.hInstance=hInstance;

  WndClass.lpfnWndProc=(WNDPROC)WndProc;

  WndClass.lpszClassName=lpszClass;

  WndClass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);

  WndClass.style=0;

  RegisterClass(&WndClass);

 

  hWnd=CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,

    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,

    (HMENU)NULL, hInstance,NULL);

  ShowWindow(hWnd, nCmdShow);

 

  HACCEL hAccel;

 

  hAccel=LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR1));

  while(GetMessage(&Message, 0, 0, 0))

  {

    if(!TranslateAccelerator(hWnd,hAccel,&Message))

    {

      TranslateMessage(&Message);

      DispatchMessage(&Message);

    }

  }

}

 

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)

{

  HMENU hMenu = GetMenu(hWnd);

  HDC hdc, hMemDC;

  PAINTSTRUCT ps;

  BITMAP bmp;

  OPENFILENAME OFN;                //파일 열기 공통 대화상자의 초기값 지정. 사용자가 대화상자에서 선택한 파일명을 이 구조체로 리턴

  char lpstrFile[MAX_PATH]="";

 

  switch(iMessage)

  {

  case WM_COMMAND:

    switch(LOWORD(wParam))

    {

    case ID_FILE_SAVE40001:

      memset(&OFN,0,sizeof(OPENFILENAME));      //파일 저장 공통 대화상자를 위해 메모리를 OPENFILENAME 구조체 값으로 채우는 작업

      OFN.lStructSize=sizeof(OPENFILENAME);

      OFN.hwndOwner=hWnd;

      OFN.lpstrFilter="Bmp File(*.Bmp)\0";

      OFN.lpstrFile=lpstrFile;

      OFN.nMaxFile=256;

      OFN.lpstrDefExt="bmp";

      OFN.lpstrTitle="파일 저장";

      if(GetSaveFileName(&OFN)!=0)              //파일 저장 대화 상자를 호출하고 OPENFILENAME 구조체를 리턴해줌.

      {

        SaveBitmap(g_hBit, lpstrFile);        //캡쳐 화면 BMP로 저장

      }

      break;

    case ID_FILE_EXIT:            //Exit

      DestroyWindow(hWnd);

      break;

    case ID_CAPTURE_FULLSCREEN:          //전체화면 캡쳐

      RegistHotKey(hWnd, MOD_CONTROL, VK_F5);        //핫키 등록 함수

      CheckMenuItem(hMenu, ID_CAPTURE_FULLSCREEN, MF_CHECKED);

      CheckMenuItem(hMenu, ID_CAPTURE_FOCUSSCREEN, MF_UNCHECKED);

      break;

    case ID_CAPTURE_FOCUSSCREEN:    //포커스 있는 화면 캡쳐

      RegistHotKey(hWnd, MOD_CONTROL, VK_F5);        //핫키 등록함수

      CheckMenuItem(hMenu, ID_CAPTURE_FOCUSSCREEN, MF_CHECKED);

      CheckMenuItem(hMenu, ID_CAPTURE_FULLSCREEN, MF_UNCHECKED);

      break;

    }

    return 0;

  case WM_HOTKEY:

    if(CheckMenuItem(hMenu, ID_CAPTURE_FOCUSSCREEN, MF_CHECKED))

    {

      g_hBit = FocusScreenCapture(hWnd);

    }

    else if(CheckMenuItem(hMenu, ID_CAPTURE_FULLSCREEN, MF_CHECKED))

    {

      g_hBit = FullScreenCapture(hWnd);

    }

    if(g_hBit != NULL)

    {

      EnableMenuItem(hMenu, ID_FILE_SAVE40001, MF_ENABLED);

    }

    CheckMenuItem(hMenu, ID_CAPTURE_FOCUSSCREEN, MF_UNCHECKED);

    CheckMenuItem(hMenu, ID_CAPTURE_FULLSCREEN, MF_UNCHECKED);

    InvalidateRect(hWnd, NULL, TRUE);

    UnregisterHotKey(hWnd,0);

    return 0;

  case WM_PAINT:

    hdc = BeginPaint(hWnd,&ps);

    if(g_hBit != NULL)

    {

      hMemDC = CreateCompatibleDC(hdc);

      SelectObject(hMemDC, g_hBit);

      GetObject(g_hBit, sizeof(BITMAP), &bmp);

      BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight, hMemDC, 0,0, SRCCOPY);

      DeleteDC(hMemDC);

    }

    return 0;

  case WM_DESTROY:

    PostQuitMessage(0);

    return 0;

  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));

}

 

void SaveBitmap(HBITMAP hbit, char *Path)

{

  BITMAPFILEHEADER bFile;                  //비트맵 파일 해더부분을 저장해둘 구조체

  BITMAPINFOHEADER bInfo;          //비트맵 정보 저장을 위한 구조체

  BITMAP bit;

  BITMAPINFO *pih;          //BITMAPINFOHEADER 구조체를 DIB의 크기, 컬러값을 받음. BITMAPINFOHEADER값을 포함하고 있음

  int PalSize;

  HANDLE hFile;

  DWORD dwWritten,Size;

  HDC hdc;

 

  hdc = GetDC(NULL);

 

  //비트맵 정보로부터 정보 구조체 초기화

  GetObject(hbit, sizeof(BITMAP), &bit);      //GDI 오브젝트에 대한 정보를 구함. &bit에 정보를 채움.

  bInfo.biSize=sizeof(BITMAPINFOHEADER);      //비트맵 정보를 저장.

  bInfo.biWidth=bit.bmWidth;

  bInfo.biHeight=bit.bmHeight;

  bInfo.biPlanes=1;

  bInfo.biBitCount=bit.bmPlanes*bit.bmBitsPixel;

  if(bInfo.biBitCount>8) bInfo.biBitCount=24;

  bInfo.biCompression=BI_RGB;

  bInfo.biSizeImage=0;

  bInfo.biXPelsPerMeter=0;

  bInfo.biYPelsPerMeter=0;

  bInfo.biClrUsed=0;

  bInfo.biClrImportant=0;

 

  //정보 구조체 + 팔레트 크기만큼 메모리 활당 정보 구조체를 복사

  PalSize=(bInfo.biBitCount==24?0:1 << bInfo.biBitCount)*sizeof(RGBQUAD);

  pih=(BITMAPINFO *)malloc(bInfo.biSize+PalSize);

  pih->bmiHeader=bInfo;

 

  //비트맵의 크기 구함

  GetDIBits(hdc,hbit,0,bit.bmHeight,NULL,pih,DIB_RGB_COLORS);        //버터에 지정된 형식의 비트맵의 비트를 가져옴.

  bInfo=pih->bmiHeader;

 

  //비트맵 크기가 구해지지 않았을 때 수작업으로 직접 계산

  if(bInfo.biSizeImage==0)

  {

    bInfo.biSizeImage=((((bInfo.biWidth*bInfo.biBitCount)+31) & ~31) >> 3) * bInfo.biHeight;

  }

 

  //래스터 데이터를 읽기 위해 메모리 재할당

  Size = bInfo.biSize+PalSize+bInfo.biSizeImage;

  pih=(BITMAPINFO *)realloc(pih, Size);

 

  //래스터 데이터 읽기.

  GetDIBits(hdc,hbit,0,bit.bmHeight,(PBYTE)pih+bInfo.biSize+PalSize,pih,DIB_RGB_COLORS);

 

  //파일 헤더

  bFile.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PalSize;

  bFile.bfReserved1=0;

  bFile.bfReserved2=0;

  bFile.bfSize=Size+sizeof(BITMAPFILEHEADER);

  bFile.bfType=0x4d42;

 

  //파일을 생성하고 파일 헤더와 정보 구조체, 팔레트, 래스터 데이터를 출력

  hFile=CreateFile(Path,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);    //파일 생성

  WriteFile(hFile,&bFile,sizeof(bFile),&dwWritten,NULL);  //파일에 데이터를 기록

  WriteFile(hFile,pih,Size,&dwWritten,NULL);              //파일에 데이터를 기록

 

  ReleaseDC(NULL, hdc);

  CloseHandle(hFile);

}

 

//핫키 등록 함수

void RegistHotKey(HWND hWnd, UINT mod, UINT vk)

{

  RegisterHotKey(hWnd, 0, mod, vk);

}

 

//포커스 가지고 있는화면 캡쳐 함수 - 해당 비트맵을 리턴

HBITMAP FocusScreenCapture(HWND hWnd)

{

  HDC hScrDC, hMemDC;

  HBITMAP hBitmap;

  RECT rt;

 

  //포커스를 가진 윈도우의 정보를 얻음.

  GetWindowRect(GetForegroundWindow(), &rt);

  rt.left = max(0, rt.left);

  rt.top = max(0, rt.top);

  rt.right = min(rt.right, GetSystemMetrics(SM_CXSCREEN));

  rt.bottom = min(rt.bottom, GetSystemMetrics(SM_CYSCREEN));

 

  //화면 전체 DC를 얻는다.

  hScrDC=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);

  hMemDC = CreateCompatibleDC(hScrDC);

  hBitmap = CreateCompatibleBitmap(hScrDC, rt.right-rt.left, rt.bottom-rt.top);

  SelectObject(hMemDC, hBitmap);

 

  //메모리 DC에 화면 DC를 복사 받는다.

  BitBlt(hMemDC, 0, 0, rt.right-rt.left, rt.bottom-rt.top, hScrDC, rt.left, rt.top, SRCCOPY);

 

  DeleteDC(hMemDC);

  DeleteDC(hScrDC);

  return hBitmap;

}

 

//전체 화면 캡쳐

HBITMAP FullScreenCapture(HWND hWnd)

{

  //전체 화면 크기 구함

  int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);

  int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);

 

  HDC hScrDC, hMemDC;

  HBITMAP hBitmap;

 

  //화면 전체 DC를 얻음

  hScrDC=CreateDC("DISPLAY",NULL,NULL,NULL);

  hMemDC = CreateCompatibleDC(hScrDC);

  hBitmap = CreateCompatibleBitmap(hScrDC, ScreenWidth, ScreenHeight);

  SelectObject(hMemDC, hBitmap);

 

  //메모리 DC에 화면 DC를 복사 받는다.

  BitBlt(hMemDC, 0, 0, ScreenWidth, ScreenHeight, hScrDC, 0, 0, SRCCOPY);

 

  DeleteDC(hMemDC);

  DeleteDC(hScrDC);

 

  return hBitmap;

}

 

resource.h

#define IDR_MENU1                       101
#define IDR_ACCELERATOR1                102
#define ID_FILE_SAVE40001               40001
#define ID_FILE_EXIT                    40002
#define ID_CAPTURE_FOCUSSCREEN          40007
#define ID_CAPTURE_FULLSCREEN           40011

이 프로그램은 단순한 화면 캡쳐 프로그램이죠. WndProc, SaveBitmap, RegistHotKey, FocusScreenCapture, FullScreenCapture 함수가 있고 SaveBitmap 함수는 캡쳐해온 이미지를 비트맵 파일로 저장시키는 함수, RegistHotKey 함수는 핫키를 저장할 때 사용되는 함수, FocusScreenCapture함수는 포커스가 있는 윈도우를 캡쳐하는 함수, FullScreenCapture 함수는 전체 화면을 캡쳐하는 함수이죠. 대략 설명을 하면 전체나, 부분 캡쳐를 실시후 세이브를 하게 되는 것입니다. 핫키는 컨트롤 F5로 지정되어있고, 캡쳐가 되어 있어야만 세이브를 할 수 있도록 되어 있습니다. 뭐 어려운 프로그램은 아니고 허접하지만 주석도 달려 있으니 다른 설명은 불필요 할 것 같군요 ^^

메뉴 설정을 위해서 CheckMenuItem함수를 썼는데 이녀석은 해당 메뉴 아이템에 체크 표시를 해두는 함수입니다.
EnableMenuItem함수는 해당 아이템을 활성화 또는 비활성화 하는 함수이죠.

왠지 여기까지 하고 나니 괜히 뿌듯해집니다. API의 A도 모르던 저인데...이런것 까지 할 수 있게되고 말이죠 ㅎ

암튼 허접한 내용 봐주셔서 감사합니다~

출처 : 윈도우즈 API 정복

728x90
반응형

'API' 카테고리의 다른 글

1초에 한번씩 캡쳐 화면 파일로 저장하기  (0) 2010.03.27
디렉토리 생성  (0) 2010.03.27
Win API Timer  (0) 2010.03.21
메뉴의 단축키, 액셀러레이터 생성  (0) 2010.03.12
메뉴 만들기  (1) 2010.03.11
포커스가 있는 윈도우 캡쳐  (0) 2010.03.10
API로 구현한 전체화면 캡쳐 소스  (0) 2010.03.09
DC 구조체에 관해....  (1) 2010.03.09
버튼 생성  (0) 2010.03.06
API의 첫 시작  (0) 2010.03.05