BITMAP-DIB変換とウインドウのBMP保存

 ウインドウのクライアント領域には、通常WM_PAINTメッセージの処理時にクライアント領域のデバイスコンテキストに描画した内容が表示されています。ただ、この内容はデバイスコンテキストに描画した結果なので描く時は良いのですが、プログラムの方で内容を取得しようとすると直接ピクセル列を取得する事が出来ず行き詰まる事になります。

例えばクライアント領域をBMPに保存したい、とかクリップボードにコピーしたい、という時にピクセル列をまとめて取得したい事もあるでしょう。しかし、クライアント領域の内容はデバイス依存の内部形式ですから、そのままでは利用できません。そこで、今回はウインドウの内容を一度BITMAPに描きそのBITMAPDIBに変換する事でピクセル列を取得してみる事にします。

クライアント領域のコピー

 まず、ウインドウのクライアント領域をBITMAPにコピーしてみます。手順としては、クライアント領域と同じ大きさでクライアント領域のデバイスコンテキスト互換のBITMAPを作成し、そこにクライアント領域全体をBitBtl()すれば良いでしょう。

  GetClientRect(hwnd,&rec); /* クライアント領域取得 */

  dwWidth=rec.right;
  dwHeight=rec.bottom;

 最初に対象となるウインドウ(ハンドルhwnd)のクライアント領域の大きさをGetClientRectで求め、ウインドウのデバイスコンテキストを取得します。

  /* ウインドウのデバイスコンテキスト取得 */
  hdc=GetDC(hwnd);

 ウインドウのデバイスコンテキストとクラインと領域の大きさが求まったら、それを元にBITMAPを作成し、クライアント領域の内容をBITMAPにコピー。

  /* ウインドウのデバイスコンテキスト互換のBITMAP作成 */
  hBMP=CreateCompatibleBitmap(hdc,dwWidth,dwHeight);

  /* BITMAPにウインドウのクライアント領域をコピー */
  hdcMem=CreateCompatibleDC(hdc);
  SelectObject(hdcMem,hBMP);
  BitBlt(hdcMem,0,0,dwWidth,dwHeight,hdc,0,0,SRCCOPY);

 これでBITMAPのハンドルhBMPにクライアント領域全体がコピーされました。

BITMAP-DIB変換

 次にhBMPを24ビットフルカラーDIBに変換し、このDIBBMPファイルに保存してみましょう。BITMAPDIBに変換するには、変換後の色形式やサイズを設定したBITMAPINFOとピクセル列を保存するバッファを作って、GetDIBits()に渡します。今回は、BMPに保存するのでDIBのヘッダとピクセル列、それにファイルのヘッダをまとめて確保しました。

  if ((dwWidth*3) % 4==0) /* バッファの1ラインの長さを計算 */
      dwLength=dwWidth*3;
  else
      dwLength=dwWidth*3+(4-(dwWidth*3) % 4);

  /* 書き込み用バッファのサイズ計算 */
  dwFSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwLength*dwHeight;

  /* バッファ確保とポインタ設定 */
  lpBuf=(LPBYTE)GlobalAlloc(GPTR,dwFSize);
  lpHead=(LPBITMAPFILEHEADER)lpBuf;
  lpInfo=(LPBITMAPINFOHEADER)(lpBuf+sizeof(BITMAPFILEHEADER));
  lpPixel=lpBuf+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

  /* 24ビットBMPファイルのヘッダ作成 */
  lpHead->bfType='M'*256+'B';
  lpHead->bfSize=dwFSize;
  lpHead->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
  lpInfo->biSize=sizeof(BITMAPINFOHEADER);
  lpInfo->biWidth=dwWidth;
  lpInfo->biHeight=dwHeight;
  lpInfo->biPlanes=1;
  lpInfo->biBitCount=24;

 バッファを確保しヘッダを設定したら、ピクセル列をGetDIBits()lpPixelに取得します。今回は、24ビットフルカラーでカラーテーブルは使いませんので、LPBITMAPINFOHEADER型ポインタlpInfoLPBITMAPINFOにキャストして使う事にしました。

  GetDIBits(hdc,hBMP,0,dwHeight,lpPixel,(LPBITMAPINFO)lpInfo,DIB_RGB_COLORS);

 なお、GetDIBitsに渡すBITMAPはデバイスコンテキストに選択した状態では渡さないように、との事なのでメモリデバイスコンテキストに選択してクライアント領域をBitBltしたら選択を解除しておきましょう。これで、lpBufBMPのイメージが出来ました。後は、このイメージを書き出せばBMPになります。

  /* バッファをファイルに書き出す */
  fh=CreateFile(lpszFn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  WriteFile(fh,lpBuf,dwFSize,&dwSize,NULL);
  CloseHandle(fh);

 以上のことをまとめると、指定したウインドウウインドウのクライアント領域を指定したファイルに保存する関数saveBMP()は以下のようになります。

  void saveBMP(HWND hwnd,LPCTSTR lpszFn) {

      DWORD dwSize,dwFSize,dwWidth,dwHeight,dwLength;
      HANDLE fh;
      LPBITMAPFILEHEADER lpHead;
      LPBITMAPINFOHEADER lpInfo;
      LPBYTE lpBuf,lpPixel;
      RECT rec;
      HDC hdc,hdcMem;
      HBITMAP hBMP,hOld;

      GetClientRect(hwnd,&rec); /* クライアント領域取得 */

      dwWidth=rec.right;
      dwHeight=rec.bottom;

      if ((dwWidth*3) % 4==0) /* バッファの1ラインの長さを計算 */
          dwLength=dwWidth*3;
      else
          dwLength=dwWidth*3+(4-(dwWidth*3) % 4);

      /* 書き込み用バッファのサイズ計算 */
      dwFSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwLength*dwHeight;

      /* バッファ確保とポインタ設定 */
      lpBuf=(LPBYTE)GlobalAlloc(GPTR,dwFSize);
      lpHead=(LPBITMAPFILEHEADER)lpBuf;
      lpInfo=(LPBITMAPINFOHEADER)(lpBuf+sizeof(BITMAPFILEHEADER));
      lpPixel=lpBuf+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

      /* 24ビットBMPファイルのヘッダ作成 */
      lpHead->bfType='M'*256+'B';
      lpHead->bfSize=dwFSize;
      lpHead->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
      lpInfo->biSize=sizeof(BITMAPINFOHEADER);
      lpInfo->biWidth=dwWidth;
      lpInfo->biHeight=dwHeight;
      lpInfo->biPlanes=1;
      lpInfo->biBitCount=24;

      /* ウインドウのデバイスコンテキスト取得 */
      hdc=GetDC(hwnd);
      /* ウインドウのデバイスコンテキスト互換のBITMAP作成 */
      hBMP=CreateCompatibleBitmap(hdc,dwWidth,dwHeight);

      /* BITMAPにウインドウのクライアント領域をコピー */
      hdcMem=CreateCompatibleDC(hdc);
      hOld=SelectObject(hdcMem,hBMP);
      BitBlt(hdcMem,0,0,dwWidth,dwHeight,hdc,0,0,SRCCOPY);
      SelectObject(hdcMem,hOld);
      GetDIBits(hdc,hBMP,0,dwHeight,lpPixel,(LPBITMAPINFO)lpInfo,DIB_RGB_COLORS);

      ReleaseDC(hwnd,hdc);
      DeleteObject(hBMP);
      DeleteObject(hdcMem);

      /* バッファをファイルに書き出す */
      fh=CreateFile(lpszFn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
      WriteFile(fh,lpBuf,dwFSize,&dwSize,NULL);
      CloseHandle(fh);

      GlobalFree(lpBuf);

  }

プログラム

 プログラムを実行すると星空(?)を描画します。保存ボタンをクリックすると、現在のクライアント領域をtest.bmpというファイル名で保存するので、出来たファイルをダブルクリックして確認してみてください。

プログラムソース表示


Windowsプログラミング実験室 > プログラミング資料庫