最近在WINCE上面搭建MFC应用,本来一般的图形显示还好,但是当我想实现BMP的边框透明效果的时候却不行了。因为在WINDOWS上是有TransparentBlt函数可以直接实现这一功能,但是在WINCE6.0上确是不可以的,而PNG图片则是需要挂上DLL或者C#托管代码,可惜我的硬件不是自己公司做的,是别的垃圾公司做的一个WINCE空壳,也就包括了硬件与基本驱动,其他什么都没有,连源码也是没有的,那意味着连基本内核修改我都是做不到的。由于本人完全没有学过WINCE,MFC碰到的情况也很少,所以从接手这个屏到现在过了三个多星期,实现的功能有限,包括是控件编写、串口通讯、打开文件处理,然后这些都还好,难的还是WINDOW上面那讨厌的刷新抖动,为了尽量让界面显示正常,用了很多办法,其中就包括了显示图片背景这块。
从目前的情况看我只能显示BMP,但是如果不显示出透明效果的话,界面会非常难看,毕竟当背景也是图片的时候,那些边边角角太难看了,特别是当有多页面的时候,图片不想经常用ps改来改去,于是上网找了下TransparenBlt的实现原理从而实现这个功能。
首先看下现有资源
要显示的图片
背景图片
//首先需要的参数有下面几个
//显示dc
CClientDC pDc(this);
//刷新区域大小
CRect rc;
//分别是要显示 的图片DC,位掩DC,以及显示缓冲DC.
CDC imageDc,maskDc,dcMem;
//背景填充图片DC
CDC bgDc;
//要显示 的图片,内存缓冲,位掩缓冲
CBitmap bmp,memBitmap,maskBmp,bgBmp;
//透明背景的颜色,这个颜色后面显示的时候会给忽略
COLORREF TransParentColor;
//获取刷新区域
GetClientRect(rc);
ClientToScreen(rc);
//读取要显示的两个图片
bmp.LoadBitmap(IDB_BITMAP1);
bgBmp.LoadBitmap(IDB_BITMAP_BG);
//创建一个单色图片来实现空心区域选择的位掩
maskBmp.CreateBitmap(rc.Width(),rc.Height(), 1,1,NULL);
//创建一个跟当前设备相关的显示缓冲,为了保证显示正常,它的指针必须是显示的dc.
memBitmap.CreateCompatibleBitmap(&pDc, rc.Width(),rc.Height());
//创建必要dc
imageDc.CreateCompatibleDC(NULL);
maskDc.CreateCompatibleDC(NULL);
dcMem.CreateCompatibleDC(NULL);
bgDc.CreateCompatibleDC(NULL);
//为dc选择缓冲
dcMem.SelectObject(&memBitmap);
imageDc.SelectObject(&bmp);
maskDc.SelectObject(&maskBmp);
bgDc.SelectObject(&bgBmp);
开始真正的功能部分
//首先根据图片来获取有效显示区域
//透明色
maskDc.SetBkColor(RGB(0,0,0));
imageDc.SetBkColor(TransParentColor);
//采用SRCINVERT模式填充能够实现将有效区域与无效区域区分开来, //因为maskDc是单色的,只有白色与黑色两种,而图片dc背景设成透明色后
//会将透明色当成无效色,而其他颜色都是有效色
maskDc.BitBlt(0,0,rc.Width(),rc.Height(),&imageDc,0,0,SRCINVERT);
// 这时候maskDc里面的图形是这样的,其中有效区域成了黑色,无效区域成了白色
//这是因为本身maskDC里面是单色的,它的所有点数据只会是黑色(RGV(0,0,0)或者白色(RGV(255,255,255),而在imageDc里面设置了透明色之后,根据SRCINVERT的用法,透明色区域都会变成白色(有效),有效色的地方反而成了黑色
//在显示缓冲中利用位掩空洞来实现填充 //这里有一个问题,有个会问为什么要多创建一个dc缓冲而不直接刷到imageDc里面去呢? //其实在网上其实相关例程也会这么处理,也就是直接刷到图片缓冲上去来实现这个效果, //但是实际上我在WINCE这么做是不行的,它无法把透明色刷掉, //不知道是不是因为BitBlth函数与WINDOWS上有区别还是CDC有区别, //所以这里只能用创建一个显示缓冲来实现 dcMem.BitBlt(0,0,rc.Width(),rc.Height(),&imageDc,0,0,SRCCOPY); dcMem.SetBkColor(RGB(0,0,255)); dcMem.BitBlt(0,0,rc.Width(),rc.Height(),&maskDc,0,0,SRCINVERT); //这样就能得到下图,要透明的部分已经给虑掉了 //这里有个技巧,如果你的图片背景本来就是黑的,那么上面的dc缓冲那步是可以不要的 //(我猜的,因为这时候并不需要将透明部分变黑)。
//用背景图片填充pDc.BitBlt(0,0,rc.Width(),rc.Height(), m_dcBk,rc1.left,rc1.top,SRCCOPY);//利用位掩吧要显示的bmp之外的背景都填充成背景图片,注意颜色,如果设定成背景黑、字色白,那么填充区域就反了,因为我们的位掩有效区域应该是黑色部分,这部分不应该填充的。 pDc.SetBkColor(RGB(255,255,255));pDc.SetTextColor(RGB(0,0,0));pDc.BitBlt(0,0,rc.Width(),rc.Height(), &maskDc,0,0,SRCAND);//这时候边框已经填充完毕了
/最后将要显示的图片缓冲填充过去,完成
pDc.BitBlt(0,0,rc.Width(),rc.Height(), &dcMem,0,0,SRCPAINT);
参考资料:
http://hi.baidu.com/liuhuaxi2009/item/b22dec10de7b0e9b99ce33c0
这应该是我找的最靠谱的了,原理说的很明白,要不我还真弄不出来,不过里面的代码不适合WINCE,原因就是出现了上面的问题。其他那些只会瞎扯跟抄的,我只能默默对那群人竖起中指---真心是在害人啊。。。
注意,这代码是完成抽出来的做示例的,没有编译调试,但是如果用自己的代码给你们,反而会让你们看不懂,因此才整理了下,加了很多注释,在我的WINCE MFC是能正常显示的,但是有一个问题,就是抖动,现在在着手把抖动去掉。