MFC Support for Direct2D – Interoperability with GDI

There are two ways to combine Direct2D with Windows GDI API in the same application:

  1. Drawing Direct2D content to a GDI device context
  2. Drawing GDI content to a Direct2D GDI-compatible render target

Let’s see each one!

Drawing Direct2D content to a GDI device context

For this purpose, use ID2D1DCRenderTarget instead of ID2D1HwndRenderTarget interface. If using MFC shipped with Visual Studio 2015 or newer, then CDCRenderTarget wrapper class makes the programmer life easier. Here are the steps:

  1. In the WM_CREATE message handler call CWnd::EnableD2DSupport passing TRUE as second parameter. This way, the MFC framework creates a CDCRenderTarget instead of a CHwndRenderTarget object.
    int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (__super::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        // Note: need Visual Studio 2015 or newer (_MFC_VER >= 0x0E00)
        BOOL bUseDCRenderTarget = TRUE;
        EnableD2DSupport(TRUE, bUseDCRenderTarget);
    
        if (!IsD2DSupportEnabled())
            return -1;
    
        return 0;
    }
    
  2. Map AFX_WM_DRAW2D registered messge.
    class CChildView : public CWnd
    {
        // ...
        afx_msg LRESULT OnAfxDraw2D(WPARAM wParam, LPARAM lParam);
    };
    
    BEGIN_MESSAGE_MAP(CChildView, CWnd)
        // ...
        ON_REGISTERED_MESSAGE(AFX_WM_DRAW2D, &CChildView::OnAfxDraw2D)
    END_MESSAGE_MAP()
    
  3. Finally, get the CDCRenderTarget* passed by MFC framework in LPARAM and enjoy.
    LRESULT CChildView::OnAfxDraw2D(WPARAM wParam, LPARAM lParam)
    {
        CDCRenderTarget* pDCRenderTarget = (CDCRenderTarget*)lParam;
        ASSERT_KINDOF(CDCRenderTarget, pDCRenderTarget);
    
        // Draw Direct2D content in GDI device context
        _DrawDirect2DContent(pDCRenderTarget);
    
        pDCRenderTarget->EndDraw();
    
        // Draw using GDI
        CWindowDC dc(this);
        _DrawGDIContent(&dc);
    
        return TRUE;
    }
    

Drawing GDI content to a Direct2D GDI-compatible render target

So far, MFC has not a wrapper class for ID2D1GdiInteropRenderTarget interface. Also, CWnd::EnableD2DSupport creates a CHwndRenderTarget which is not GDI-compatible. So, we must do it ourselves. Here is the sample code:

class CChildView : public CWnd
{
    // ...
    CComQIPtr<ID2D1GdiInteropRenderTarget> m_spGdiInteropRenderTarget;
    // ...
    afx_msg LRESULT OnAfxDraw2D(WPARAM wParam, LPARAM lParam);
};
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (__super::OnCreate(lpCreateStruct) == -1)
        return -1;

    // get global Direct2D factory
    ID2D1Factory* pDirect2DFactory = AfxGetD2DState()->GetDirect2dFactory();

    // create GDI-compatible window render target
    D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
    rtProps.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
    rtProps.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
    rtProps.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;

    ID2D1HwndRenderTarget* pHwndRenderTarget = NULL;
    HRESULT hr = pDirect2DFactory->CreateHwndRenderTarget(
        &rtProps,
        &D2D1::HwndRenderTargetProperties(m_hWnd, CD2DSizeU()),
        &pHwndRenderTarget);
    if (FAILED(hr))
        return -1;

    // instantiate new CHwndRenderTarget and attach the GDI-compatible render target
    m_pRenderTarget = new CHwndRenderTarget;
    GetRenderTarget()->Attach(pHwndRenderTarget);

    // create ID2D1GdiInteropRenderTarget instance
    m_spGdiInteropRenderTarget = pHwndRenderTarget;
    if (!m_spGdiInteropRenderTarget)
        return -1;

    return 0;
}
LRESULT CChildView::OnAfxDraw2D(WPARAM wParam, LPARAM lParam)
{
    CHwndRenderTarget* pHwndRenderTarget = (CHwndRenderTarget*)lParam;
    ASSERT_VALID(pHwndRenderTarget);

    // draw using Direct2D
    _DrawDirect2DContent(pHwndRenderTarget);

    HDC hDC = NULL;
    HRESULT hr = m_spGdiInteropRenderTarget->GetDC(
        D2D1_DC_INITIALIZE_MODE_COPY, &hDC);

    if (FAILED(hr) || (NULL == hDC))
        return FALSE;

    // draw GDI content in a GDI-compatible window render target
    _DrawGDIContent(CDC::FromHandle(hDC));

    CRect rcClient;
    GetClientRect(rcClient);
    hr = m_spGdiInteropRenderTarget->ReleaseDC(rcClient);
    if (FAILED(hr))
        return FALSE;

    return TRUE;
}

Demo projects

Download: Direct2D and GDI Interop Demo.zip (1390 downloads)

The Visual C++ solution attached here has two simple projects showing the both Direct2D and GDI interoperatibily methods listed above.

Direct2D and GDI Interop - Demo Application
Direct2D and GDI Interop – Demo Application

 

Notes

  • can be observed that Direct2D drawing quality is higher than GDI drawing, because Direct2D is capable of rendering with antialiasing;
  • given the first note, my opinion is that GDI drawing has no much sense once using Direct2D but never know…

Resources and related articles

3 thoughts on “MFC Support for Direct2D – Interoperability with GDI”

    • m_pRenderTarget is a protected member of CWnd.
      You can press F12 to see where it is defined or right-click and choose Peek definition/go to definition.

      Reply

Leave a Comment