我为DDB绘制操作计时,该操作使用多个StretchBltStretchDIBits调用。

而且我发现,完成时间是与目标窗口大小成正比的增加/减少。
在900x600的窗口中,大约需要5毫秒,而在1920x1080的窗口中,则需要55毫秒(源图像为1280x640)。

似乎Stretch .. API不使用任何硬件加速功能。

源图像(实际上是临时绘图 Canvas )是使用CreateDIBSection创建的,因为我需要为绘制的每个帧生成(拉伸(stretch)和合并)位图的像素数据。

假设Windows GDI是绝望的。那么,什么是有前途的选择呢?

我考虑了使用WIC方法的D3D,D2D(写入WIC位图并使用D2D绘制它,然后从WIC位图读回像素数据)。
我计划尝试使用WIC方法进行D2D,因为不久以后我将需要使用广泛的文本绘图功能。

但似乎WIC并不那么有前途:What is the most effective pixel format for WIC bitmap processing?

最佳答案

我今天实现了D2D + WIC例程。测试结果真的很好。

在我以前的GDI StretchDIBits版本中,将1280x640 DDB绘制到1920x1080窗口中需要20到60毫秒的时间。切换到Direct2D + WIC后,通常需要5毫秒以下的时间,图像质量也会更好。

我将ID2D1HwndRenderTarget与WicBitmapRenderTarget一起使用,因为我需要读取/写入原始像素数据。

HWndRenderTarget仅用于屏幕绘画(WM_PAINT)。
HWndRenderTarget的主要优点是目标窗口的大小不会影响图形性能。

WicBitmapRenderTarget用作临时绘图 Canvas (作为GDI绘图中的Memory DC)。我们可以使用WIC位图对象(例如GDI DIBSection)创建WicBitmapRenderTarget。我们可以随时从该WIC位图中读取/写入原始像素数据。而且它非常快。附带说明一下,有些类似的D3D GetFrontBufferData调用确实很慢。

实际的像素I/O通过IWICBitmap和IWICBitmapLock接口(interface)完成。

写作:

IWICBitmapPtr m_wicRemote;
...
const uint8* image = ...;
...
WICRect rcLock = { 0, 0, width, height };
IWICBitmapLockPtr wicLock;
hr = m_wicRemote->Lock(&rcLock, WICBitmapLockWrite, &wicLock);
if (SUCCEEDED(hr))
{
    UINT cbBufferSize = 0;
    BYTE *pv = NULL;
    hr = wicLock->GetDataPointer(&cbBufferSize, &pv);
    if (SUCCEEDED(hr))
    {
        memcpy(pv, image, cbBufferSize);
    }
}

m_wicRenderTarget->BeginDraw();
m_wicRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
ID2D1BitmapPtr d2dBitmap;
hr = m_wicRenderTarget->CreateBitmapFromWicBitmap(m_wicRemote, &d2dBitmap.GetInterfacePtr());
if (SUCCEEDED(hr))
{
    float cw = (renderTargetSize.width / 2);
    float ch = renderTargetSize.height;
    float x, y, w, h;
    FitFrameToCenter(cw, ch, (float)width, (float)height, x, y, w, h);
    m_wicRenderTarget->DrawBitmap(d2dBitmap, D2D1::RectF(x, y, x + w, y + h));
}
m_wicRenderTarget->EndDraw();

读:
IWICBitmapPtr m_wicCanvas;
IWICBitmapLockPtr m_wicLockedData;
...
UINT width, height;
HRESULT hr = m_wicCanvas->GetSize(&width, &height);
if (SUCCEEDED(hr))
{
    WICRect rcLock = { 0, 0, width, height };
    hr = m_wicCanvas->Lock(&rcLock, WICBitmapLockRead, &m_wicLockedData);
    if (SUCCEEDED(hr))
    {
        UINT cbBufferSize = 0;
        BYTE *pv = NULL;
        hr = m_wicLockedData->GetDataPointer(&cbBufferSize, &pv);
        if (SUCCEEDED(hr))
        {
            return pv; // return data pointer
            // need to Release m_wicLockedData after reading is done
        }
    }
}

绘画:
ID2D1HwndRenderTargetPtr m_renderTarget;
....

D2D1_SIZE_F renderTargetSize = m_renderTarget->GetSize();
m_renderTarget->BeginDraw();
m_renderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
m_renderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));

ID2D1BitmapPtr d2dBitmap;
hr = m_renderTarget->CreateBitmapFromWicBitmap(m_wicCanvas, &d2dBitmap.GetInterfacePtr());
if (SUCCEEDED(hr))
{
    UINT width, height;
    hr = m_wicCanvas->GetSize(&width, &height);
    if (SUCCEEDED(hr))
    {
        float x, y, w, h;
        FitFrameToCenter(renderTargetSize.width, renderTargetSize.height, (float)width, (float)height, x, y, w, h);

        m_renderTarget->DrawBitmap(d2dBitmap, D2D1::RectF(x, y, x + w, y + h));
    }
}
m_renderTarget->EndDraw();

我认为,GDI Stretch.. API在Windows 7+设置中(对于性能敏感的应用程序)完全没有用。

还应注意,与Direct3D不同,在Direct2D中基本的图形操作(如文本绘图,图形绘图)确实非常简单。

09-07 09:33