Introduction
I finally managed to get transparent drawing working and made a few routines that makes it a snap. I adapted this code from an example Chris Becke's Bitmap Basics - A GDI tutorial.
The situation
I have a master picture with lots of images in it, the transparent areas are all in purple. At runtime I pick parts of this picture and BitBlt
it on the screen while preserving the transparency.
Or maybe you have a bitmap that you wish to show transparently, just set the transparent areas to a unique color and use the routines below.
First the routines, at the end an example that puts it all together.
//**----------------------------------------------------------
//** STEP 1: Load the bitmap into a CBitmap Object
//**----------------------------------------------------------
BOOL CMyDlg::LoadFileBitmap(CBitmap* pBmp, LPCTSTR szFilename)
{
pBmp->DeleteObject();
return pBmp->Attach(LoadImage(NULL, szFilename, IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_DEFAULTSIZE));
}
//**----------------------------------------------------------
//** STEP 2: Create the Mask and dump it into a CBitmap Object
//**----------------------------------------------------------
void CMyDlg::PrepareMask( CBitmap* pBmpSource,
CBitmap* pBmpMask,
COLORREF clrpTransColor, // Pass null if unknown
int iTransPixelX, // = 0
int iTransPixelY // = 0
)
{
BITMAP bm;
// Get the dimensions of the source bitmap
pBmpSource->GetObject(sizeof(BITMAP), &bm);
// Create the mask bitmap
pBmpMask->DeleteObject();
pBmpMask->CreateBitmap( bm.bmWidth, bm.bmHeight, 1, 1, NULL);
// We will need two DCs to work with. One to hold the Image
// (the source), and one to hold the mask (destination).
// When blitting onto a monochrome bitmap from a color, pixels
// in the source color bitmap that are equal to the background
// color are blitted as white. All the remaining pixels are
// blitted as black.
CDC hdcSrc, hdcDst;
hdcSrc.CreateCompatibleDC(NULL);
hdcDst.CreateCompatibleDC(NULL);
// Load the bitmaps into memory DC
CBitmap* hbmSrcT = (CBitmap*) hdcSrc.SelectObject(pBmpSource);
CBitmap* hbmDstT = (CBitmap*) hdcDst.SelectObject(pBmpMask);
// Dynamically get the transparent color
COLORREF clrTrans;
if (clrpTransColor == NULL)
{
// User did not specify trans color so get it from bmp
clrTrans = hdcSrc.GetPixel(iTransPixelX, iTransPixelY);
}
else
{
clrTrans = clrpTransColor;
}
// Change the background to trans color
COLORREF clrSaveBk = hdcSrc.SetBkColor(clrTrans);
// This call sets up the mask bitmap.
hdcDst.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &hdcSrc,0,0,SRCCOPY);
// Now, we need to paint onto the original image, making
// sure that the "transparent" area is set to black. What
// we do is AND the monochrome image onto the color Image
// first. When blitting from mono to color, the monochrome
// pixel is first transformed as follows:
// if 1 (black) it is mapped to the color set by SetTextColor().
// if 0 (white) is is mapped to the color set by SetBkColor().
// Only then is the raster operation performed.
COLORREF clrSaveDstText = hdcSrc.SetTextColor(RGB(255,255,255));
hdcSrc.SetBkColor(RGB(0,0,0));
hdcSrc.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &hdcDst,0,0,SRCAND);
// Clean up by deselecting any objects, and delete the
// DC hdcDst.SetTextColor(clrSaveDstText);
hdcSrc.SetBkColor(clrSaveBk);
hdcSrc.SelectObject(hbmSrcT);
hdcDst.SelectObject(hbmDstT);
hdcSrc.DeleteDC();
hdcDst.DeleteDC();
}
//**----------------------------------------------------------
//** STEP 3: Drawing with Transparency. Call from OnPaint
//**----------------------------------------------------------
void CMyDlg::DrawTransparentBitmap(CMemDC* pDC,
int xStart, int yStart,
int wWidth, int wHeight,
CDC* pTmpDC,
int xSource, // = 0
int ySource // = 0)
{
// We are going to paint the two DDB // 1st the monochrome bitmap will be blitted using an AND operation to
// cut a hole in the destination. The color image will then be ORed
// with the destination, filling it into the hole, but leaving the
// surrounding area untouched.
CDC hdcMem;
hdcMem.CreateCompatibleDC(NULL);
CBitmap* hbmT = hdcMem.SelectObject(&m_bmpMask);
pDC->BitBlt( xStart, yStart, wWidth, wHeight, &hdcMem,
xSource, ySource, SRCAND);
// Also note the use of SRCPAINT rather than SRCCOPY.
pDC->BitBlt(xStart, yStart, wWidth, wHeight, pTmpDC,
xSource, ySource,SRCPAINT);
// Now, clean up.
hdcMem.SelectObject(hbmT);
hdcMem.DeleteDC();
}
It is that simple. MSDN examples are very confusing. Chris Becke's examples are a lot better but in Win32. :)
So here is a example on how to put it together in a real life situation.
In YourDlg.h, add the following
CBitmap m_bmpPlmain;
CBitmap m_bmpMask;
BOOL LoadFileBitmap(CBitmap* pBmp, LPCTSTR szFilename);
void PrepareMask( CBitmap* pBmpSource,
CBitmap* pBmpMask,
COLORREF clrpTransColor,
int iTransPixelX = 0,
int iTransPixelY = 0 );
void DrawTransparentBitmap (CMemDC* pDC,
int xStart, int yStart,
int wWidth, int wHeight,
CDC* pTmpDC,
int xSource = 0,
int ySource = 0);
CYourDlg::OnInitDialog()
{
...
...
...
if (!LoadFileBitmap(&m_bmpPLMain, "BmpWithHoles.bmp"))
{
}
PrepareMask( &m_bmpPLMain, &m_bmpMask, NULL, 200, 50);
}
CYourDlg::OnPaint()
{
CPaintDC dc(this); CDC dcMem; dcMem.CreateCompatibleDC(&dc);
CBitmap* pOldBmp = (CBitmap*) dcMem.SelectObject(&m_bmpPLMain);
DrawTransparentBitmap( &dc, POP_X, POP_Y,
POP_WIDTH, POP_HEIGHT,
&dcMem, POP_BMP_X_OFF, POP_BMP_Y_OFF);
....
....
dcMem->SelectObject(pOldBmp);
}
CYourDlg::OnDestroy()
{
m_bmpPLMain.DeleteObject();
m_bmpMask.DeleteObject();
}
That's about it ...
Any mistakes, additions or optimizations, please feel free to point them out. Just send the comments to windev and/or raja@gelife.com.my