如何从GDI设备上下文获取32bpp位图/图像?

时间:2015-10-10 20:43:48

标签: c++ mfc gdi+ gdi alphablending

我正在使用此项目http://www.codeproject.com/Articles/9064/Yet-Another-Transparent-Static-Control中的代码,以便将我的子类Button控件中的透明按钮图像绘制到我的CDialogEx上。

此代码适用于传统的24bpp GDI函数:

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;
}

然而,我的CDialogEx的背景是用GDI + 32bpp渲染的,如下所示:

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());
}

这会导致第一个代码段备份黑色矩形而不是32bpp的内容。这再次导致我的按钮控件始终具有黑色背景。

为了清楚我的问题,请看下面的图片:

  1. 正在将按钮图像绘制到CDialogEx背景上(通常):
  2. Normal Button Background

    1. 正在使用第一个代码段
    2. 绘制按钮图像

      Faulty Button Background

      正如您所见,GDI 24bpp无法看到对话框背景。它只是假设纯黑色背景。只有GDI +可以看到它。但是我无法找到从Gdiplus::Graphics对象获取位图的方法。

      如何才能获得32bpp后台备份才能正确绘制透明图像?

      根本不使用备用图像会导致GDI +的Alpha混合在每次绘制时越来越模糊背景。

2 个答案:

答案 0 :(得分:2)

经过3天的计算,我通过测试找到了一些东西。这实际上可以更容易和32bpp渲染

// 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);

你走了! 4行代码,你得到一个32bpp的Gdiplus :: Bitmap!您可以稍后在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());

请注意,Bitmap应该是性能提升的成员变量。它也必须释放控制破坏。

答案 1 :(得分:1)

而不是Gdiplus::Image,请使用Gdiplus::Bitmap

GetHBITMAP

在此示例中,对于具有透明背景的png图像,背景设置为红色:

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);

或者,您可以使用画笔来处理绘画:

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);
}