Re: drawing a transparent window control
PaulH wrote:
I'm trying to draw a control, but I don't want the control to have a
background, I'd like to just use whatever is behind it already. So, I
was going to draw the control to a "back-buffer" and bitblt() that
bitmap on to the window using the technique described by Microsoft
here: http://support.microsoft.com/kb/79212. Unfortunately, all I get
is a black rectangle where my control should be. If I just do a
bitblt() SRCCOPY, my control appears, but is drawn on a black
background instead of the background of the window behind it.
Below is the back-buffer class I wrote to handle this effect.
What can I do differently to get the desired effect?
Thanks,
PaulH
//////////////////////////////////////////////////////////////////////////
class CTransparentDC: public CDC
{
public:
// Data members
HDC m_hDCOriginal;
RECT m_rcPaint;
CBitmap m_bmp;
HBITMAP m_hBmpOld;
// Constructor/destructor
CTransparentDC(HDC hDC, RECT& rcPaint) : m_hDCOriginal(hDC),
m_hBmpOld(NULL)
{
m_rcPaint = rcPaint;
CreateCompatibleDC(m_hDCOriginal);
ATLASSERT(m_hDC != NULL);
m_bmp.CreateCompatibleBitmap(m_hDCOriginal, m_rcPaint.right -
m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top);
ATLASSERT(m_bmp.m_hBitmap != NULL);
m_hBmpOld = SelectBitmap(m_bmp);
SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top);
}
~CTransparentDC()
{
//use the line below instead of DrawTransparentBitmap() and the
control appears, but is painted on a black background
//::BitBlt(m_hDCOriginal, m_rcPaint.left, m_rcPaint.top,
m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top,
m_hDC, m_rcPaint.left, m_rcPaint.top, SRCCOPY);
//using DrawTransparentBitmap(), all I get is a black box where
the control should be.
DrawTransparentBitmap(m_hDCOriginal, m_bmp, m_rcPaint.left,
m_rcPaint.top, RGB(0,0,0));
SelectBitmap(m_hBmpOld);
}
void DrawTransparentBitmap(HDC hdc, HBITMAP hBitmap, int xStart,
int yStart, COLORREF cTransparentColor)
{
BITMAP bm;
COLORREF cColor;
HBITMAP bmAndBack, bmAndObject, bmAndMem, bmSave;
HBITMAP bmBackOld, bmObjectOld, bmMemOld, bmSaveOld;
HDC hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
POINT ptSize;
hdcTemp = ::CreateCompatibleDC(hdc);
::SelectObject(hdcTemp, hBitmap); // Select the bitmap
::GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
ptSize.x = bm.bmWidth; // Get width of bitmap
ptSize.y = bm.bmHeight; // Get height of bitmap
::DPtoLP(hdcTemp, &ptSize, 1); // Convert from device
// to logical points
// Create some DCs to hold temporary data.
hdcBack = ::CreateCompatibleDC(hdc);
hdcObject = ::CreateCompatibleDC(hdc);
hdcMem = ::CreateCompatibleDC(hdc);
hdcSave = ::CreateCompatibleDC(hdc);
// Create a bitmap for each DC. DCs are required for a number
of
// GDI functions.
// Monochrome DC
bmAndBack = ::CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
// Monochrome DC
bmAndObject = ::CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
bmAndMem = ::CreateCompatibleBitmap(hdc, ptSize.x,
ptSize.y);
bmSave = ::CreateCompatibleBitmap(hdc, ptSize.x,
ptSize.y);
// Each DC must select a bitmap object to store pixel data.
bmBackOld = (HBITMAP)::SelectObject(hdcBack,
(HGDIOBJ)bmAndBack);
bmObjectOld = (HBITMAP)::SelectObject(hdcObject,
(HGDIOBJ)bmAndObject);
bmMemOld = (HBITMAP)::SelectObject(hdcMem,
(HGDIOBJ)bmAndMem);
bmSaveOld = (HBITMAP)::SelectObject(hdcSave,
(HGDIOBJ)bmSave);
// Set proper mapping mode.
::SetMapMode(hdcTemp, ::GetMapMode(hdc));
// Save the bitmap sent here, because it will be overwritten.
::BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
SRCCOPY);
// Set the background color of the source DC to the color.
// contained in the parts of the bitmap that should be
transparent
cColor = ::SetBkColor(hdcTemp, cTransparentColor);
// Create the object mask for the bitmap by performing a BitBlt
// from the source bitmap to a monochrome bitmap.
::BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
SRCCOPY);
// Set the background color of the source DC back to the
original
// color.
::SetBkColor(hdcTemp, cColor);
// Create the inverse of the object mask.
::BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
NOTSRCCOPY);
// Copy the background of the main DC to the destination.
::BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, xStart, yStart,
SRCCOPY);
// Mask out the places where the bitmap will be placed.
::BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
SRCAND);
// Mask out the transparent colored pixels on the bitmap.
::BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0,
SRCAND);
// XOR the bitmap with the background on the destination DC.
::BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
SRCPAINT);
// Copy the destination to the screen.
::BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0,
SRCCOPY);
// Place the original bitmap back into the bitmap sent here.
::BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0,
SRCCOPY);
// Delete the memory bitmaps.
::DeleteObject(::SelectObject(hdcBack, bmBackOld));
::DeleteObject(::SelectObject(hdcObject, bmObjectOld));
::DeleteObject(::SelectObject(hdcMem, bmMemOld));
::DeleteObject(::SelectObject(hdcSave, bmSaveOld));
// Delete the memory DCs.
::DeleteDC(hdcMem);
::DeleteDC(hdcBack);
::DeleteDC(hdcObject);
::DeleteDC(hdcSave);
::DeleteDC(hdcTemp);
}
};
Ah, nevermind. I've rediscovered the TransparentBlt() command.
Although, now I have the problem where I'm painting on top of what I
painted before, so everything ends up in a big blur after a couple
frames.
So, I need to somehow erase the background, but without causing flicker
(right now my WM_ERASEBKGND handler just says return 1;)
How does one do that?
Thanks,
PaulH