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

<!-- /* 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” 。下面是书中的程序:

    void CTransBitmapDlg::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, UINT cBitsPerPel, 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值