3

I timed a DDB drawing operation which uses multiple StretchBlt and StretchDIBits calls.

And I found that, time to complete is increase/decrease proportionally to the destination window size.
With 900x600 window it takes around 5ms, but with 1920x1080 it takes as large as 55ms (source image is 1280x640).

It seems Stretch.. APIs don't use any hardware acceleration features.

Source image (actually this is temporary drawing canvas) is created with CreateDIBSection because I need resulting (stretched and merged) bitmap's pixel data for every frame drawn.

Let's assume, Windows GDI is hopeless. Then what is the promising alternative?

I considered D3D, D2D with WIC method (write to WIC bitmap and draw it with D2D then read back pixel data from the WIC bitmap).
I planed to try D2D with WIC method because I will needed to use extensive text drawing feature sometime soon.

But it seems WIC is not that promising: What is the most effective pixel format for WIC bitmap processing?

Community
  • 1
  • 1
9dan
  • 4,222
  • 2
  • 29
  • 44
  • Yes, StretchBlt is really slow. And if you want more quality (e.g. using HALFTONE option) it is not possible to work in real-time rendering. Anyway, what OS is your software targeted at? – Anton Angelov Oct 02 '14 at 07:16
  • @AntonAngelov Target OS is Windows 7+. Currently my laptop can do 900x600@20fps smoothly (by GDI), and my goal is 1920x1080@30fps. If I get the result after next week, I might ask another question :) – 9dan Oct 02 '14 at 08:37
  • Okay. But can you formulate your question more precisely (e.g in one sentance)? – Anton Angelov Oct 02 '14 at 08:53
  • @AntonAngelov My goal is accomplished by D2D + WIC method :) Thank you for your kind concerns. – 9dan Oct 02 '14 at 16:44

1 Answers1

5

I've implemented D2D + WIC routine today. Test results are really good.

With my previous GDI StretchDIBits version, it took 20 ~ 60ms time for drawing 1280x640 DDB into a 1920x1080 window. After switching to Direct2D + WIC, it usually takes under 5ms, also picture quality looks better.

I used ID2D1HwndRenderTarget with WicBitmapRenderTarget, because I need to read/write raw pixel data.

HWndRenderTarget is only used for screen painting (WM_PAINT).
The main advantage of HWndRenderTarget is that the destination window size doesn't affect drawing performance.

WicBitmapRenderTarget is used as a temporary drawing canvas (as Memory DC in GDI drawing). We can create WicBitmapRenderTarget with a WIC bitmap object (like GDI DIBSection). We can read/write raw pixel data from/to this WIC bitmap at any time. Also it's very fast. For side note, somewhat similar D3D GetFrontBufferData call is really slow.

Actual pixel I/O is done through IWICBitmap and IWICBitmapLock interface.

Writing:

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

Reading:

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

Drawing:

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

In my opinion, GDI Stretch.. APIs are totally useless in Windows 7+ setup (for performance sensitive applications).

Also note that, unlike Direct3D, basic graphics operations such as text drawing, ling drawing are really simple in Direct2D.

9dan
  • 4,222
  • 2
  • 29
  • 44
  • I still cannot understand why you need to use WIC render target. Do you have to do CPU pixel manipulation after the resize, or something else? By the way, have you tried multimedia frameworks like DirectShow, Media Foundation? – Anton Angelov Oct 03 '14 at 06:55
  • I think my performance gain come solely from D2D rendering. WIC is just a replacement of GDI DIB section API, not for performance. I used WIC because I need final pixel data for screen recording. DirectShow requires complicated 'pin' interface coding and its own timing rules. Don't know much about MF. – 9dan Oct 03 '14 at 08:42