【问题标题】:How to get a 32bpp bitmap/image from a GDI Device Context?如何从 GDI 设备上下文中获取 32bpp 位图/图像?
【发布时间】:2016-01-08 14:43:47
【问题描述】:

我正在使用来自此项目 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());
}

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

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

  1. 按钮图像被绘制到 CDialogEx 背景上(通常):

  1. 正在使用第一个代码 sn-p 绘制按钮图像

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

如何获得 32bpp 背景备份以正确绘制透明图像?

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

【问题讨论】:

    标签: c++ mfc gdi+ gdi alphablending


    【解决方案1】:

    经过 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 应该是提高性能的成员变量。它还必须在控件销毁时被释放。

    【讨论】:

      【解决方案2】:

      使用Gdiplus::Bitmap 代替Gdiplus::Image,它具有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);
      }
      

      【讨论】:

      • 它实际上可以在没有老式 CBitmaps 和 Gdiplus 32bpp 渲染的情况下完成!感谢您的支持
      猜你喜欢
      • 2016-01-07
      • 2013-07-13
      • 2013-09-15
      • 2011-08-29
      • 2011-12-11
      • 1970-01-01
      • 1970-01-01
      • 2018-01-01
      • 2014-02-16
      相关资源
      最近更新 更多