(半) 透明位图原理及代码

来源:互联网 发布:红楼梦知乎 编辑:程序博客网 时间:2024/05/16 18:03

<!-- /* Font Definitions */ @font-face{font-family:宋体;panose-1:2 1 6 0 3 1 1 1 1 1;mso-font-alt:SimSun;mso-font-charset:134;mso-generic-font-family:auto;mso-font-pitch:variable;mso-font-signature:3 135135232 16 0 262145 0;}@font-face{font-family:Tahoma;panose-1:2 11 6 4 3 5 4 4 2 4;mso-font-charset:0;mso-generic-font-family:swiss;mso-font-pitch:variable;mso-font-signature:1627421319 -2147483648 8 0 66047 0;}@font-face{font-family:"/@宋体";panose-1:2 1 6 0 3 1 1 1 1 1;mso-font-charset:134;mso-generic-font-family:auto;mso-font-pitch:variable;mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal{mso-style-parent:"";margin:0cm;margin-bottom:.0001pt;text-align:justify;text-justify:inter-ideograph;mso-pagination:none;font-size:10.5pt;mso-bidi-font-size:12.0pt;font-family:"Times New Roman";mso-fareast-font-family:宋体;mso-font-kerning:1.0pt;} /* Page Definitions */ @page{mso-page-border-surround-header:no;mso-page-border-surround-footer:no;}@page Section1{size:595.3pt 841.9pt;margin:72.0pt 90.0pt 72.0pt 90.0pt;mso-header-margin:42.55pt;mso-footer-margin:49.6pt;mso-paper-source:0;layout-grid:15.6pt;}div.Section1{page:Section1;}-->

(半) 透明位图原理及代码

摘自<<http://www.bc99.cn/Article/ASPX/2007-3-27-cs/65D5KI8EJFGK.html>>

 绘制半透明位图

 有的时侯,我们希望显示一幅半透明的位图。也就是说我们将一幅位图B
显示到A位图上,又希望透过B位图看到A位图的一部分图像但不是全部。比如A
图是一幅曲线图,B是一幅提示位图,我们想在显示提示的同时看到已显示的曲
线,但不需要曲线的背景,就需有用到半透明位图。曲线看上去就象从B位图中渗
透过来,其实半透明技术就是一种渗透技术,渗透公式我们可选用多种,在这里
我们选用(A AND 0x7FOR B。注意,白色不能产生渗透。

//
参数说明:
//hDIB -
位图句柄
//pPal -
位图调色板
//xDest -
显示位图的左上角x坐标
//yDest -
显示位图的左上角y坐标
void DrawSemiTransparentBitmap(CDC *pDC, int nXDest, int nYDest, HGLOBAL hDIB,CPalette *pPal)
BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
int nColors = bmInfo.bmiHeader.biClrUsed ? bmInfo.bmiHeader.biClrUsed :
1 << bmInfo.bmiHeader.biBitCount;
int nWidth = bmInfo.bmiHeader.biWidth;
int nHeight = bmInfo.bmiHeader.biHeight;

LPVOID lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
CDC memDC;
memDC.CreateCompatibleDC( pDC );
CBitmap bmp;
bmp.CreateCompatibleBitmap( pDC, nWidth, nHeight );
CBitmap *pOldBitmap = memDC.SelectObject( &bmp );
if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE&&nColors<256)
CPalette *pOldMemPalette = memDC.SelectPalette(pPal, FALSE);
memDC.RealizePalette();
::SetDIBitsToDevice(memDC.m_hDC, 0, 0, nWidth, nHeight, 0, 0, 0, nHeight, lpDIBBits, (LPBITMAPINFO)hDIB, DIB_RGB_COLORS);

CDC maskDC;
CBitmap mbm;
maskDC.CreateCompatibleDC(pDC);
mbm.CreateCompatibleBitmap(pDC, nWidth, nHeight);
maskDC.SelectObject(&mbm);
maskDC.FillSolidRect(CRect(0, 0, nWidth, nHeight), RGB(0x7F, 0x7F, 0x7F));
pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &maskDC, 0, 0, SRCAND);
pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &memDC, 0, 0, SRCPAINT);
memDC.SelectObject(pOldBitmap);
}
 

如何画透明位图

    
在丰富多彩的软件世界中,位图的处理技术尤为重要。透明位图的显示作为
一种常用的图像处理方法,被用在众多的软件中。其基本原理,也就是将一幅需
要透明显示的位图(其透明色为已知),制作出二幅需要的位图AB,其中A为除
透明色外均填充为黑色,B为把透明色填充为黑色其余色不变,再用指定光栅操作
将两幅位图合并,可形成透明位图。
创建过程如下:
1
、使用透明色背景,将位图拷贝到内存屏蔽位图中。
2
、利用与白色相与不变色,与黑色相与成黑色的原理,将内存位图的的背景设置

成黑色、前景设置成白色,并将屏蔽位图用与操作拷贝到此内存位图中。形成位图B
3
、将显示设备背景设置为白色、前景设置成黑色,并将屏蔽位图用与操作拷贝到
显示设备中。形成位图A
4
、将内存位图用或操作拷贝到显示设备中。最终形成透明位图。

//
参数说明:
//hDIB -
位图句柄
//pPal -
位图调色板
//xDest -
显示位图的左上角x坐标
//yDest -
显示位图的左上角y坐标
//colorTransparent -
透明色

void DrawTransparentBitmap( CDC *pDC, int nXDest, int nYDest,HGLOBAL hDIB, COLORREF colorTransparent, CPalette *pPal)

{

BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
int nColors = bmInfo.bmiHeader.biClrUsed ? bmInfo.bmiHeader.biClrUsed :1<
int nWidth = bmInfo.bmiHeader.biWidth;
int nHeight = bmInfo.bmiHeader.biHeight;
LPVOID lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
CDC memDC,maskDC;
memDC.CreateCompatibleDC( pDC );
CBitmap bmp;
bmp.CreateCompatibleBitmap( pDC, nWidth, nHeight );
CBitmap *pOldBitmap = memDC.SelectObject( &bmp );

if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE&&nColors<256)
CPalette *pOldMemPalette = memDC.SelectPalette(pPal, FALSE);
memDC.RealizePalette();
::SetDIBitsToDevice(memDC.m_hDC, 0, 0, nWidth, nHeight, 0, 0, 0,
nHeight, lpDIBBits, (LPBITMAPINFO)hDIB, DIB_RGB_COLORS);
maskDC.CreateCompatibleDC(pDC);
CBitmap maskBitmap;
maskBitmap.CreateBitmap( nWidth, nHeight, 1, 1, NULL );
maskDC.SelectObject( &maskBitmap );
memDC.SetBkColor( colorTransparent );

maskDC.BitBlt( 0, 0, nWidth, nHeight, &memDC, 0, 0, SRCCOPY );
memDC.SetBkColor(RGB(0,0,0));
memDC.SetTextColor(RGB(255,255,255));
memDC.BitBlt(0, 0, nWidth, nHeight, &maskDC, 0, 0, SRCAND);
pDC->SetBkColor(RGB(255,255,255));
pDC->SetTextColor(RGB(0,0,0));
pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &maskDC, 0, 0, SRCAND);
pDC->BitBlt(nXDest, nYDest, nWidth, nHeight, &memDC,0, 0, SRCPAINT);
memDC.SelectObject( pOldBitmap );
}


画透明位图通常的方法是使用遮罩。所谓遮罩就是一张黑白双色的位图,他和
要透明的位图是对应的,遮罩描述了位图中需要透明的部分,透明的部分是黑色的,
而不透明的是白色的,白色的部分就是透明的部分。
假设图A是要画的透明位图,图B是遮罩,图A上是一个大写字母A,字母是红色的,背
景是黑色的,图B背景是白色的,上面有一个黑色的字母A和图A的形状是一样的。
比如我们要在一张蓝天白云的背景上透明地画图A,就是只把红色的字母A画上去。我

们可以先将图B和背景进行与操作,再把图B和背景进行或操作就可以了。
VC++ MFC实现的代码如下:
void CDemoDlg::OnPaint()
CPaintDC dc(this);
Cbitmap BmpBack,BmpA,BmpB,*pOldBack,*pOldA,*pOldB;
BmpBack.LoadBitmap(IDB_BACKGROUND); // 
载入背景图
BmpA.LoadBitmap(IDB_BITMAPA); //
载入图A
BmpB.LoadBitmap(IDB_BITMAPB); //
载入图B
CDC dcBack,dcA,dcB; //
声明三个内存DC用于画图
dcBack.CreateCompatibleDC(&dc);
dcA.CreateCompatibleDC(&dc);
dcB.CreateCompatibleDC(&dc); //
把这三个内存DC创建成和PaintDC兼容的DC

pOldBack=dcBack.SelectObject(&BmpBack);
pOldA=dcA.SelectObject(&BmpA);
pOldB=dcB.SelectObject(&BmpB); //
把三个位图选入相应的DC
dc.BitBlt(0,0,100,100,&dcBack,0,0,SRCCOPY); //
画背景
dc.BitBlt(0,0,48,48,&dcB,0,0,SRCAND); //
用与的方式画遮罩图B
dc.BitBlt(0,0,48,48,&dcA,0,0,SRCPAINT); //
用或的方式画遮图A
dcBack.SelectObject(pOldBack);
dcBack.SelectObject(pOldA);
dcBack.SelectObject(pOldB); //
从内存DC中删除位图
你会看到红色的字母A透明地画在背景上了。

用遮罩的方法必须事先做好遮罩,遮罩和位图大小一样等于多消耗一倍的资源,
比较浪费。还有一种画透明位图的方法,基本原理是一样的,只是不用事先做好
遮罩,根据需要动态生成遮罩,但是要求需要透明的位图必须指定一种透明色,
凡是这个透明色的地方则画成透明的。
VC++ MFC实现的代码如下:
/*
这是一个用来画透明位图的函数
CDC *pDC 
需要画位图的CDC指针
UINT IDImage 
位图资源ID
Crect &rect 
指定位图在pDC中的位置
COLORREF rgbMask 
位图的透明色

*/
void DrawTransparentBitmap(CDC *pDC, UINT IDImage,Crect &rect, COLORREF rgbMask)
{

CDC ImageDC,MaskDC;
Cbitmap Image,*pOldImage;
Cbitmap maskBitmap,*pOldMaskDCBitmap ;
Image.LoadBitmap(IDImage);
ImageDC.CreateCompatibleDC(pDC);
pOldImage=ImageDC.SelectObject(&Image);
MaskDC.CreateCompatibleDC(pDC);
maskBitmap.CreateBitmap( rect.Width(), rect.Height(), 1, 1, NULL );
pOldMaskDCBitmap = MaskDC.SelectObject( &maskBitmap );
ImageDC.SetBkColor(rgbMask);
MaskDC.BitBlt( 0, 0, rect.Width(), rect.Height(), &ImageDC, 0, 0, SRCCOPY );

ImageDC.SetBkColor(RGB(0,0,0));
ImageDC.SetTextColor(RGB(255,255,255));
ImageDC.BitBlt(0, 0, rect.Width(), rect.Height(), &MaskDC, 0, 0, SRCAND);
pDC->BitBlt(rect.left,rect.top,rect.Width(), rect.Height(), &MaskDC, 0, 0, SRCAND);
pDC->BitBlt(rect.left,rect.top,rect.Width(), rect.Height(), &ImageDC, 0, 0,SRCPAINT);
MaskDC.SelectObject(pOldMaskDCBitmap);
ImageDC.SelectObject(pOldImage);

}

void CDemoDlg::OnPaint()

{

CPaintDC dc(this);
Cbitmap BmpBack,*pOldBack,;
BmpBack.LoadBitmap(IDB_BACKGROUND);

CDC dcBack;
dcBack.CreateCompatibleDC(&dc);
pOldBack=dcBack.SelectObject(&BmpBack);
dc.BitBlt(0,0,100,100,&dcBack,0,0,SRCCOPY);
DrawTransparentBitmap(&dc,IDB_BITMAPA,Crect(0,0,48,48),RGB(192,192,0));

dcBack.SelectObject(pOldBack);

}

//XOR 方式

    首先是异或运算:
      
异或的运算方法是一个二进制运算:
      
异或的运算方法是一个二进制运算:
              1^1=0
               0^0=0
               1^0=1
                0^1=1

           
两者相等为0,不等为1.

           
这样我们发现交换两个整数的值时可以不用第三个参数。
           
a=11,b=9.以下是二进制
            a=a^b=1011^1001=0010;
            b=b^a=1001^0010=1011;
            a=a^b=0010^1011=1001;
           
这样一来a=9,b=11了。

   
再就是sgyxslsc先生的这篇文章(原文出处:http://sgyxslsc.spaces.live.com/blog/
   

    异 或运算有这么个特点:用一个数B与另一个数A进行两次异或运算,那么A的值保持不变。可以设想一下,在显示位图的时候,图片中的位与欲显示的位图的区域进行两次异或操作时,欲显示的位图的区域不会产生任何变化。也就是说,如果将图片中的背景色与欲显示的位图的区域进行两次异或运算,那么背景色就被过滤掉 了,但同样,位图中非背景的内容也被过滤掉了。

    现在我们这样,第一次异或运算之后,加这样一个操作。把位图中的主要内容与0作与运算,而背景区域与1作与运算。这样位图的主要内容区变为都变为0,而背 景区域还是原图像与窗口背景的异或值。这样我们再进行第二次异或操作,位图的主区域与0异或保持不变,而背景区域被消除了。

    而执行这个操作需要我们建立一个面具图片,这个图片只有两种颜色:黑和白。黑色的RGB值全部是0组成,白色的RGB值肯定是全部由1组成。而这张图片的特点就是 主要内容的值都是0(黑),背景色都是1(白)。在两次异或的中间用这个图片以与的方式在显示区域显示就可以了。

    BitBlet函数中,异或对应于“SRCINVERT”对应于“SRCCOPY”。下面是书中的程序:

    voidCTransBitmapDlg::OnPaint()

    {

       CDC *PDC = GetDC();

       CDC dcImage, dcTrans;

       

       //装入欲显示的位图

       CBitmap bitmap;

       bitmap.LoadBitmap(IDB_BITMAPID);

       //取得位图的详细信息

       BITMAP bm;

       bitmap.GetBitmap(&bm);

       int nWidth = bm.bmWidth;

       int nHeight = bm.bmHeight;

       //建立与设备环境相关的位图,dcImage中用来装入欲显示的位图

       //dcTrans中用来装入过滤背景色的位图

       dcImage.CreateCompatibleDC(pDC);

       dcTrans.CreateCompatibleDC(pDC);

       CBitmap *pOldBitmapImage = dcImage.SelectObject(&bitmap);

       // 建立与欲显示的位图等大的单色位图,用来过滤背景色

       CBitmap bitmapTrans;

       // “面具就在这步做好

       bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL);

       CBitmap *pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);

       //强行将欲显示的位图的背景色设置为白色,以方便建立dcTrans, 这步很重要。如果你的

       //位图的背景色不是白色的,你只需要修改这一步的参数。

       dcImage.SetBkColor(RGB(255, 255, 255));

       dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY);

       //dcTrans中,背景色变为白色,而主要图像的各种色彩都被设置成黑色

       //显示面具,这一步主要目的的主要目的在于让你更能够明白面具的真面目

       pDC->BitBlt(0, bm.bmHeight, nWidth, nHeight, &dcTrans, 0, 0, SRCCOPY);

       pDC->BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT);

       //该步骤将源位图与目标区域进行异或运算

       pDC->BitBlt(0, 0, nWidth, nHeight, &dcTrans, 0, 0, SRCAND);

       //通过dcTrans与目标区域进行操作,建立了与主要图像轮廓一致的黑框以保护图片的主要内容不会改变

       pDC->BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT);

       //用黑框位图与源位图进行黑色异或运算,将源码位图中有色彩(除背景色以外)

       //的内容还原。在这步,背景色经过与目标区域进行了两次异或运算,背景色已经被过滤

}

    }

   其中CreateBitmap不明白,怎么利用它就创建出白底黑面的位图了,又看了疯狐先生的文章,一切都明白了:
   

函数功能:该函数创建一个带有特定宽度、高度和颜色格式的位图。

函数原型:HBITMAP CreateBitmap(int nWidth, int nHeight, UINT cPlanes, UINTcBitsPerPel, CONST VOID *lpvBits);

参数:

nWidth:指定位图宽度,单位为像素。

nHeight:指定位图高度,单位为像素。

cPlanes:指定该设备使用的颜色位面数目。

cBitsPerPel:指定用来区分单个像素点颜色的位数(比特数目)。

lpvBits:指向颜色数据数组指针。这些颜色数据用来置矩形区域内像素的颜色。矩形区域中的每一扫描线必须是双字节整数倍(不足部分以0填充)。如果该参数为NULL,那么就表示没有定义新位图。

备注:在创建完位图后,可以通过使用SelectObject函数把它选入到设备环境中。尽管函数CreateBitmap可以用来创建彩色位图,但由于性能方面的原因,应用程序使用CreateBitmap函数来创建单色位图,创建彩色位图应该使用函数 CreateCompatibleBitmap。当由CreateBitmap创建而返回的彩色位图被选入到设备环境时,系统必须确保选入进去的设备环境格式与位图匹配。由于函数CreateComapatibleBitmap获取设备环境,所以它返回的位图与指定的设备环境有相同的格式。所以对 Select的后续调用都要比从CreateBitmap函数创建返回的彩色位图调用快。

如果位图是单色的,那么对于目标设备环境而言,0代表前景颜色,而1表示背景颜色。

如果应用程序将nWidthnHeight参数设为0,那么函数CreateBitmap返回的是只有一个像素的单色位图句柄。当不再需要位图时,可调用DeleteObject函数删除它。

Windows CE:参数cPlanes必须是1