一、半透明效果
半透明效果在游戏世界里是用的很频繁的,比如鬼魂,隐形人物。
简单的说,半透明是前景图案和背景图案像素颜色的混合。可以简单的把半透明处理,整理为一个公式:
半透明图色彩 = 前景图色彩× 不透明度 + 背景图色彩 × (1 -不透明度)
半透明处理的步骤:
1. 获取位图结构。
GetObject()获取位图的结构后,可得到位图信息。
2. 建立暂存数组
获得了位图结构后,接下来需要先建立一个暂时存储位图像素的颜色值。这个暂存数组的大小有前一步获得位图信息的bmHeight, bmWidthBytes决定。
这里有必要提一下BITMAP结构,因为在之后将会用到。
typedef struct tagBITMAP { LONG bmType; //位图类型 LONG bmWidth; //位图宽度 LONG bmHeight; //长度 LONG bmWidthBytes; //每一列像素所占Byte数 WORD bmPlanes; //颜色平面数 WORD bmBitsPixel; //像素位数 LPVOID bmBits; //位图内存指针 } BITMAP, *PBITMAP;
3. 取得位图位值。
建立数组后,可以用GetBitmapBits()来获得位图位值。
4. 合成像素颜色值
获得位图的所有像素颜色值后,按照公式完成半透明颜色值的合成。
5. 重设位图颜色。
根据数组的内容来重新设位图的颜色。使用SetBitmapBits() 。
完成后,最后就是贴图了。
范例:
首先创建win32项目。让系统生成基本框架代码。
在程序开始处添加全局变量。
HBITMAP bg, girl; HDC mdc; //常量 const int xstart= 50; const int ystart= 20;
然后在InitInstance()函数中添加代码
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; HDC hdc; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } MoveWindow(hWnd, 10, 10, 600, 450, true); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); hdc = GetDC(hWnd); mdc = CreateCompatibleDC(hdc); BITMAP bm1, bm2; unsigned char *px1,*px2;//用于存储位图额色彩值 //处理背景 bg = (HBITMAP)LoadImage(NULL, L"bg.bmp", IMAGE_BITMAP, 600, 450, LR_LOADFROMFILE); GetObject(bg, sizeof(BITMAP), &bm1);//将bg的位图信息获取到bm1 px1 = new unsigned char [bm1.bmHeight * bm1.bmWidthBytes];//动态定义数组,大小有获取位图决定 GetBitmapBits(bg, bm1.bmHeight * bm1.bmWidthBytes, px1);//获得位图位值到px1 girl = (HBITMAP)LoadImage(NULL, L"girl.bmp", IMAGE_BITMAP, 298, 329, LR_LOADFROMFILE); GetObject(girl, sizeof(BITMAP), &bm2);//将girl的位图信息获取到bm2 px2 = new unsigned char [bm2.bmHeight * bm2.bmWidthBytes]; GetBitmapBits(girl, bm2.bmHeight * bm2.bmWidthBytes, px2); int xend, yend; int x, y, i; int rgb_b; int PxBytes = bm1.bmBitsPixel / 8; xend = xstart + 298;//位图的大小为:*329 yend = ystart + 329; //处理背景像素颜色 //从(xstart,ystart)开始遍历半透明化位置的每个像素点 for (y = ystart; y < yend; y++) { for (x = xstart; x < xend; x++) { //rgb_b用于找到位图在px1中的位置(px1为一维数组) rgb_b = y * bm1.bmWidthBytes + x * PxBytes; px1[rgb_b] = px1[rgb_b] * 0.7; px1[rgb_b+1] = px1[rgb_b+1] * 0.7; px1[rgb_b+2] = px1[rgb_b+2] * 0.7; } } //处理前景图像像素颜色 //由于前景是从girl位图的(0,0)开始的 for (y = 0; y < (bm2.bmHeight); y++) { for (x = 0; x < bm2.bmWidth; x++) { rgb_b = y * bm2.bmWidthBytes + x * PxBytes; i = (ystart+y) * bm1.bmWidthBytes + (xstart+x) * PxBytes; px2[rgb_b] = px2[rgb_b] * 0.3 + px1[i]; px2[rgb_b+1] = px2[rgb_b+1] * 0.3 + px1[i+1]; px2[rgb_b+2] = px2[rgb_b+2] * 0.3 + px1[i+2]; } } SetBitmapBits(girl, bm2.bmWidthBytes * bm2.bmHeight, px2); MyPaint(hdc); ReleaseDC(hWnd, hdc); delete [] px1; delete [] px2; return TRUE; } void MyPaint(HDC hdc) { SelectObject(mdc, bg); BitBlt(hdc, 0, 0, 600, 450, mdc, 0, 0, SRCCOPY); SelectObject(mdc, girl); BitBlt(hdc, xstart, ystart, 298, 329, mdc, 0, 0, SRCCOPY); }
最后在WndProc函数的WM_PAINT消息中添加MyPaint()函数
二、透明半透明效果
看到上面例子中效果还不太好,因为前景图还可以看到原来位图矩形边框。这里我们要把它做得更好。这里使用一个内存DC和位图对象,在DC中先完成透明效果,再来取出DC内存位图进行半透明处理。这样就达到了目的。
这个范例中的前景图必须要有如下图所示:
下面的编码和上面类似。
添加全局变量
HBITMAP bg, girl; HDC mdc; const int xstart = 50; const int ystart = 20;
在InitInstance()函数中,添加代码:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; HDC hdc, bufdc; HBITMAP bmp; BITMAP bm1, bm2; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } MoveWindow(hWnd, 10, 10, 600, 450, true); // 初始窗口位置和大小 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); bg = (HBITMAP)LoadImage(NULL, L"bg.bmp", IMAGE_BITMAP, 600, 450, LR_LOADFROMFILE); bmp = (HBITMAP)LoadImage(NULL, L"girlmask.bmp", IMAGE_BITMAP, 596, 329, LR_LOADFROMFILE); GetObject(bg, sizeof(BITMAP), &bm1); hdc = GetDC(hWnd); mdc = CreateCompatibleDC(hdc); bufdc = CreateCompatibleDC(hdc); girl = CreateCompatibleBitmap(hdc, 298, 329); SelectObject(mdc, girl); //前景图和背景图透明处理,保存在mdc SelectObject(bufdc, bg); BitBlt(mdc, 0, 0, 298, 329, bufdc, xstart, ystart, SRCCOPY); SelectObject(bufdc, bmp); BitBlt(mdc, 0, 0, 298, 329, bufdc, 298, 0, SRCAND); BitBlt(mdc, 0, 0, 298, 329, bufdc, 0, 0, SRCPAINT); //开始半透明处理 unsigned char *px1,*px2; //处理背景图 px1 = new unsigned char [bm1.bmHeight * bm1.bmWidthBytes]; GetBitmapBits(bg, bm1.bmHeight * bm1.bmWidthBytes, px1); //处理前景图 GetObject(girl, sizeof(BITMAP), &bm2); px2 = new unsigned char [bm2.bmHeight * bm2.bmWidthBytes]; GetBitmapBits(girl, bm2.bmHeight * bm2.bmWidthBytes, px2); int x, y, xend, yend; int i; int rgb_b; int PxBytes = bm1.bmBitsPixel / 8; xend = xstart + 298; yend = ystart + 329; //处理背景图 for (y = ystart; y < yend; y++) { for (x = xstart; x < xend; x++) { rgb_b = y * bm1.bmWidthBytes + x * PxBytes; px1[rgb_b] = px1[rgb_b] * 0.7; px1[rgb_b+1] = px1[rgb_b+1] * 0.7; px1[rgb_b+2] = px1[rgb_b+2] * 0.7; } } //处理前景图像素颜色 for(y=0;y<(bm2.bmHeight); y++) { for(x=0;x<bm2.bmWidth; x++) { rgb_b = y * bm2.bmWidthBytes + x * PxBytes ; i = (ystart+y) * bm1.bmWidthBytes + (xstart+x) * PxBytes; px2[rgb_b] = px2[rgb_b] *0.3 + px1[i]; px2[rgb_b+1] = px2[rgb_b+1] *0.3 + px1[i+1]; px2[rgb_b+2] = px2[rgb_b+2] *0.3 + px1[i+2]; } } SetBitmapBits(girl,bm2.bmHeight*bm2.bmWidthBytes,px2); MyPaint(hdc);//贴图 ReleaseDC(hWnd, hdc); DeleteDC(bufdc); DeleteObject(bmp); delete [] px1; delete [] px2; return TRUE; }
void MyPaint(HDC hdc) { //将背景图和处理后的透明半透明图贴入 SelectObject(mdc, bg); BitBlt(hdc, 0, 0, 600, 450, mdc, 0, 0, SRCCOPY); SelectObject(mdc, girl); BitBlt(hdc, xstart, ystart, 298, 329, mdc, 0, 0, SRCCOPY); }
最后把MyPaint()函数写入消息循环中的WM_PAINT消息中:
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... MyPaint(hdc); EndPaint(hWnd, &ps); break;
这样,我们的程序就写好了。运行程序。