一、绘制透明背景的位图,windows提供了一个API函数
TransparentBlt
The TransparentBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context.
BOOL TransparentBlt( HDC hdcDest, // handle to destination DC int nXOriginDest, // x-coord of destination upper-left corner int nYOriginDest, // y-coord of destination upper-left corner int nWidthDest, // width of destination rectangle int hHeightDest, // height of destination rectangle HDC hdcSrc, // handle to source DC int nXOriginSrc, // x-coord of source upper-left corner int nYOriginSrc, // y-coord of source upper-left corner int nWidthSrc, // width of source rectangle int nHeightSrc, // height of source rectangle UINT crTransparent // color to make transparent );
使用起来也挺方便,如下
// 加载显示位图 CBitmap bmpTest; if ( bmpTest.LoadBitmap( IDB_BITMAP1 ) ) { BITMAP bmpInfo; bmpTest.GetBitmap(&bmpInfo); CDC mBufferDC; mBufferDC.CreateCompatibleDC( &dc ); CBitmap* pOldBmp1 = mBufferDC.SelectObject( &bmpTest ); COLORREF bgColor = RGB( 27, 240, 67 ); // 位图背景颜色 ::TransparentBlt( dc, 100, 100, bmpInfo.bmWidth, bmpInfo.bmHeight, mBufferDC, 0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight, bgColor ); mBufferDC.SelectObject( pOldBmp1 ); }
二、下面模拟TransparentBlt的实现过程,探究下底层实现, 整个实现过程的思想如下图所示,
代码如下:
{ CPaintDC dc(this); // 刷显示背景为绿色RGB( 125, 125, 200 ) CRect rect; GetClientRect(&rect); dc.FillSolidRect( rect.left, rect.top, rect.right, rect.bottom, RGB( 125, 125, 200 ) ); // 加载位图 CBitmap bmpToShow; if ( !bmpToShow.LoadBitmap( IDB_BITMAP1 ) ) { return; } // 获取位图宽、高 BITMAP bmpInfo; bmpToShow.GetBitmap(&bmpInfo); int nBmpWidth = bmpInfo.bmWidth; int nBmpHeight = bmpInfo.bmHeight; // 位图背景色 COLORREF bmpBkColor = RGB(27, 240, 67); // 位图DC CDC bmpToShowDC; bmpToShowDC.CreateCompatibleDC( &dc ); CBitmap* pOldBmp1 = bmpToShowDC.SelectObject( &bmpToShow ); // 创建单色掩码位图 CBitmap maskBmp; maskBmp.CreateBitmap( nBmpWidth, nBmpHeight, 1, 1, NULL ); // 掩码DC CDC bmpMaskDC; bmpMaskDC.CreateCompatibleDC( &dc ); CBitmap* pOldBmp2 = bmpMaskDC.SelectObject( &maskBmp ); // 【对应上图步骤1】生成位图的单色掩码图 // 原理:如果目标dc的位图是单色位图,源dc的位图是颜色位图, // 则在实际光栅操作之前要把颜色位图转换成单色位图,转换规 // 则是,颜色位图中所有和背景色一致的象素都变成1,其他的象 // 素都被转换成0。 bmpToShowDC.SetBkColor( bmpBkColor ); bmpMaskDC.BitBlt( 0, 0, nBmpWidth, nBmpHeight, &bmpToShowDC, 0, 0, SRCCOPY ); // 【对应上图步骤2】将显示区域与单色位图作AND操作 // 原理:当目标dc的位图是颜色位图,源dc的位图是单色的时候, // 单色位图在实际的光栅操作(ROP)之前会被转换成颜色位图, // 对应的位如果是0,则被转换目标dc的前景色,如果该位是1, // 则被转换成目标dc的背景色。 dc.SetBkColor( RGB(255, 255, 255) ); dc.SetTextColor( RGB(0, 0, 0) ); dc.BitBlt( 100, 100, nBmpWidth, nBmpHeight, &bmpMaskDC, 0, 0, SRCAND ); // // 【对应上图步骤3】对掩码进行取反操作 // bmpMaskDC.BitBlt( 0, 0, nBmpWidth, nBmpHeight, // &bmpMaskDC, 0, 0, NOTSRCCOPY ); // // // 【对应上图步骤4】原图与掩码进行AND操作 // // 原理:当目标dc的位图是颜色位图,源dc的位图是单色的时候, // // 单色位图在实际的光栅操作(ROP)之前会被转换成颜色位图, // // 对应的位如果是0,则被转换目标dc的前景色,如果该位是1, // // 则被转换成目标dc的背景色。 // bmpToShowDC.SetBkColor( RGB( 255, 255, 255 )); // bmpToShowDC.SetTextColor( RGB( 0, 0, 0) ); // bmpToShowDC.BitBlt( 0, 0, nBmpWidth, nBmpHeight, // &bmpMaskDC, 0, 0, SRCAND ); // 步骤3和步骤4可以合并为一步 // 原理:当目标dc的位图是颜色位图,源dc的位图是单色的时候, // 单色位图在实际的光栅操作(ROP)之前会被转换成颜色位图, // 对应的位如果是0,则被转换目标dc的前景色,如果该位是1, // 则被转换成目标dc的背景色。 bmpToShowDC.SetBkColor( RGB( 0, 0, 0 ) ); bmpToShowDC.SetTextColor( RGB( 255, 255, 255 ) ); bmpToShowDC.BitBlt( 0, 0, nBmpWidth, nBmpHeight, &bmpMaskDC, 0, 0, SRCAND ); // 【对应上图步骤5】生成目标图片 dc.BitBlt( 100, 100, nBmpWidth, nBmpHeight, &bmpToShowDC, 0, 0, SRCPAINT ); bmpMaskDC.SelectObject( pOldBmp2 ); bmpToShowDC.SelectObject( pOldBmp1 ); }
参考: