- ベストアンサー
C++ bmp 透過処理
標準C++で画像フォーマットはビットマップです。 画像Aの上に画像Bを乗せる。 画像Bの特定の色を透けさせる。 その透けた部分だけ画像A、それ以外は画像Bが表示される。 といった処理を実現したいです。 どのようにすればよろしいでしょうか。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 昔、ゲームプログラムの書籍に、unsigned long型のバッファ同士を四角形に見立てて、コピーし合う様な手段が紹介されていました(最終的にはOSが持つ描写関数へ渡されます)。 動作確認はしていますが、アラも在りそうなので、以下参考程度で。 struct IMAGE { int width; int height; unsigned long* image; }; static void Fill(IMAGE* p, int x, int y, int w, int h, unsigned long color) { x = std::min(std::max(0, x), p->width); y = std::min(std::max(0, y), p->height); w = std::min(std::max(0, w), p->width); h = std::min(std::max(0, h), p->height); for(int dy = y; dy < h; ++dy) { for(int dx = x; dx < w; ++dx) { p->image[dx + dy * p->width] = color; } } } static void Create(IMAGE* p, int width, int height, unsigned long color) { p->width = width; p->height= height; p->image = new unsigned long[p->width * p->height]; ::Fill(p, 0, 0, width, height, color); } static void Delete(IMAGE* p) { const IMAGE zero = {0}; delete [] p->image; *p = zero; } static void BitBlt(IMAGE* pdst, int xpos, int ypos, const IMAGE* psrc, unsigned long key) { const int x = std::max(0, xpos); const int y = std::max(0, ypos); const int x2 = xpos < 0 ? -xpos : 0; const int y2 = ypos < 0 ? -ypos : 0; for(int sy = y2, dy = y; (sy < psrc->height) && (dy < pdst->height); ++sy, ++dy) { for(int sx = x2, dx = x; (sx < psrc->width) && (dx < pdst->width); ++sx, ++dx) { const int dpos = dx + dy * pdst->width; const int spos = sx + sy * psrc->width; if(psrc->image[spos] == key) continue; pdst->image[dpos] = psrc->image[spos]; } } } //ココは大きく環境依存する static void SaveOSDepend(const IMAGE* p) { #pragma pack(push, 1) struct { BITMAPINFOHEADER bih; DWORD f[3]; } info = {0}; #pragma pack(pop) info.bih.biSize = sizeof(info.bih); info.bih.biBitCount = 32; info.bih.biPlanes = 1; info.bih.biCompression = BI_BITFIELDS; info.bih.biWidth = p->width; info.bih.biHeight = p->height; info.bih.biSizeImage = info.bih.biWidth * info.bih.biHeight * (info.bih.biBitCount / 8); info.f[0] = 0xff0000; info.f[1] = 0x00ff00; info.f[2] = 0x0000ff; BITMAPFILEHEADER bfh = {0}; bfh.bfType = *((LPWORD)"BM"); bfh.bfOffBits = sizeof(bfh) + sizeof(info); bfh.bfSize = bfh.bfOffBits + info.bih.biSizeImage; FILE* fp = ::fopen("test.bmp", "wb"); ::fwrite(&bfh, sizeof(bfh), 1, fp); ::fwrite(&info, sizeof(info), 1, fp); for(int y = info.bih.biHeight - 1; y >= 0; --y) { const int pos = y * info.bih.biWidth; ::fwrite(&p->image[pos], info.bih.biWidth, info.bih.biBitCount / 8, fp); } ::fclose(fp); } int main(int argc, _TCHAR* argv[]) { IMAGE imgA; IMAGE imgB; //画像A 緑色で作成 ::Create(&imgA, 128, 64, 0xff00); //画像B 赤色で作成 ::Create(&imgB, 256, 36, 0xff0000); //画像B 5ピクセル余白を残して、白色で塗る(赤色の枠に白色の内部見たいな感じ) ::Fill(&imgB, 5, 5, 256 - 5, 36 - 5, 0xffffff); //白色を透明色に指定して、画像Aに画像Bを転送する ::BitBlt(&imgA, 20, -2, &imgB, 0xffffff); //32bit(windows bitmap)に変換セーブ ::SaveOSDepend(&imgA); //画像A,Bの後始末 ::Delete(&imgA); ::Delete(&imgB); return 0; }
その他の回答 (3)
- machongola
- ベストアンサー率60% (434/720)
こんにちは。御礼いただきました。 32bit,16bitでbmpファイルを作成する際、BITMAPINFOHEADER構造体の後ろに、DWORD x 3の配列を付加させないといけません。 本来は、 ::malloc(sizeof(BITMAPINFOHEADER) + sizeof(DWORD) * 3); の様にして52バイト割り当てますが、メモリーリークの種を増やしたくなかったので、 #pragma pack(push, 1)//パディングが入ってサイズが崩れるとマズイいので念のため struct{ BITMAPINFOHEADER bih; DWORD f[3]; } info = {0}; #pragma pack(pop) 上記の様な52バイトの構造体を作成しました。 大体ビットマップのフォーマットは、以下の様になっています。 http://homepage2.nifty.com/tsugu/sotuken/ronbun/sec3-1.html http://dencha.ojaru.jp/programs_07/pg_graphic_04.html#color_mask 24bit BITMAPFILEHEADER BITMAPINFOHEADER イメージ 32, 16bit BITMAPFILEHEADER BITMAPINFOHEADER DWORD[3] イメージ モノクロ、16色、256色 BITMAPFILEHEADER BITMAPINFOHEADER RGBQUAD[2 又は 16 又は 256] イメージ ※(1)sizeof(DWORD) と sizeof(RGBQUAD)は等しい ※(2)その他、RLE4bit と RLE8bitが存在する ※(3)DWORD[3]の中身 32bitの場合 [0] = 0xff0000 [1] = 0xff00 [2] = 0xff 16bitの場合 [0] = 0xf800 [1] = 0x7e0 [2] = 0x1f 又は、 [0] = 0x7c00 [1] = 0x3e0 [2] = 0x1f を選択。 因みに当方が書いたコードは32bitですが、windows APIでビットマップハンドルを使用して構わないならば、このコードは回りくど過ぎるかもしれません。
お礼
補足ありがとうございます。 #pragma pack(push, 1)以下の処理に関してある程度は理解できました。 確認させていただきたいのですが、 http://ja.wikipedia.org/wiki/Windows_bitmap このプログラムでは32bit、16bitのみ対応で、24bitなどDWORD[3]が含まれない形式の場合は正常に動作しない、ということでよろしいでしょうか。(全ビット数に対応する場合は「1ピクセルあたりのビット数」を参照してIMAGE構造体の内容を変える?) また、BITMAPINFOHEADER構造体以外にも BITMAPINFOHEADER2:64byteだから新しい形式? BITMAPV5HEADER:今はこれが主流? などの構造体も存在するらしいですが、これらにも対応はしていないということですよね。 >因みに当方が書いたコードは32bitですが、windows APIでビットマップハンドルを使用して構わないならば、このコードは回りくど過ぎるかもしれません。 WindowsAPIの使用に関しては一向に構いません。(寧ろ BITMAPINFOHEADER bin; はAPIの構造体だと思っておりました) //画像表示 HDC hDC = GetDC( hWnd ); HDC hCompatDC = CreateCompatibleDC( hDC ); HBITMAP hPrevBitmap = (HBITMAP)SelectObject( hCompatDC, hBitmap ); BitBlt( hDC, 0, 0, 640, 480, hCompatDC, 0, 0, SRCCOPY ); 当初このような処理から透過処理を実現できないものかと考えておりました。 最も提供していただいたソースの方が他のフォーマットに対応する時(jpeg等)色々とやりやすそうですが。
- Situgyosya
- ベストアンサー率41% (21/51)
ビットマップにも色々とフォーマットがありますが、 画像の透過処理の一般的な理屈は 1.転送先の画像ビットデータに対して 2.転送元の画像ビットデータが透過色でなければ それをを上書きする だけです。 実際の関数は対象OS、ビットマップの種類、使用する ライブラリによって異なります。
お礼
ビットマップはピクセルあたり24ビットのものを想定しています。 (bmpの種類とはこのことですよね?) OSはWindowsです。 使用するライブラリは標準C++ライブラリです。 これだけでは説明不足でしょうか。 >2.転送元の画像ビットデータが透過色でなければそれをを上書きする 実際に透明にしているわけではないんですね。
- ICE_FALCON
- ベストアンサー率56% (63/111)
MFCじゃなくて? 標準関数のみでそんなことできるのか??? できるとしてもOS書かなきゃ。 MFCなら TransparentBlt AlphaBlend とか。
お礼
回答ありがとうございます。 使用するのはあくまで標準関数のみです。 >できるとしてもOS書かなきゃ。 失礼しました、OSはWindowsです。
補足
意味を取り違えたかもしれませんので補足します。 >標準関数のみ WindowsのAPIは使用します。
お礼
回答ありがとうございます。 APIにLoadImage関数が存在していたので、透過処理も何かbmp専用の関数が存在するのかと思っていました。 >動作確認はしていますが、アラも在りそうなので、以下参考程度で。 補足をいただきたい点がございます。 #pragma pack(1)を使用する理由: 「BITMAPINFOHEADER構造体の仕様」と「パッキング」が関係しているらしいということはわかりました。 しかしそれ以上の情報を発見できなかったため、 #pragma pack(push, 1) struct{ BITMAPINFOHEADER bih; DWORD f[3]; } info = {0}; #pragma pack(pop) のコードを解析できません。 宜しければ何をしているのか教えていただけないでしょうか。