Tom's Blog

Musings on Windows, Graphics, and Technology

Using Direct2D

Using Direct2D

  • Comments 23
  • Likes

In today's post, we're going to walk through a simple demonstration of Direct2D. We're not going to cover advanced features, such as interop with GDI/GDI+ or Direct3D. More on that in upcoming posts.

 

Direct2D integrates seamlessly into the familiar Win32 programming paradigm, and follows a usage pattern which is similar to Direct3D. First, you create a factory...

 

 

ID2D1FactoryPtr m_spD2DFactory;

 

HRESULT hr = D2D1CreateFactory(

    D2D1_FACTORY_TYPE_SINGLE_THREADED,

    &m_spD2DFactory

    );

 

 

Next, you use the factory to create resources that you need.  One important distinction from GDI/GDI+ is that, since Direct2D is a lower-level API (like Direct3D), you need to be aware that some resources (eg. render targets, bitmaps, brushes, gradient stop collections, layers, etc) have a close association with the device on which they were created, while others (eg. geometries, meshes, stroke style, geometry sinks, tessellation sinks, etc) are not associated with a device.  Also, like Direct3D, device-dependent resources need to be recreated in cases where the device is lost or undergoes a state change. Okay, now let's create a simple geometry. Geometries are examples of device-independent resources which can be used with any render target.

 

   

ID2D1RectangleGeometryPtr m_spRectangleGeometry;

 

IFR(m_spD2DFactory->CreateRectangleGeometry(

            D2D1::Rect(20.f, 20.f, 30.f, 50.f),           

            &m_spRectangleGeometry));

 

 

Before we can draw this geometry, we're going to need a few other things. Let's create a render target . Direct2D will attempt to create a hardware render target and, if hardware isn't available, it will fall back to a software render target. We need to create a red brush to draw the geometry. Also, we're going to use Windows Imaging Codecs (WIC) to load an image from disk, convert it to 32bppPBGRA, and then create a Direct2D bitmap from it. Note that brushes and bitmaps are device-dependent resources that are associated with a render target.

 

 

HRESULT Application::CreateDeviceResources()

{

   HRESULT hr = S_OK;

 

   if (!m_spRT)

   {

       IWICImagingFactoryPtr spWICFactory;

       IWICBitmapDecoderPtr spDecoder;

       IWICFormatConverterPtr spConverter;

 

       RECT rc;

       GetClientRect(

           m_hwnd,

           &rc);

 

       D2D1_SIZE_U size = D2D1::SizeU(

           rc.right - rc.left,

           rc.bottom - rc.top);

 

       //create a D2D render target

       hr = spD2DFactory->CreateHwndRenderTarget(

           D2D1::RenderTargetProperties(),

           D2D1::HwndRenderTargetProperties(

               hwnd,

               size),

   &spRT);

 

//create a red brush

IFR(spRT->CreateSolidColorBrush(

    D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f),

    &spRedBrush));

 

//create WIC factory

IFR(CoCreateInstance(

    CLSID_WICImagingFactory,

    NULL,

    CLSCTX_INPROC_SERVER,

    IID_IWICImagingFactory,

    reinterpret_cast<void **>(&spWICFactory)

    ));

 

//load image using WIC

IFR(spWICFactory->CreateDecoderFromFilename(

    L"tiger.jpg",

    NULL,

    GENERIC_READ,

    WICDecodeMetadataCacheOnLoad,

    &spDecoder));

 

//get the initial frame

IFR(spDecoder->GetFrame(

    0,

    &spSource));

 

//format convert to 32bppPBGRA -- which D2D expects

IFR(spWICFactory->CreateFormatConverter(

    &spConverter));

 

//initialize the format converter

IFR(spConverter->Initialize(

    spSource,

    GUID_WICPixelFormat32bppPBGRA,

    WICBitmapDitherTypeNone,

    NULL,

    0.f,

    WICBitmapPaletteTypeMedianCut));

 

//create a D2D bitmap from the WIC bitmap.

IFR(spRT->CreateBitmapFromWicBitmap(

    spConverter,

    NULL,

    &m_spBitmap));

}

return S_OK;

}

 

At this point, we have the basic resources that we need to draw. What we need is a place to do it. Note that we're drawing in response to a WM_PAINT message, but we aren't using a GDI HDC at all.

 

   

        case WM_PAINT:

        case WM_DISPLAYCHANGE:

            {

                PAINTSTRUCT ps;

                BeginPaint(hwnd, &ps);

 

                OnRender(ps.rcPaint);

 

                EndPaint(hwnd, &ps);

            }

 

 

Here is our render function. You've already seen the CreateDeviceResources function above. This is where you create any device-dependent resources. Note that creation of device resources is only done once. After a render target and its associated resources have been created, CreateDeviceResources does nothing. Our render function checks to see whether the render target is occluded (aka covered). This is an optimization which prevents unnecessary drawing in cases where the window is hidden from view. We call BeginDraw on the render target to initiate drawing. All drawing instructions must be bracketed between BeginDraw and EndDraw calls.

 

Next, we set an identity transform, which means that anything drawn will be relative to the origin in the top left hand corner of the render target. Next, we clear the render target with a white color. You need to clear the target; otherwise, the render target will be initialized with the content from the previous drawing operations. If none have been performed yet, the result is undefined. We draw a bitmap, and then set a transform and draw our red rectangle geometry. We call EndDraw on the render target to signify that drawing operations are complete. Finally,we check the return code from EndDraw to determine whether there was any kind of failure condition.

 

 

HRESULT Application::OnRender(const RECT &rcPaint)

{

  HRESULT hr = S_OK;

 

  //this is where we create device resources if they don't already

  //exist (eg. m_spRT, m_spBitmap)

 

  IFR(CreateDeviceResources());

 

  if (!(m_spRT->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))

  {

    m_spRT->BeginDraw();

 

    m_spRT->SetTransform(D2D1::Matrix3x2F::Identity());

 

    m_spRT->Clear(D2D1::ColorF(D2D1::ColorF::White));

 

    D2D1_SIZE_F size = m_spBitmap->GetSize();

 

    m_spRT->DrawBitmap(

       m_spBitmap,

       D2D1::Rect<float>(0.0f, 0.0f, size.width, size.height));

 

    m_spRT->SetTransform(

        D2D1::Matrix3x2F::Translation(rtSize.width - 200, 0));

 

    m_spRT->FillGeometry(

        m_spRectangleGeometry,

        m_spRedBrush);

 

    hr = m_spRT->EndDraw();

}

 

if (hr == D2DERR_RECREATE_TARGET)

{

    //if the device is lost, we need to discard all of the resources

    //associated with that device (eg. m_spRT, m_spBitmap, etc). We will

    //recreate the next time we need to paint

    DiscardDeviceResources();

}

 

return hr;

}    

 

As mentioned above, Direct2D is a lower-level API, and there are scenarios under which the display device can be lost (eg. adapter removed, display resolution changed, etc). GDI handles these device lost scenarios transparently but, with Direct2D (as with Direct3D), you need to be aware of and handle these conditions. If the device is lost for any reason, Direct2D will let you know, and you should free any  resources that are associated with that render target. Keep in mind that you don't have to release device-independent resources (eg. m_spRectangleGeometry).

  

 

void Application::DiscardDeviceResources()

{

m_spRT.Release();

m_spBitmap.Release();

m_spRedBrush.Release();

}

    

 

 

Render Target Interfaces

 

 

 

Device-Independent Resource Interfaces

 

 

 

 

Device-Dependent Resource Interfaces

 

Comments
  • Do you support other pixel formats than 32bppPBGRA (especially high dynamic range ones, e.g. 128bppPRGBAFloat) for bitmap render targets?

  • Does anyone where/whether the SDK with the above D2D bits is available for download?

    Or will it be available later once the W7 Beta comes out?

    Thanks

  • Do you support other pixel formats than 32bppPBGRA (especially high dynamic range ones, e.g. 128bppPRGBAFloat) for bitmap render targets?

    Lionel:

    Unfortunately, we do not support any high dynamic range formats. We're looking into adding this support for our next version (Windows 8).

    We do support a few other pixel formats. D2D pixel formats consist of a DXGI_FORMAT and a D2D1_ALPHA_MODE. We support

    1. (DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)

    2. (DXGI_FORMAT_B8G8R8A8_UNORM,

    D2D1_ALPHA_MODE_IGNORE)

    3. (DXGI_FORMAT_A8_UNORM,

    D2D1_ALPHA_MODE_STRAIGHT)

    4. (DXGI_FORMAT_A8_UNORM,

    D2D1_ALPHA_MODE_PREMULTIPLIED) (same meaning as 3.)

    5. (DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_IGNORE) (only supported on D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING targets)

    6. 5. (DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED) (only supported on D2D1_RENDER_TARGET_USAGE_FORCE_HARDWARE_RENDERING targets)

  • Does anyone where/whether the SDK with the above D2D bits is available for download? Or will it be available later once the W7 Beta comes out?

    It will be available with the Windows 7 Beta.

  • Direct2D is a 2D rendering API built on top of Direct3D. You have to either implement your own windowless controls using D2D, or use the standard USER32 windowed controls (e.g. button, edit box, listview, etc). It has always been possible to overlay a Win32 HWND over a Direct3D swap chain (and, by extension, a D2D render target). What you need to do is create your primary application window (m_hwnd, below) with WS_CLIPCHILDREN style and, then, when you need to pop up, say, an edit control, do the following:

     

            // Create the window for the edit control window.

            #define ID_EDIT 1000

     

            DWORD dwWindowStyle =

                WS_CHILD | WS_VISIBLE |

                ES_LEFT | ES_NOHIDESEL | ES_AUTOHSCROLL | ES_AUTOVSCROLL;

     

            HWND hwndEdit = CreateWindow(

                    TEXT("edit"),   // Class name

                    NULL,           // Window text

                    dwWindowStyle,  // Window style

                    0,              // x-coordinate of the upper-left corner

                    0,              // y-coordinate of the upper-left corner

                    400,            // Width of the window for the edit control

                    20,             // Height of the window for the edit control

                    m_hwnd,         // Parent window

                    (HMENU)ID_EDIT, // Control identifier

                    HINST_THISCOMPONENT, // Instance handle

                    NULL);

     

  • Wow! Some actual use of WIC..but hey wait there are no codecs except RAW codecs. Can Microsoft fix this and write some WIC codecs for more formats? Why not buy a company like Leadtools and end the format issue once and for all?

  • hi,where and how to get the direct2d sdk?

    You can get Direct2D and DirectWrite with the Windows 7 Beta. See http://msdn.microsoft.com/en-us/evalcenter/dd353271.aspx

     And here is the Windows 7 Beta SDK (which contains Direct2D libs/headers/etc): http://www.microsoft.com/downloads/details.aspx?FamilyID=a91dc12a-fc94-4027-b67e-46bab7c5226c&DisplayLang=en

     

  • Just to be clear - is Windows 7 a requirement, or is that just the distribution channel?  

    If I needed something that works with Windows Server 2003 to do fast 2D image work, what would the best choices be?  Would going to Windows Server 2008 provide any additional options?

  • Just to be clear - is Windows 7 a requirement, or is that just the distribution channel?  

    If I needed something that works with Windows Server 2003 to do fast 2D image work, what would the best choices be?  Would going to Windows Server 2008 provide any additional options?

  • Just to be clear - is Windows 7 a requirement, or is that just the distribution channel?  

    Direct2D will ship on both Windows 7 and Windows Vista. 

    If I needed something that works with Windows Server 2003 to do fast 2D image work, what would the best choices be?  

    By "fast 2D image work", I assume you mean "fast render 2D content to an image". On Windows Server 2003, your option is primarily GDI+, or GDI and Windows Imaging Codecs (WIC).

    Would going to Windows Server 2008 provide any additional options?

    No. Microsoft has not announced plans to ship Direct2D outside of Windows 7 and Vista.

  • Is really Direct2D easier than Direct3D? they don't think so... http://braid-game.com/news/?p=466

  • Is really Direct2D easier than Direct3D? they don't think so... http://braid-game.com/news/?p=466

    Direct2D is a general-purpose 2D rendering API. It isn't solely a "rectangle-drawing API". If you want a more honest comparison, try a counter-example which draws antialiased ClearType text, buttons, lists, and most other common UI elements solely with Direct3D -- and then tell me whether you think it's easier than Direct2D. I don't think so.

    Furthermore, if you're going to use lines of code as the sole metric for comparing code, then it would be more honest to include fundamental things such as error-handling (eg. device loss, allocation failures, etc), comments, cleanup of resources upon completion, and other common elements that professional developers wouldn't consider optional.

  • Hello!

    In the demonstration code, BeginPaint is used on WM_DISPLAYCHANGE. I know the description about BeginPaint in MSDN library: “An application should not call BeginPaint except in response to a WM_PAINT message.”

    http://msdn.microsoft.com/en-us/library/dd183362.aspx

    In windows 7, BeginPaint on WM_DISPLAYCHANGE is allowed officially?

    Thank you!

  • Where can I find a sample with how to use WIC with GDI?

  • There are many ways to do this, there is a good posting here

    http://www.mvps.org/user32/gditutorial.html

    that shows how to create a dib section and render a bitmap with GDI.

    A good way to go about this is to decode the image with WIC. Here is good overview:

    http://msdn.microsoft.com/en-us/library/ms737413(VS.85).aspx

    Once you have an IWICBitmapSource you can call CopyPixels on it and copy it into a dib buffer. Then you can basically bitblt it your hdc.

    Here are the steps:

    Decode the image using WIC:

    1. Create a WIC Factory

    2. Create a decoder from filename

    3. Get a particular frame by calling get frame

    4. Format convert into the correct pixel format - 32 BGR works well with GDI.

    5. Query Interface for an IWICBitmapSource.

    Setup a GDI DIB and draw to the screen. Much this is shown in tutorial four in the link above.

    1. Create a hBitmap by calling CreateDIBSection.  http://msdn.microsoft.com/en-us/library/dd183494(VS.85).aspx

    2. CopyPixels from the IWICBitmapSource into the buffer used to create the dib section.

    3. In your WM_PAINT message handler you should create an hdc from CreateCompatibleDC.

    4. Then associate the hBitmap to the DC you just created - call SelectBitmap.

    5. Then you should copy the contents of the DC you created to the dc associated with the m_hWnd. Call BitBlt with the appropriate parameters.

    Be sure to clean up your resources.

    I hope this helps.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment