Windows 7 中的 GDI 加速/绘制到内存位图

2022-01-12 00:00:00 c++ mfc gdi

我的 GDI 程序在 Windows XP 上运行良好,但在 Windows Vista 和 7 上,由于缺少 GDI 硬件加速,它看起来很糟糕.我记得几年前读过一篇文章,说 Windows 7 为一些 GDI 函数添加了硬件加速,包括 BitBlt() 函数.据推测,如果您绘制到内存位图,然后使用 BitBlt() 将图像复制到主窗口,它的运行速度与 XP 大致相同.这是真的吗?

My GDI program runs fine on Windows XP but on Windows Vista and 7 it looks pretty terrible due to the lack of GDI hardware acceleration. I recall reading an article a few years back saying that Windows 7 added hardware acceleration to some GDI functions, including BitBlt() function. Supposedly, if you if you draw to a memory bitmap and then use BitBlt() to copy the image to your main window it runs about the same speed as XP. Is that true?

如果是真的,你是怎么做到的?我在编程方面很糟糕,并且遇到了一些麻烦.我创建了下面的类来尝试让它工作:

If it is true, how do you do it? I'm terrible at programming and am having a bit of trouble. I created the below class to to try and get it working:

class CMemBmpTest
{
private: 
    CDC         m_dcDeviceContext;
    CBitmap     m_bmpDrawSurface;

public:
    CMemBmpTest();
    ~CMemBmpTest();
    void Init();
    void Draw();
};

CMemBmpTest::CMemBmpTest()
{
}

CMemBmpTest::~CMemBmpTest()
{
    m_bmpDrawSurface.DeleteObject();
    m_dcDeviceContext.DeleteDC();
}

void CMemBmpTest::Init()
{  
    m_dcDeviceContext.CreateCompatibleDC(NULL);
    m_bmpDrawSurface.CreateCompatibleBitmap(&m_dcDeviceContext, 100, 100);
}

void CMemBmpTest::Draw()
{  
    m_dcDeviceContext.SelectObject(I.m_brshRedBrush);
    m_dcDeviceContext.PatBlt(0, 0, 100, 100, BLACKNESS);
}

在窗口的 OnPaint() 函数中我添加了一行:

In the OnPaint() function of the window I added the line:

pDC->BitBlt(2, 2, 100, 100, &m_MemBmp, 0, 0, SRCCOPY);

我希望在窗口的角落看到一个 100x100 的黑框,但它没有用.我可能做的一切都非常错误,所以如果有人能建议我如何正确地做到这一点,我将不胜感激.

I was hoping to see a 100x100 black box in the corner of the window but it didn't work. I'm probably doing everything horrifically wrong, so would be grateful if somebody could advise me as to how to do this correctly.

感谢您提供的任何建议.

Thanks for any advice you can offer.

推荐答案

AFAIK 你可以在所有版本的 Windows 上获得 GDI 功能的硬件加速(如果有人可以更详细地解释它,我很高兴对此进行纠正).但无论哪种方式,你都是正确的,相对于直接在屏幕上绘图,双缓冲(这就是你所说的)提供了巨大的性能提升(更重要的是没有闪烁).

AFAIK you get hardware acceleration on GDI functions on all versions of Windows (I'm happy to stand corrected on this if someone can explain it in more detail). But either way, you're correct that double buffering (which is what you're talking about) provides a massive performance boost (and more importantly no flickering) relative to drawing direct to the screen.

我已经做了很多这样的工作,并提出了一种方法,允许您在绘图功能中同时使用 GDI 和 GDI+,但受益于 BitBlt 在屏幕绘图中的硬件加速.GDI+ 不是硬件加速的 AFAIK,但在许多更复杂的绘图技术中非常有用,因此可以选择.

I've done quite a lot of this and have come up with a method to allow you to use GDI and GDI+ at the same time in your drawing functions, but benefit from the hardware acceleration of the BitBlt in drawing to screen. GDI+ isn't hardware accelerated AFAIK but can be very useful in many more complex drawing techniques so it can be useful to have the option of.

所以,我的基本视图类将有以下成员:

So, my basic view class will have the following members :

Graphics *m_gr;
CDC *m_pMemDC;
CBitmap *m_pbmpMemBitmap;

那么类本身就会有类似这样的代码

Then the class itself will have code something like this

    /*======================================================================================*/
    CBaseControlPanel::CBaseControlPanel()
    /*======================================================================================*/
    { 
        m_pMemDC = NULL;
        m_gr = NULL;
        m_pbmpMemBitmap = NULL;
    }

    /*======================================================================================*/
    CBaseControlPanel::~CBaseControlPanel()
    /*======================================================================================*/
    {
        // Clean up all the GDI and GDI+ objects we've used
        if(m_pMemDC)
        { delete m_pMemDC; m_pMemDC = NULL; }
        if(m_pbmpMemBitmap)
        { delete m_pbmpMemBitmap; m_pbmpMemBitmap = NULL; }
        if(m_gr)
        { delete m_gr; m_gr = NULL; }
    }   

/*======================================================================================*/
void CBaseControlPanel::OnPaint()
/*======================================================================================*/
{
    pDC->BitBlt(rcUpdate.left, rcUpdate.top, rcUpdate.Width(), rcUpdate.Height(),
                        m_pMemDC, rcUpdate.left, rcUpdate.top, SRCCOPY);
}

/*======================================================================================*/
void CBaseControlPanel::vCreateScreenBuffer(const CSize szPanel, CDC *pDesktopDC)
// In : 
//      szPanel = The size that we want the double buffer bitmap to be
// Out : None
/*======================================================================================*/
{
    // Delete anything we're already using first
    if(m_pMemDC)
    {
        delete m_gr;
        m_gr = NULL;
        delete m_pMemDC;
        m_pMemDC = NULL;
        delete m_pbmpMemBitmap;
        m_pbmpMemBitmap = NULL;
    }
    // Make a compatible DC
    m_pMemDC = new CDC;
    m_pMemDC->CreateCompatibleDC(pDesktopDC);           
    // Create a new bitmap
    m_pbmpMemBitmap = new CBitmap;
    // Create the new bitmap
    m_pbmpMemBitmap->CreateCompatibleBitmap(pDesktopDC, szPanel.cx, szPanel.cy);
    m_pbmpMemBitmap->SetBitmapDimension(szPanel.cx, szPanel.cy);
    // Select the new bitmap into the memory DC
    m_pMemDC->SelectObject(m_pbmpMemBitmap);
    // Then create a GDI+ Graphics object
    m_gr = Graphics::FromHDC(m_pMemDC->m_hDC);
    // And update the bitmap
    rcUpdateBitmap(rcNewSize, true);
}

/*======================================================================================*/
CRect CBaseControlPanel::rcUpdateBitmap(const CRect &rcInvalid, const bool bInvalidate, const bool bDrawBackground /*=true*/)
// Redraws an area of the double buffered bitmap
// In : 
//      rcInvalid - The rect to redraw
//      bInvalidate - Whether to refresh to the screen when we're done
//      bDrawBackground - Whether to draw the background first (can give speed benefits if we don't need to)
// Out : None
/*======================================================================================*/
{
   // The memory bitmap is actually updated here

   // Then make the screen update
   if(bInvalidate)
   { InvalidateRect(rcInvalid); }
}

因此,您可以直接绘制到内存 DC 并调用 InvalidateRect() 或将所有绘图代码放在 rcUpdateBitmap() 中,这对于我使用它的方式更方便.您需要在 OnSize() 中调用 vCreateScreenBuffer().

So, you can then either just draw direct to the memory DC and call InvalidateRect() or put all your drawing code in rcUpdateBitmap() which was more convenient for the way I was using it. You'll need to call vCreateScreenBuffer() in OnSize().

希望无论如何都能给你一些想法.双缓冲绝对是提高速度和不闪烁 UI 的方法.开始可能需要一点努力,但绝对值得.

Hopefully that gives you some ideas anyway. Double buffering is definitely the way to go for speed and non-flickering UI. It can take a little bit of effort to get going but it's definitely worth it.

相关文章