绘制不规则位图方法总结,多种实现方法,全面测试比较
??????? 我說的這些方法都是在VC中的,VB中應該可以方便的用其它格式的圖片如gif,可以為透明,所以本文不針對VB讀者。這些問題都是我在CSDN中問而未果的問題,希望給與我有同樣迷惑的朋友一點幫助!
?????? 程序中的w和h為位圖的寬度和高度。
方法一:
?????? 首先把不規(guī)則圖形以外的地方(即要求是透明的地方),弄成圖形中不會出現(xiàn)的顏色(用圖像處理軟件),如白色,然后用下面的程序:
//包函#include "Wingdi.h"
//并在工程設(shè)置中的link中的對象/庫模塊中加入:msimg32.lib
CBitmap YourBmp;
YourBmp.LoadBitmap(IDB_XXXX); //要顯示的位圖
CDC* pDC=GetDC();
CDC YourDC;
YourDC.CreateCompatibleDC(pDC);
YourDC.SelectObject(&Yourbmp);
TransparentBlt(pDC->m_hDC,0,0,w,h,YourDC.m_hDC,0,0,w,h,
????RGB(255,255,255) //在位圖中視為透明的顏色的RGB值
?);
ReleaseDC(pDC);
評價:程序編制簡單,但運行速度慢,有閃爍(用一張208*15的位圖測試),所以還是不要圖方便!
方法二:
?????? 做一張蒙板位圖,大小與要繪制的位圖一樣,分辨率也一樣,讓蒙板對應于圖形區(qū)域的地方為純白色,其余地方(要求透明的地方)為純黑色。
CBitmap YourBmp;
YourBmp.LoadBitmap(IDB_XXXX); //要顯示的位圖
CBitmap YourMasker;
YourMasker.LoadBitmap(IDB_XXXX); //蒙板位圖
CBitmap background;
background.LoadBitmap(IDB_XXXX); //背景位圖
CDC* pDC=GetDC();
CDC YourDC;
YourDC.CreateCompatibleDC(pDC);
YourDC.SelectObject(&Yourbmp);
CDC MaskerDC;
MaskerDC.CreateCompatibleDC(pDC);
MaskerDC.SelectObject(&YourMasker);
CDC backgroundDC;
backgroundDC.CreateCompatibleDC(pDC);
backgroundDC.SelectObject(&background);
CBitmap TempBitmap; //臨時位圖
TempBitmap.CreateCompatibleBitmap(pDC,w,h);
CDC TempDC;
TempDC.CreateCompatibleDC(pDC);
TempDC.SelectObject(&TempBitmap);
TempDC.BitBlt(0,0,w,h,pDC,0,0,SRCCOPY);
TempDC.BitBlt(0,0,w,h,&YourDC,0,0,SRCAND);
TempDC.BitBlt(0,0,w,h,&MaskerDC,0,0,SRCPAINT);
TempDC.BitBlt(0,0,w,h,&YourDC,0,0,SRCAND);
pDC->BitBlt(0,0,w,h,&TempDC,0,0,SRCCOPY);
上面5行為核心行,基本算法就是先在臨時的TempDC中畫好位圖(透明區(qū)域已表現(xiàn)出來),再一次性的(最后一行)繪制到屏幕,這樣防止閃爍,但要多占CPU資源和內(nèi)存,在我的2.4G CPU上測試,CPU占有率也不到1%(每100毫秒運行一次上面的程序),可以接受。
ReleaseDC(pDC);
評價:程序復雜,但視覺效果較好!
方法三:
做一張蒙板位圖,大小與要繪制的位圖一樣,分辨率也一樣,讓蒙板對應于圖形區(qū)域的地方為純白色,其余地方(要求透明的地方)為純黑色。
CBitmap YourBmp;
YourBmp.LoadBitmap(IDB_XXXX); //要顯示的位圖
CBitmap YourMasker;
YourMasker.LoadBitmap(IDB_XXXX); //蒙板位圖
CDC* pDC=GetDC();
CDC YourDC;
YourDC.CreateCompatibleDC(pDC);
YourDC.SelectObject(&Yourbmp);
CDC MaskerDC;
MaskerDC.CreateCompatibleDC(pDC);
MaskerDC.SelectObject(&YourMasker);
pDC->BitBlt(0,0,w,h,&YourDC,0,0,SRCAND);
pDC->BitBlt(0,0,w,h,&MaskerDC,0,0,SRCPAINT);
pDC->BitBlt(0,0,w,h,&YoureDC,0,0,SRCAND);
ReleaseDC(pDC);
評價:該方法和方法二本質(zhì)上一樣,方法二先把要顯示的位圖存起來最后一次性顯示,方法三則分三步顯示位圖,所以屏幕上繪制的圖像要變化三次才能達到透明的效果,所以還是稍有閃爍,但比方法一好得多,在資源的消耗上比方法三要少,處于折衷位置!推薦使用方法三,它在效率和視覺上都表現(xiàn)的很好,而且window在移動鼠標的時候也是用這種方法,沒有任何理由比微軟都是這樣用的這個理由更充分,畢竟大家都在用他的操作系統(tǒng)!
方法四:
????? ?這個立法我沒有經(jīng)過實驗,因為沒有找到ICO制作工具,方法就是把要顯示的位圖做成ICO,自定義ICO應該可以自定義大小吧,不太確定,
大家都知道ICO可以透明,想哪兒透明就哪兒透明,然后就DrawIcon或DrawImage函數(shù)把它繪制上屏幕,如果這個方法可行,那就是最簡單的
了,只要一行程序!
方法五:
?????? 用CreateHatchBrush函數(shù)把要顯示的位圖做成刷子,再把要顯示的位圖輪廓做成一個區(qū)域(要做區(qū)域,可以根據(jù)位圖上的顏色輕松做出來,前提是先把要求為透明的地方弄成位圖中不會出現(xiàn)的顏色),再用FillRgn函數(shù)用做成的刷子去刷這個區(qū)域,最后就達到效果了。該方法應該要求區(qū)域是連續(xù)的,而且左上角第一點不能為透明,所以應該沒有什么實用價值!但我想可以用BitBlt函數(shù)來改善這種狀況。
方法六:
??????? 用AlphaBlend函數(shù),以下程序只有在Vc++.net下才能通過,因為使用了透明位圖的概念,而2000以前的系統(tǒng)是不理解透明位圖的。
//包函#include "Wingdi.h"
CBitmap YourBmp;
YourBmp.LoadBitmap(IDB_XXXX); //要顯示的位圖
CDC* pDC=GetDC();
CDC YourDC;
YourDC.CreateCompatibleDC(pDC);
YourDC.SelectObject(&YourBmp);
BLENDFUNCTION b;
b.BlendOp=AC_SRC_OVER; //定值
b.BlendFlags=0; //定值
b.SourceConstantAlpha=255; //不用總體透明度,用每一象素點自己的alpha值
b.AlphaFormat=AC_SRC_ALPHA;
//vc++6.0中沒有AC_SRC_ALPHA的定義,只有AC_SRC_NO_ALPHA的定義,這正是在vc++6.0中無法使用的原因。
AlphaBlend(pDC->m_hDC,0,0,w,h,GateDC.m_hDC,0,0,w,h,b);
ReleaseDC(pDC);
評價:方法簡單,但要求2000以上系統(tǒng),而且事先必需要做個32位位圖并給plpha賦適當?shù)闹?#xff08;以下會討論),目前我還沒有找到一個做32位位圖的軟件,所以還只有用程序來實現(xiàn),好在我實現(xiàn)了這樣一個函數(shù)!
做32位透明位圖的方法!
??????? 該函數(shù)只能把24位轉(zhuǎn)成32位,大家可以改動一下就能轉(zhuǎn)換其它位的位圖了,調(diào)用函數(shù)前,請把需要透明的地方弄成位圖中不會出現(xiàn)的
顏色,假設(shè)為白色。
??? 注:該函數(shù)是寫給我自己用的,沒有作任何錯誤判斷,假設(shè)我不會犯錯誤,如果要想更完美,可以自己加上錯誤的捕捉。
?BITMAPFILEHEADER bf;
?BITMAPINFOHEADER bi;
?CFileDialog dlg
(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"bmp 文件(*.bmp)|*.bmp||");
?CString filename;
?CFile file;
?if(IDOK==dlg.DoModal())
?{
??filename=dlg.GetPathName();
??file.Open(filename,CFile::modeRead | CFile::shareDenyNone); //打開要轉(zhuǎn)換的位圖文件
??file.Read(&bf,sizeof(bf));
??file.Read(&bi,sizeof(bi));
?}
?///
?UpdateData(); //取得edit框中的輸入,edit框與CString變量name相連,該輸入作為轉(zhuǎn)換后的輸出文件的文件名
?CFile Output;
?Output.Open(name,CFile::modeCreate | CFile::modeWrite,NULL);
?DWORD FImageSize=bi.biSizeImage; //轉(zhuǎn)換前的圖像數(shù)據(jù)大小
?DWORD SImageSize=bi.biWidth*bi.biHeight*4; //轉(zhuǎn)換后的圖像數(shù)據(jù)大小
?int SrowByte=bi.biWidth*3;
?if(SrowByte%4)
??SrowByte+=4-SrowByte%4;
?//以上求轉(zhuǎn)換前一行圖像需要的字節(jié)數(shù),注意必需是4的倍數(shù),簡單來說就是高度相同,208寬和207寬的24位位圖的文件大小相等。
?//也可以這樣求:SrowByte=bi.biSizeImage/bi.biHeight;
?//注意下面求32位位圖一行的字節(jié)數(shù)時,并沒有判斷是否是4的倍數(shù),而直接寫成bi.biWidth*4,原因很簡單,32位位圖每像素占
?//4位,所以一行占用的字節(jié)數(shù)無論如何都將是4的倍數(shù)。
?bf.bfSize=bf.bfOffBits+SImageSize;
?Output.Write(&bf,sizeof(bf)); //給輸出文件寫頭信息
?bi.biSizeImage=SImageSize;
?bi.biBitCount=32;
?Output.Write(&bi,sizeof(bi)); //給輸出文件寫頭信息
?bi.biSizeImage=FImageSize; //恢復以前的bi數(shù)據(jù)值
?bi.biBitCount=24; //恢復以前的bi數(shù)據(jù)值
?///
?char* Sbuff=new char[SrowByte]; //用于保存源位圖一行的信息
?char* Dbuff=new char[bi.biWidth*4]; //用于保存目的位圖一行的信息
?for(int j=0;j<bi.biHeight;j++)
?{
??file.Read(Sbuff,SrowByte);
??for(int i=0;i<bi.biWidth;i++)
??{
???for(int k=0,n=0;k<3;k++) //i每循環(huán)一次,處理一個象素點,一象素占3字節(jié)。
????if((char)255==(Dbuff[4*i+k]=Sbuff[3*i+k])) //拷貝圖像信息,并記錄達到最大值的通道數(shù)
?????n++; //達最大值255的通道數(shù)加1
???if(3==n) //如果紅、綠、藍通道均達到最大,說明該點為透明
????Dbuff[4*i+3]=Dbuff[4*i+2]=Dbuff[4*i+1]=Dbuff[4*i]=(char)0;
????//把該點的紅、綠、藍、alpha通道賦0,表示完全透明
???else //該點不透明,把alpha賦255,表示完全不透明
????Dbuff[4*i+3]=(char)255;
??}
??Output.Write(Dbuff,bi.biWidth*4); //將轉(zhuǎn)換后的數(shù)據(jù)輸出到文件中。
?}
?delete[] Sbuff;
?delete[] Dbuff;
?file.Close();
?Output.Close();
?///
?AfxMessageBox("轉(zhuǎn)換完成!");
用這個函數(shù)做成的32位位圖,AlphaBlend函數(shù)完全理解,運行正常!
總結(jié)
以上是生活随笔為你收集整理的绘制不规则位图方法总结,多种实现方法,全面测试比较的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云炬Android开发笔记 2-2 A
- 下一篇: 透明窗体的又一实现