Visual C++游戏编程基础之透明半透明效果

一、基本思路

1.半透明的原理

   一张位图由许多像素组成,而每一像素包含R、G、B三原色,三原色的值决定了像素的色彩,要实现半透明效果,需要把前景图    和背景图彼此对应的像素颜色按某一比例进行调配,称为 ‘不透明度’,直接贴图,则前景图不透明度为100%,背景图为0%;

   公式:半透明图色彩=前景图色彩 * 不透明度 + 背景图色彩 * (1-不透明度)

2.半透明操作步骤

(1)取得位图结构,需要使用GetObject函数,介绍如下:

    函数原型:int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOID lpvObject);

    函数功能:表示获取位图的信息

    参数一   : 要获取的位图句柄

    参数二   : 指定将要写到缓冲区的信息的字节数目,在这里是位图类型的字节数 

    参数三   : 在这里是指向位图结构变量的地址,该变量当作缓冲区

    关于位图结构BITMAP介绍如下:

    

(2)建立暂存数组,目的是为了存储位图中所有像素的颜色值

    unsigned char * px1 = new unsigned char [bm1.bmHeight * bm1.bmWidthBytes];

    其中数组的大小即为整个位图所有像素占的字节数,若一个像素24bits,则占3个字节,也就是3个数组元素

(3)取得位图中所有的颜色值,并存储到暂存数组中,函数介绍如下:

    函数原型:LONG GetBitmapBits(HBITMAP hbmp, LONG cbBuffer, LPVOID lpvBits);

    函数功能:把位图的色彩值存储到暂存数组中

    参数一   :位图句柄

    参数二   :要取得的字节数

    参数三   :指向暂存数组

(4)合成像素颜色值,这里介绍基本思想:

     假设每个像素24bit,则每个像素占3字节,关于背景图,首先要取得整张图像素各原色值所在的索引值,即:

     

	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;		        //PxBytes为每个像素所占的字节数
			px1[rgb_b+1] = px1[rgb_b+1] * 0.7;	        //bmWidthBytes为每一列像素占的字节数
			px1[rgb_b+2] = px1[rgb_b+2] * 0.7;	
		}
	}

  举个简单的例子,假设从(xstart,ystary)~(xend,yend) 为 (0,0)~(1,1),则有如下情况:

                                                                                      [0][1][2]   [3][4][5]

                                                                                      [6][7][8]   [9][10][11]

注释:每个像素占3个字节;每一列像素占6字节,书里都把行称为列,也不知道为啥 

同理:处理前景图时,直接进行颜色合成:前景图30%的不透明度+背景图70%的不透明度,存放到px2

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];
		}
	}

(5)重设位图颜色,根据暂存数组中的内容重设位图颜色

    函数原型:LONG SetBitmapBits(HBITMAP hmbp, DWORD cBytes, CONST VOID (lpBits));

    函数功能:重设位图颜色

    参数一    :要设置的位图句柄

    参数二    :暂存数组的字节数

    参数三    :指向暂存数组

二、结合前面所学的实现透明半透明效果,代码如下:


#include "stdafx.h"
#include <stdio.h>

HINSTANCE hInst;
HBITMAP bg,girl;
HDC		mdc;

const int xstart = 222;
const int ystart = 50;


ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
void				MyPaint(HDC hdc);


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	MSG msg;

	MyRegisterClass(hInstance);

	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	return msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "canvas";
	wcex.hIconSm		= NULL;

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HWND hWnd;
	HDC hdc,bufdc;
	HBITMAP bmp;
	BITMAP bm1,bm2;

	hInst = hInstance;

	hWnd = CreateWindow("canvas", "绘图窗口" , WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

	if (!hWnd)
	{
		return FALSE;
	}

	MoveWindow(hWnd,10,10,600,445,true);
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	bg = (HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,600,400,LR_LOADFROMFILE); //背景图句柄
	bmp = (HBITMAP)LoadImage(NULL,"girlmask.bmp",IMAGE_BITMAP,766,345,LR_LOADFROMFILE);//前景图句柄

	GetObject(bg,sizeof(BITMAP),&bm1);	  //get the message of image to bm1
	
	if(bm1.bmBitsPixel != 32 && bm1.bmBitsPixel != 24) //bit of pixe     l
	{
		MessageBox(NULL,"此程序只能在32 bit 或 24 bit 显示模式中运行","警告",0);
		return FALSE;
	}

	hdc = GetDC(hWnd);
	mdc = CreateCompatibleDC(hdc);//建立与窗口DC兼容的内存DC "mdc"
	bufdc = CreateCompatibleDC(hdc);//建立与窗口DC兼容的内存DC "bufdc",选择前景图或背景图,贴到mdc作透明处理
	girl = CreateCompatibleBitmap(hdc,383,345);//建立一个与窗口相兼容的空位图"girl",298*329像素,处理后就是要贴到窗口的透明半透明图案

	SelectObject(mdc,girl);//girl 存到 mdc,在mdc上进行透明处理


	SelectObject(bufdc,bg);//bg replace bufdc,背景图bg存入bufdc
	BitBlt(mdc,0,0,383,345,bufdc,xstart,ystart,SRCCOPY);//将透明区域的背景图贴到mdc中
	SelectObject(bufdc,bmp);//将包含屏蔽图的前景图存入bufdc
	BitBlt(mdc,0,0,383,345,bufdc,383,0,SRCAND);//屏蔽图在整张图中,左上角坐标(298,0),屏蔽图与背景图and运算
	BitBlt(mdc,0,0,383,345,bufdc,0,0,SRCPAINT);//前景图与背景图or运算,得到透明后的前景图,接下来进行半透明处理

	unsigned char *px1,*px2;


	px1 = new unsigned char [bm1.bmHeight * bm1.bmWidthBytes];
	GetBitmapBits(bg,bm1.bmHeight * bm1.bmWidthBytes,px1);//px1储存位图颜色值


	GetObject(girl,sizeof(BITMAP),&bm2);//bme得到girl的全部信息
	px2 = new unsigned char [bm2.bmHeight * bm2.bmWidthBytes];
	GetBitmapBits(girl,bm2.bmHeight * bm2.bmWidthBytes,px2);//px2储存位图bm2的颜色值

	int x,y,xend,yend;
	int i;
	int rgb_b;
	int PxBytes = bm1.bmBitsPixel / 8 ;

	xend = xstart + 383;
	yend = ystart + 345;

	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);//bg replace mdc,bg存入到mdc中
	BitBlt(hdc,0,0,600,400,mdc,0,0,SRCCOPY);//将mdc贴到hdc

	SelectObject(mdc,girl);
	BitBlt(hdc,xstart,ystart,383,345,mdc,0,0,SRCCOPY);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
		case WM_PAINT:						
			hdc = BeginPaint(hWnd, &ps);
			MyPaint(hdc);
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:				
			DeleteDC(mdc);
			DeleteObject(bg);
			DeleteObject(girl);
			PostQuitMessage(0);
			break;
		default:						
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

四、效果

猜你喜欢

转载自blog.csdn.net/Sruggle/article/details/90647084
今日推荐