32ビットアルファチャネルビットマップを描画する方法
-
08-07-2019 - |
質問
アルファチャネル付きのbmp画像を表示するカスタムコントロールを作成する必要があります。背景はさまざまな色で塗ることができ、画像には影が付いているので、本当に「ペイント」する必要があります。アルファチャンネル。
その方法を知っている人はいますか?
また、可能であれば、アルファチャンネル情報を使用してマスクを作成し、マウスが画像または透明領域をクリックしたかどうかを確認したいです。
あらゆる種類のヘルプが歓迎されます!
ありがとう。
Edited(JDePedro):一部の人が示唆しているように、アルファブレンドを使用してアルファチャネルでビットマップをペイントしようとしています。これは、リソースから32ビットのビットマップをロードし、AlphaBlend関数を使用してペイントしようとする実装済みのテストです。
void CAlphaDlg::OnPaint()
{
CClientDC dc(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_BITMAP);
BITMAP BitMap;
bitmap.GetBitmap(&BitMap);
int nWidth = BitMap.bmWidth;
int nHeight = BitMap.bmHeight;
CBitmap *pOldBitmap = dcMem.SelectObject(&bitmap);
BLENDFUNCTION m_bf;
m_bf.BlendOp = AC_SRC_OVER;
m_bf.BlendFlags = 0;
m_bf.SourceConstantAlpha = 255;
m_bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(dc.GetSafeHdc(), 100, 100, nWidth, nHeight, dcMem.GetSafeHdc(), 0, 0,nWidth, nHeight,m_bf);
dcMem.SelectObject(pOldBitmap);
CDialog::OnPaint();
}
これは単なるテストであるため、ダイアログのOnPaintにコードを配置しました(CDCオブジェクトのAlphaBlend関数も試しました)。
不透明な領域は正しくペイントされていますが、ビットマップが透明になるはずの場所で白になります。
ヘルプ???
これはスクリーンショットです。見にくいですが、青い円の周りに白い長方形があります。 代替テキストhttp://img385.imageshack.us/img385/7965/alphamh8.png
わかりました。わかった!アルファ値のすべてのピクセルを事前に乗算する必要があります。誰かがそれを行うための最適化された方法を提案できますか?
解決
通常これを行う方法は、DIBSection-ピクセルを直接変更できるデバイスに依存しないビットマップです。残念ながら、DIBSectionsに対するMFCサポートはありません。Win32関数CreateDIBSection()を使用して使用する必要があります。
ビットマップを32ビットRGBAとしてロードすることから始めます(つまり、ピクセルあたり4バイト:赤、緑、青、およびアルファチャネル用に1つ)。コントロールで、適切なサイズのDIBSectionを作成します。次に、ペイントルーチンで
- ビットマップイメージを背景色とブレンドするためにアルファチャネルバイトを使用して、ビットマップデータをDIBSectionのビットマップデータにコピーします。
- デバイスコンテキストを作成し、それにDIBSectionを選択します。
- BitBlt()を使用して、新しいデバイスコンテキストからペイントデバイスコンテキストにコピーします。
アルファチャネル値を調べるだけで、未加工のビットマップデータを指定してマスクを作成できます。ここで何を尋ねているのかわかりません。
他のヒント
将来のグーグルユーザー向けに、機能する事前乗算関数を以下に示します。これは http://www.viksoe.dk/code/alphatut1.htmから取得したことに注意してください。 a>。
inline void PremultiplyBitmapAlpha(HDC hDC, HBITMAP hBmp)
{
BITMAP bm = { 0 };
GetObject(hBmp, sizeof(bm), &bm);
BITMAPINFO* bmi = (BITMAPINFO*) _alloca(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
::ZeroMemory(bmi, sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BOOL bRes = ::GetDIBits(hDC, hBmp, 0, bm.bmHeight, NULL, bmi, DIB_RGB_COLORS);
if( !bRes || bmi->bmiHeader.biBitCount != 32 ) return;
LPBYTE pBitData = (LPBYTE) ::LocalAlloc(LPTR, bm.bmWidth * bm.bmHeight * sizeof(DWORD));
if( pBitData == NULL ) return;
LPBYTE pData = pBitData;
::GetDIBits(hDC, hBmp, 0, bm.bmHeight, pData, bmi, DIB_RGB_COLORS);
for( int y = 0; y < bm.bmHeight; y++ ) {
for( int x = 0; x < bm.bmWidth; x++ ) {
pData[0] = (BYTE)((DWORD)pData[0] * pData[3] / 255);
pData[1] = (BYTE)((DWORD)pData[1] * pData[3] / 255);
pData[2] = (BYTE)((DWORD)pData[2] * pData[3] / 255);
pData += 4;
}
}
::SetDIBits(hDC, hBmp, 0, bm.bmHeight, pBitData, bmi, DIB_RGB_COLORS);
::LocalFree(pBitData);
}
したがって、OnPaintは次のようになります。
void MyButton::OnPaint()
{
CPaintDC dc(this);
CRect rect(0, 0, 16, 16);
static bool pmdone = false;
if (!pmdone) {
PremultiplyBitmapAlpha(dc, m_Image);
pmdone = true;
}
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 255;
bf.AlphaFormat = AC_SRC_ALPHA;
HDC src_dc = m_Image.GetDC();
::AlphaBlend(dc, rect.left, rect.top, 16, 16, src_dc, 0, 0, 16, 16, bf);
m_Image.ReleaseDC();
}
そして画像の読み込み(コントロールのコンストラクター内):
if ((HBITMAP)m_Image == NULL) {
m_Image.LoadFromResource(::AfxGetResourceHandle(), IDB_RESOURCE_OF_32_BPP_BITMAP);
}
背景色とアルファブレンドを行い、アルファを取り出す必要がありますチャネルをコントロールにペイントします。
アルファチャネルは、画像の4バイトごとに設定する必要があります。これをマスクに直接使用することも、4バイトごとに新しいマスクイメージにコピーすることもできます。
AlphaBlend関数を使用すると、非常に簡単にペイントできます。
マスクについては、が必要です。ビットマップのビットを取得し、興味のある各ピクセルのアルファチャンネルバイトを調べます。
RGBチャンネルとアルファチャンネルを事前に乗算する最適化された方法は、計算された乗算結果を含む[256] [256]配列をセットアップすることです。最初の次元はアルファ値、2番目はR / G / B値、配列内の値は必要な事前乗算値です。
この配列を正しく設定すると、次のように必要な値を計算できます:
R = multiplicationLookup[alpha][R];
G = multiplicationLookup[alpha][G];
B = multiplicationLookup[alpha][B];