Using Direct2D





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

  1. Thomas.Olsen says:

    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.

  2. Thomas.Olsen says:

    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.

  3. Lionel Fourquaux says:

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

  4. Thomas.Olsen says:

    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.

  5. Anonymous says:

    Could you please answer my question related to Direct2D which is available here (with proper formatting)

    http://stackoverflow.com/questions/24860870/direct2d-preserve-the-existing-content-and-overwrite-the-new-values

  6. Thomas.Olsen says:

    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?

  7. Adrian S says:

    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

  8. Tom Mulcahy says:

    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)

  9. Martin says:

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

     

  10. anonymous says:

    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?

  11. vvu says:

    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

     

  12. newbie says:

    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?

  13. cprieto says:

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

  14. Egtra says:

    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!

  15. Te-jé says:

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

  16. Varun Bhartia says:

    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.

  17. ML49448 says:

    Hi, I ‘ve been using it successfully except I cannot draw a bitmap. My code is this:

    void DrawImage(int x1,int y1,HBITMAP hB,float Op)

    {

    BITMAP bo;

    GetObject(hB,sizeof(bo),&bo);

    WICBitmapAlphaChannelOption ao = WICBitmapUseAlpha;

    IWICBitmap* wb = 0;

    pImageFactory->CreateBitmapFromHBITMAP(hB,(HPALETTE)GetStockObject(DEFAULT_PALETTE),ao,&wb);

    if (wb)

    {

    // Convert it

    IWICFormatConverter*spConverter = 0;

    pImageFactory->CreateFormatConverter(&spConverter);

    if (spConverter)

    {

    spConverter->Initialize(wb,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,NULL,0.f,WICBitmapPaletteTypeMedianCut);

    ID2D1Bitmap* b = 0;

    pRT->CreateBitmapFromWicBitmap(spConverter,0,&b);

    if (b)

    {

    D2D1_RECT_F r;

    r.left = (FLOAT)x1;

    r.top = (FLOAT)y1;

    r.right = (FLOAT)(x1 + bo.bmWidth);

    r.bottom = (FLOAT)(y1 + bo.bmHeight);

    pRT->DrawBitmap(b,r,Op);

    b->Release();

    }

    spConverter->Release();

    }

    wb->Release();

    }

    }

    }

    It just draws a bitmap with black and some blue noise.

  18. ML49448 says:

    After some testing, I saw that it is the WIC fault that creates a bad bitmap… Any clues ?

  19. scru says:

    Creating a button control atop a Window that has a HwndRenderTarget causes the button to leave temporary trails of itself and/or a black trail as the window is resized.

    Does anybody have any ideas on how to get around this? (Direct2D beta)

  20. Avi says:

    I am trying to decode jpeg frames and display it using hWnd render target

    I use the following scenario:

    1) create IWICStream  and initialize it with jpeg from memory

    2) Decode it using  factory standard jpeg decoder

    3) Get the frame from decoder and convert it to GUID_WICPixelFormat32bppBGRA (direct2d require)

    4) Calls render function DisplayBitmap to display the frame.

    It is actually show the frame on the screen but the performances are poor.

    Drawing one frame take about 30 – 80 mill second (most of it is convert and display functions)

    Any idea why its take too much time?

  21. Rob says:

    Hi,

    I’m using MFC to build a 2D type CAD application and wondering if D2D would be useful for this purpose. I need to setup a world coordinate system (in mils or mm) and then zoom, pan, scroll etc. Additionally my application needs to draw primitive objects (lines, rects, circles, arcs, polys, text) with the ability to rotate, mirror, etc. It is desirable to have all the drawing primitives support alpha blending for transparency also.

    Currently I’m using XP and GDI and tried GDI+ to get transparency. GDI+ seems slow and limited for serious CAD type applications.

    How can XOR type operations be achieved with D2D when a user drags an object, say a rectangle, around the screen also?

    Thanks!

    rob

  22. charles says:

    CreateBitmapFromWicBitmap currently supports only DXGI_FORAMT_B8G8R8A*_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED. I have a transparent bitmap that i want to convert to D2D bitmap , currently transparent in the original bitmap show up as black in the final output. Any idea?

  23. Bruce says:

    If I want to draw in a CPU memory ( No create the Direct2D from hwnd or render target surface),  does the Direct2D have below limitation?

    1.  texture size limitation (very large image size supported?)

    2.  the speed is low? If I want to get drawing geometries from GUP surface back to CPU memory?