注意:crTransparent设置为RGB(255,0,255),否则可能会有问题。
参考网友资料:
半透明原理:
假设LCD是256色的。颜色格式为332(RGB)
显存中的每一个字节的数据对应一个象素点。
在数据写入显存之前,读取相应相素点值,然后与新的数据按一定的规则混合之后,再写入相应像素点的显存。
这样主要问题关键是混合算法。
混合算法目前在游戏上常用到的算法是AlphaBlend。计算公式如下
假设一幅图象是A,另一幅透明的图象是B,那么透过B去看A,看上去的图象C就是B和A的混合图象,设B图象的透明度为alpha(取值为0-1,1为完全透明,0为完全不透明),Alpha混合公式如下:
R(C)=(1-alpha)*R(B)+alpha*R(A)
G(C)=(1-alpha)*G(B)+alpha*G(A)
B(C)=(1-alpha)*B(B)+alpha*B(A)
R(x)、G(x)、B(x)分别指颜色x的RGB分量原色值。从上面的公式可以知道,Alpha其实是一个决定混合透明度的数值。应用Alpha混合技术,可以实现游戏中的许多特效,比如火光、烟雾、阴影、动态光源等半透明效果。
HBITMAP LoadPngImage(const wchar_t* pfileName, COLORREF crTransparent)
{IImagingFactory *pImgFactory = NULL;
IImage *pImage = NULL;
HBITMAP hBitmap = NULL;
// Normally you would only call CoInitialize/CoUninitialize
// once per thread. This sample calls CoInitialize in this
// draw function simply to illustrate that you must call
// CoInitialize before calling CoCreateInstance.
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Create the imaging factory.
if (SUCCEEDED(CoCreateInstance (CLSID_ImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IImagingFactory,
(void **)&pImgFactory)))
{
// Load the image from the JPG file.
pImgFactory->CreateImageFromFile(pfileName, &pImage);
pImgFactory->Release();
}
if( pImage ) {
COLORREF crTransColor = crTransparent;
ImageInfo tempimageinfo;
pImage->GetImageInfo(&tempimageinfo);
HDC hWinDC = ::GetWindowDC(NULL);
HDC memedc = ::CreateCompatibleDC(NULL);
RECT rcWin = { 0, 0, tempimageinfo.Width-1, tempimageinfo.Height-1 };
hBitmap = ::CreateCompatibleBitmap( hWinDC, rcWin.right-rcWin.left, rcWin.bottom-rcWin.top );
HBITMAP holdBitmap = (HBITMAP)::SelectObject(memedc, hBitmap);
::FillSolidRect(memedc, &rcWin, crTransColor);
pImage->Draw( memedc, &rcWin, NULL);
pImage->Release();
::SelectObject(memedc, holdBitmap);
::DeleteDC( memedc );
ReleaseDC( NULL, hWinDC );
}
CoUninitialize();
return hBitmap;
}
HRGN GetRegionFromImage(HBITMAP hBmp, COLORREF cTransparentColor, COLORREF cTolerance)
{
HRGN hRgn = NULL;
if (hBmp)
{
//Create a memory DC inside which we will scan the bitmap content
HDC hMemDC = CreateCompatibleDC(NULL);
if (hMemDC)
{
//Get bitmap size
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
// Create a 32 bits depth bitmap and select it into the memory DC
BITMAPINFOHEADER RGB32BITSBITMAPINFO = {
sizeof(BITMAPINFOHEADER), //biSize
bm.bmWidth, //biWidth;
bm.bmHeight, //biHeight;
1, //biPlanes;
32, //biBitCount
BI_RGB, //biCompression;
0, //biSizeImage;
0, //biXPelsPerMeter;
0, //biYPelsPerMeter;
0, //biClrUsed;
0 //biClrImportant;
};
VOID * pbits32;
HBITMAP hbm32 = CreateDIBSection(hMemDC, (BITMAPINFO *)&RGB32BITSBITMAPINFO, DIB_RGB_COLORS, &pbits32, NULL, 0);
if (hbm32)
{
HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);
//Create a DC just to copy the bitmap into the memory DC
HDC hDC = CreateCompatibleDC(hMemDC);
if (hDC)
{
//Get how many bytes per row we have for the bitmap bits (rounded up to 32 bits)
BITMAP bm32;
GetObject(hbm32, sizeof(bm32), &bm32);
while (bm32.bmWidthBytes % 4)
bm32.bmWidthBytes++;
//Copy the bitmap into the memory DC
HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
//cTransparentColor = ::GetPixel(hDC, 0, 0);
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);
//For better performances, we will use the ExtCreateRegion() function to create the
//region. This function take a RGNDATA structure on entry. We will add rectangles by
//amount of ALLOC_UNIT number in this structure.
#define ALLOC_UNIT 100
DWORD maxRects = ALLOC_UNIT;
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
//Keep on hand highest and lowest values for the "transparent" pixels
BYTE lr = GetBValue(cTransparentColor);
BYTE lg = GetGValue(cTransparentColor);
BYTE lb = GetRValue(cTransparentColor);
BYTE hr = min(0xff, lr + GetRValue(cTolerance));
BYTE hg = min(0xff, lg + GetGValue(cTolerance));
BYTE hb = min(0xff, lb + GetBValue(cTolerance));
//Scan each bitmap row from bottom to top (the bitmap is inverted vertically)
BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;
for (int y = 0; y < bm.bmHeight; y++)
{
//Scan each bitmap pixel from left to right
for (int x = 0; x < bm.bmWidth; x++)
{
//Search for a continuous range of "non transparent pixels"
int x0 = x;
LONG *p = (LONG *)p32 + x;
while (x < bm.bmWidth)
{
BYTE b = GetRValue(*p);
if (b >= lr && b <= hr)
{
b = GetGValue(*p);
if (b >= lg && b <= hg)
{
b = GetBValue(*p);
if (b >= lb && b <= hb)
//This pixel is "transparent"
break;
}
}
p++;
x++;
}
if (x > x0)
{
//Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region
if (pData->rdh.nCount >= maxRects)
{
GlobalUnlock(hData);
maxRects += ALLOC_UNIT;
hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE);
pData = (RGNDATA *)GlobalLock(hData);
}
RECT *pr = (RECT *)&pData->Buffer;
SetRect(&pr[pData->rdh.nCount], x0, y, x, y + 1);
if (x0 < pData->rdh.rcBound.left)
pData->rdh.rcBound.left = x0;
if (y < pData->rdh.rcBound.top)
pData->rdh.rcBound.top = y;
if (x > pData->rdh.rcBound.right)
pData->rdh.rcBound.right = x;
if (y+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = y + 1;
pData->rdh.nCount++;
//On Windows98, ExtCreateRegion() may fail if the number of rectangles is too
//large (ie: > 4000). Therefore, we have to create the region by multiple steps.
if (pData->rdh.nCount == 2000)
{
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
if (hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
else hRgn = h;
pData->rdh.nCount = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
}
}
}
//Go to next row (remember, the bitmap is inverted vertically)
p32 -= bm32.bmWidthBytes;
}
//Create or extend the region with the remaining rectangles
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
if (hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
}
else
hRgn = h;
//Clean up
GlobalFree(hData);
SelectObject(hDC, holdBmp);
DeleteDC(hDC);
}
DeleteObject(SelectObject(hMemDC, holdBmp));
}
DeleteDC(hMemDC);
}
}
return hRgn;
}