1
votes

I am using code from this Project http://www.codeproject.com/Articles/9064/Yet-Another-Transparent-Static-Control in order to draw transparent button images from my subclassed Button control onto my CDialogEx.

This code is meant for legacy 24bpp GDI functions:

BOOL CTransparentStatic2::OnEraseBkgnd(CDC* pDC)
{
   if (m_Bmp.GetSafeHandle() == NULL)
   {
      CRect Rect;
      GetWindowRect(&Rect);
      CWnd *pParent = GetParent();
      ASSERT(pParent);
      pParent->ScreenToClient(&Rect); //convert our corrdinates to our parents

      //copy what's on the parents at this point
      CDC *pDC = pParent->GetDC();
      CDC MemDC;
      MemDC.CreateCompatibleDC(pDC);
      m_Bmp.CreateCompatibleBitmap(pDC,Rect.Width(),Rect.Height());
      CBitmap *pOldBmp = MemDC.SelectObject(&m_Bmp);

      MemDC.BitBlt(0,0,Rect.Width(),Rect.Height(),pDC,Rect.left,Rect.top,SRCCOPY);

      MemDC.SelectObject(pOldBmp);

      pParent->ReleaseDC(pDC);
   }
   else //copy what we copied off the parent the first time back onto the parent
   {
      CRect Rect;
      GetClientRect(Rect);
      CDC MemDC;
      MemDC.CreateCompatibleDC(pDC);
      CBitmap *pOldBmp = MemDC.SelectObject(&m_Bmp);
      pDC->BitBlt(0,0,Rect.Width(),Rect.Height(),&MemDC,0,0,SRCCOPY);
      MemDC.SelectObject(pOldBmp);
   }

   return TRUE;
}

However the background of my CDialogEx is being drawn with GDI+ 32bpp rendering like this:

BOOL CParentDialogEx::OnEraseBkgnd(CDC* pDC)
{
   // Get GDI+ Graphics for the current Device Context
   Graphics gr(*pDC);

   // Get the client area
   CRect clientRect;
   GetClientRect(&clientRect);

   // Draw the dialog background
   // PLEASE NOTE: m_imgDlgBkgnd is a Gdiplus::Image with PNG format ==> 32bpp Image
   gr.DrawImage(m_imgDlgBkgnd, 0, 0, clientRect.Width(), clientRect.Height());
}

This causes the first code snippet to make a backup of a black rectangle instead of the 32bpp drawn content to it. This again causes my button control to always have a black background.

To make my problem clear, please see the pictures below:

  1. Button images are being drawn onto the CDialogEx background (normally):

Normal Button Background

  1. Button images are being drawn with the first code snippet

Faulty Button Background

As you can see GDI 24bpp cannot see the dialog background. It just assumes plain black background. Only GDI+ could see it. However I was not able to find a way to get a bitmap from a Gdiplus::Graphics object.

How can I get a 32bpp background backup in order to draw my transparent images correctly?

Using no backup images at all causes the alpha blending of GDI+ to blur the background more and more on every draw.

2

2 Answers

2
votes

After 3 days of figuring around I found something by testing. This can actually be done way easier and with 32bpp rendering!

// Get the client area on the parent
CRect Rect;
GetWindowRect(&Rect);
CWnd *pParent = GetParent();
ASSERT(pParent);
pParent->ScreenToClient(&Rect);

// Get the Parent's DC
CDC *parentDC = pParent->GetDC();

// GDI Code (only 24bpp support)
//CDC MemDC;
//CBitmap bmp;
//MemDC.CreateCompatibleDC(parentDC);
//bmp.CreateCompatibleBitmap(parentDC,Rect.Width(),Rect.Height());
//CBitmap *pOldBmp = MemDC.SelectObject(&bmp);
//MemDC.BitBlt(0,0,Rect.Width(),Rect.Height(),parentDC,Rect.left,Rect.top,SRCCOPY);
//MemDC.SelectObject(pOldBmp);

// GDI+ Code with 32 bpp support (here comes the important change)
Bitmap* bmp = new Bitmap(Rect.Width(), Rect.Height());
Graphics bmpGraphics(bmp);
HDC hBmpDC = bmpGraphics.GetHDC();
BitBlt(hBmpDC, 0, 0, Rect.Width(), Rect.Height(), parentDC->GetSafeHdc(), Rect.left, Rect.top, SRCCOPY); // BitBlt is actually capable of doing 32bpp blts

// Release DCs
bmpGraphics.ReleaseDC(hBmpDC);
pParent->ReleaseDC(parentDC);

Here you go! 4 lines of code and you get a 32bpp Gdiplus::Bitmap! Which you can draw later on in OnEraseBkgnd:

// Get client Area
CRect rect;
GetClientRect(&rect);

// Draw copied background back onto the parent
Graphics gr(*pDC);
gr.DrawImage(bmp, 0, 0, rect.Width(), rect.Height());

Please note that the Bitmap should be a member variable for performance increase. It also has to be freed on the controls destruction.

1
votes

Instead of Gdiplus::Image, use Gdiplus::Bitmap which has GetHBITMAP

In this example the background is set to red for png image with transparent background:

Gdiplus::Bitmap gdi_bitmap(L"c:\\test\\filename.png");
HBITMAP hbitmap = NULL;
gdi_bitmap.GetHBITMAP(Gdiplus::Color(128, 0, 0), &hbitmap);
CBitmap bmp;
bmp.Attach(hbitmap);
CDC memdc;
memdc.CreateCompatibleDC(pDC);
memdc.SelectObject(&bmp);
pDC->StretchBlt(0, 0, clientRect.Width(), clientRect.Height(), &memdc, 0, 0, 
        gdi_bitmap.GetWidth(), gdi_bitmap.GetHeight(), SRCCOPY);

Alternatively, you can use a brush to handle the painting:

CBrush m_Brush;
BOOL CMyDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    Gdiplus::Bitmap bitmap(L"c:\\test\\filename.png");
    HBITMAP hbitmap = NULL;
    bitmap.GetHBITMAP(Gdiplus::Color(128, 0, 0), &hbitmap);
    CBitmap bmp;
    bmp.Attach(hbitmap);
    m_Brush.CreatePatternBrush(&bmp);
    return 1;
}

HBRUSH CMyDialog::OnCtlColor(CDC* pDC, CWnd* wnd, UINT nCtlColor)
{
    //uncomment these two lines if you only want to change dialog background
    //if (wnd != this)
    //    return CDialogEx::OnCtlColor(pDC, wnd, nCtlColor);
    if (nCtlColor == CTLCOLOR_STATIC)
    {
        pDC->SetTextColor(RGB(0, 128, 128));
        pDC->SetBkMode(TRANSPARENT);
    }
    CPoint pt(0, 0);
    if (wnd != this)
        MapWindowPoints(wnd, &pt, 1);
    pDC->SetBrushOrg(pt);
    return m_Brush;
}

BOOL CMyDialog::OnEraseBkgnd(CDC* pDC)
{//don't do anything
    return CDialogEx::OnEraseBkgnd(pDC);
}