- ベストアンサー
ビットマップ編集の経験がある方へのアドバイスをお願いします
- ビットマップの作成結果が真っ黒な状態になってしまいます。おそらく、ビットマップ情報がメモリDCに描画されていないまま作成されているのが原因ではないかと思います。
- 現在の操作ではBitbltがサポートされていないため、別の方法でメモリDCに転送する必要があるかもしれません。
- ビットマップ編集に経験のある方で、この問題の解決方法をご存知の方がいらっしゃいましたら、アドバイスをいただければと思います。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
見たところ、おかしな点は無いような気がしますが・・・ 以下は、私の使っているHDCの内容をBMPに出力する関数です。 bool SaveWindowImageForBMP(HDC hDC, int width, int height, const char *file_name) { if (file_name == NULL){ return false; } DWORD dwSize, dwFSize, dwWidth, dwHeight, dwLength; HANDLE fh; LPBITMAPFILEHEADER lpHead; LPBITMAPINFOHEADER lpInfo; LPBYTE lpBuf,lpPixel; HDC hdcMem; HBITMAP hBMP,hOld; dwWidth=width; dwHeight=height; if ((dwWidth*2) % 4==0){ // バッファの1ラインの長さを計算 dwLength=dwWidth*2; } else { dwLength=dwWidth*2+(4-(dwWidth*2) % 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); // 16ビット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=16; // ウインドウのデバイスコンテキスト互換のBITMAP作成 hBMP=CreateCompatibleBitmap(hDC,dwWidth,dwHeight); // BITMAPにウインドウのクライアント領域をコピー hdcMem=::CreateCompatibleDC(hDC); hOld=(HBITMAP)::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); ::DeleteObject(hBMP); ::DeleteObject(hdcMem); // バッファをファイルに書き出す fh=CreateFile(file_name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if(fh == INVALID_HANDLE_VALUE){ GlobalFree(lpBuf); //確保したメモリの開放 return false; } if (WriteFile(fh,lpBuf,dwFSize,&dwSize,NULL) == 0){ CloseHandle(fh); GlobalFree(lpBuf); //確保したメモリの開放 return false; } CloseHandle(fh); GlobalFree(lpBuf); //確保したメモリの開放 return true; }
その他の回答 (3)
- tettsu
- ベストアンサー率30% (4/13)
> 結果BitBltは使ってテストでアクティブウインドウを元DCにして試してみたのですが、完成時のビットマップが横方向に帯状に同じ画像が出力されていました。 画像は、どのようにして確認されたのでしょうか? 多分、ビットマップの仕様で、横幅のバイト数が4の倍数になるというのがあてはまりそうな気がします。 たとえば、10×10の24ビットカラーのビットマップの場合に、取得できるビットイメージは、 横32バイト(3バイト×10で30バイト。4の倍数にするため2を足す) 縦30バイト 合計960バイト必要になります。 > 元DCをプリンタのDCにした場合、なぜか真っ黒なビットマップになってしまいました。元DCをプリンタのDCに変えた場合とデスクトップのアクティブウインドウにした場合で何か違いが有ったのでしょうか? DC(デバイスコンテキスト)は、出力デバイスを仮想化したものです。(間違ってるかも知れませんが、私はこう理解してます。) ディスプレイに表示されているウインドウの場合は、表示されているままのビットイメージが取れます。 もし、この対象のウインドウに他のウインドウが上に重なっている場合は、対象ウインドウに重なった状態で取得されてしまいます。 プリンタのDCの場合、やったこと無いですが、他のアプリケーション等で印刷を指示して、実際に出力が始まるまでの間になら、ビットイメージが取得できる可能性があるかもしれないです。でも、もちろんハードウェアに依存すると思います。
補足
アドバイス有り難うございます。 >画像は、どのようにして確認されたのでしょうか? BITMAPFILEHEADER→BITMAPINFOHEADER→ビットマップ情報と順に新規作成ファイルに書き込んでいきました。出来たBMPファイルをペイントツールで開いて確認しました。 BITMAPFILEHEADER は以下になります。 bfType = 19778(BM) bfSize = 358454 bfReserved1、bfReserved2 = 0 bfOffBits= 54 BITMAPINFOHEADER は以下になります。 biSize = 40 biWidth = 560 biHeight = 320 biPlanes = 1 biBitCount = 16 biCompression = 0 biSizeImage = 358400 その他項目は 0 です。 BITMAPFILEHEADER を書き込んだ後、 char *szBuffer; hHndl = GlobalReAlloc(LhDIB,LdwLen,GMEM_MOVEABLE); // LdwLen = 358440 です。 LhDIB = hHndl; lpbi = (LPBITMAPINFOHEADER)GlobalLock(LhDIB); Liret = GetDIBits(MhBitmapMemDC, MhBitmapDC, 0, (WORD)LtpBmpIH.biHeight,(LPBYTE)lpbi + (WORD)lpbi->biSize + PaletteSize(lpbi),(LPBITMAPINFO)lpbi, DIB_RGB_COLORS); szBuffer = (char *)GlobalLock(LhDIB); lCount = (long)LtpBitmapFH.bfSize - sizeof (BITMAPFILEHEADER); lBuffCnt = 0; while(1) { if (lCount<=lBuffCnt) break; Liret = _lwrite( hF,&szBuffer[lBuffCnt], 1); lBuffCnt = lBuffCnt + 1; } で書き込んでファイルを作成しました。
- guccii
- ベストアンサー率31% (14/44)
以前GetDeviceCapsの使用を回答したものです。 うる憶えで答えてしまって、逆に時間をとらせてしまったみたいです。大変申し訳ありませんでした。
お礼
gucciiさん、お気になさらないで下さい。 gucciiさんに頂いたアドバイスはビットマップ編集初めての自分にとって充分に参考になりました。これからももし困って質問書き込むようなことがあれば又、アドバイスなどして頂ければ嬉しく思います。
- tettsu
- ベストアンサー率30% (4/13)
元DCの画像からビットマップを作成したいのでしょうか? であれば、通常なら MemDC = CreateCompatibleDC(元DC); //元DCと互換のメモリDC生成 hBitmap = CreateCompatibleBitmap(元DC,Width,LlnHeight);//元DCと互換のあるビットマップ生成 SelectObject(MemDC ,hBitmap);//メモリDCにビットマップを関連付け。この時点ではビットマップは空 このあとに、 Bitbltで、元DCから、MemDCへビットを転送するのですが、 Bitbltがサポートされていないデバイスコンテキストからだと、 GetPixelを使って、元DCから1ピクセルずつ取得して、 MemDCへSetPixelを使って書き込むことになると思います。 その後、GetDIBitsにMemDCと、hBitmapを渡せば、ビット情報が取得できると思います。 ですが、Bitbltをサポートしてないとなると、GetDIBitsもサポートしてない可能性もありますよね。 なので、最初のCreateCompatibleDCに渡すDCはデスクトップウインドウのDCが良いかもしれないです。 ちょっと後学の為に教えていただきたいのですが、Bitbltをサポートしてないデバイスコンテキストというと、プロッタ等のプリンタしか思い浮かばないのですが、元DCのハードウェアって何ですか?
お礼
丁寧にアドバイス頂き、有難うございます。 BitBltがサポートされていないと思ったのは自分の勘違いでGetDeviceCapsの引数に直接 RC_BITBLT を渡していたためにサポートされていないと思い込んでいたためでした、申しわけ有りません。 結果BitBltは使ってテストでアクティブウインドウを元DCにして試してみたのですが、完成時のビットマップが横方向に帯状に同じ画像が出力されていました。 下の図を1枚のビットマップ画像とすると 0123456789012 ←1本目の帯状画像 3456789012345 ←2本目の帯状画像 7890123456789 ←3本目の帯状画像 5678901234567 ←4本目の帯状画像 といったようにそれぞれの帯でずれた状態で帯状に描画されていました。 ヘッダ情報のbiBitCountを1に変更してモノクロ画像にすると若干ずれが有ったような気もしましたが一応ウインドウの画像がモノクロで描画できました。また、元DCをプリンタのDCにした場合、なぜか真っ黒なビットマップになってしまいました。元DCをプリンタのDCに変えた場合とデスクトップのアクティブウインドウにした場合で何か違いが有ったのでしょうか?
お礼
引き続き回答有難うございます。 自分でもなぜモノクロ以外だと正常に描画できないのか、プリンタDCからだと描画が正常にできないのか分かりません・・。 自分はVC++ Ver1.51を使用しているのでtettsuさんに書いていただいたソースコードと大きく変わるところといえば WriteFile関数の所なのかなと自分では思っています。 私のコードだと、ファイルに書き込むとき以下のようにループさせなければいけないので、szBufferの確保が正常に出来ていないのかとも考えたりしています。 _lwriteだと一度で書き込むことが出来ないみたいなので・・。 lCount = (long)LtpBitmapFH.bfSize - sizeof (BITMAPFILEHEADER); lBuffCnt = 0; while(1) { if (lCount<=lBuffCnt) break; Liret = _lwrite( hF,&szBuffer[lBuffCnt], 1); lBuffCnt = lBuffCnt + 1; } ここまで書いて頂いたのに自分で解消できず申しわけ有りません。 自分でももう少し進展するまで締め切るのを待とうと思います。 もし後ほどお気づきになるようなことがありましたら又アドバイスいただけると幸いです。
補足
アクティブウインドウからのビットマップ作成に成功しました。 ビットマップのサイズを小さくして作成してみた所、カラーでも正常に作成できたので、LlnSelObj が 65534 を超えてしまった場合に正常に出ないような気がしてヒュージに変えてみた所、正常に出力されました。 有り難うございました。 char _huge *a; a = (char *)GlobalLock(hDIB); LlnSelObj = (long)LtpBitmapFH.bfSize - sizeof (BITMAPFILEHEADER); LlnBuffCnt = 0; while(1) { if (LlnSelObj<=LlnBuffCnt) break; Liret = _lwrite( hF,a, 40); a = a + 40; LlnBuffCnt+=40; }