Transparent Bitmap - True Mask Method

This code shows you how to draw a transparent bitmap using memory rather than directly using the screen. This is important as intensive bitmap operations are slow due to the number of bits that are affected, and using the screen makes things even worse; some flicker results. The visual flickering can be eliminated by using memory bitmaps:

  1. Copy the section of the screen that is to be affected to a memory bitmap.
  2. Carry out the bitmap operation on the memory bitmap instead of on the screen.
  3. Copy the memory bitmap back to the screen.

The result is that only one blt affects the screen, so there is no flicker. We may be using two more blt operations, but the operation will probably be perceived as quicker as there is no flicker.

N.B. Some device memory blts are faster than blts that have to access the screen.

MSDN

I had written the source code provided many months ago now for a project I was working on. When I decided to submit it to CodeGuru I had to look at MSDN again to refresh my memory and help me construct a decent explanation for the technique that I use.

One article in particular is called 'Bitmaps with Transparency by Ron Gery (Microsoft Developer Network Technology Group)'. His explanation of transparent blts is excellent, and I have used this as my reference for constructing the following brief explanation. If you are still a bit unclear I recommend you read some of the articles that helped this one along...

The True Mask Method

True mask blting does not need any modification on the part of the source bitmap to be useful. The masked blt involves a three-step process and a mask that has all transparent pixels set to 1 and all opaque pixels set to 0:

  1. XOR the source bitmap onto the destination (BitBlt with SRCINVERT). This looks a bit funny, but the second XOR restores the destination to its original state.
  2. Masking operation. When the mask is ANDed to the destination (BitBlt with SRCAND), all of the transparent pixels leave the destination pixels unchanged, while the opaque pixels set the destination to black. Now the destination has a blacked-out image of the opaque part of the source and an XORed image of itself in the transparent part.
  3. XOR the source to the destination (BitBlt with SRCINVERT). The transparent pixels are restored to their original state, and the opaque pixels are copied directly from the source.

How do I use it?

The function takes four parameters:

  1. The device context that you wish to display the bitmap in,
  2. The horizontal position you wish to draw from,
  3. the vertical position you wish to draw from,
  4. and the colour in the bitmap that is considered to be transparent (as a COLORREF).

pBitmap->DrawTransparent(pCDC, xPos, yPos, crTrans);

Before I start getting emails on the subject, I should point out that the techniques described in this article specifically target displays and may not necessarily work on some printer devices.

Source Code

void CCISBitmap::DrawTransparent(CDC * pDC, int x, int y, COLORREF crColour)
{
	COLORREF crOldBack = pDC->SetBkColor(m_crWhite);
	COLORREF crOldText = pDC->SetTextColor(m_crBlack);
	CDC dcImage, dcTrans;

	// Create two memory dcs for the image and the mask
	dcImage.CreateCompatibleDC(pDC);
	dcTrans.CreateCompatibleDC(pDC);

	// Select the image into the appropriate dc
	CBitmap* pOldBitmapImage = dcImage.SelectObject(this);

	// Create the mask bitmap
	CBitmap bitmapTrans;
	int nWidth = Width();
	int nHeight = Height();
	bitmapTrans.CreateBitmap(nWidth, nHeight, 1, 1, NULL);

	// Select the mask bitmap into the appropriate dc
	CBitmap* pOldBitmapTrans = dcTrans.SelectObject(&bitmapTrans);

	// Build mask based on transparent colour
	dcImage.SetBkColor(crColour);
	dcTrans.BitBlt(0, 0, nWidth, nHeight, &dcImage, 0, 0, SRCCOPY);

	// Do the work - True Mask method - cool if not actual display
	pDC->BitBlt(x, y, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT);
	pDC->BitBlt(x, y, nWidth, nHeight, &dcTrans, 0, 0, SRCAND);
	pDC->BitBlt(x, y, nWidth, nHeight, &dcImage, 0, 0, SRCINVERT);

	// Restore settings
	dcImage.SelectObject(pOldBitmapImage);
	dcTrans.SelectObject(pOldBitmapTrans);
	pDC->SetBkColor(crOldBack);
	pDC->SetTextColor(crOldText);
}


Drawing a Bitmap Transparently by Zafir Anjum

This is a similar article on this subject which uses a slightly different technique often referred to as 'The Black Source Method'.

The True Mask method requires no additional work: The mask is built and the source needs no manipulation. The three blts do cause on-screen flicker, but there are only three of them.

The Black Source method requires additional work on the source bitmap: the transparent bits need to be made black. The on-screen flashing is less noticeable with this method, and once the source is set-up with black in the correct places, transparency looks very good. For bitmaps as small as icons, transparency is achieved very smoothly. (This mechanism is the one used by Windows to display icons on the screen.)

Direct Transparent Blts

Some device drivers support transparent blts directly. You can determine this using the C1_TRANSPARENT bit of the CAPS1 capability word returned by the GetDeviceCaps function. A special background mode, NEWTRANSPARENT, indicates that subsequent blts are transparent blts. The current background colour of the destination is the transparent colour. When this capability is available on the driver, the basic transparent blt operation can be performed as follows:

// Only attempt this if device supports functionality.
if(GetDeviceCaps(hdcDest, CAPS1) & C1_TRANSPARENT)
{
   // Special transparency background mode
      oldMode = SetBkMode(hdcDest, NEWTRANSPARENT);
      rgbBk = SetBkColor(hdcDest, rgbTransparent);
   // Actual blt is a simple source copy; transparency is automatic.
      BitBlt(hdcDest, x, y, dx, dy, hdcSrc, x0, y0, SRCCOPY);
      SetBkColor(hdcDest, rgbBk);
      SetBkMode(hdcDest, oldMode);
}

This code has been compiled and built with Microsoft Visual C++ Version 5.0 SP3.

Download source - 1.31 KB