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

  • Anonymous
    January 01, 2003
    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.

  • Anonymous
    January 01, 2003
    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.

  • Anonymous
    January 01, 2003
    The comment has been removed

  • Anonymous
    January 01, 2003
    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.

  • Anonymous
    January 01, 2003
    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

  • Anonymous
    January 01, 2003
    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?

  • Anonymous
    November 04, 2008
    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

  • Anonymous
    November 04, 2008
    The comment has been removed

  • Anonymous
    November 04, 2008
    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);  

  • Anonymous
    January 01, 2009
    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?

  • Anonymous
    January 13, 2009
    hi,where and how to get the direct2d sdk? You can get Direct2D and DirectWrite with the Windows 7 Beta. Seehttp://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  

  • Anonymous
    January 26, 2009
    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?

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

  • Anonymous
    February 14, 2009
    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!

  • Anonymous
    March 11, 2009
    Where can I find a sample with how to use WIC with GDI?

  • Anonymous
    April 08, 2009
    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.
  6. Create a hBitmap by calling CreateDIBSection.  http://msdn.microsoft.com/en-us/library/dd183494(VS.85).aspx
  7. CopyPixels from the IWICBitmapSource into the buffer used to create the dib section.
  8. In your WM_PAINT message handler you should create an hdc from CreateCompatibleDC.
  9. Then associate the hBitmap to the DC you just created - call SelectBitmap.
  10. 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.
  • Anonymous
    April 24, 2009
    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 IWICFormatConverterspConverter = 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.

  • Anonymous
    April 24, 2009
    After some testing, I saw that it is the WIC fault that creates a bad bitmap... Any clues ?

  • Anonymous
    April 24, 2009
    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)

  • Anonymous
    April 26, 2009
    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?
  • Anonymous
    March 21, 2010
    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

  • Anonymous
    September 01, 2010
    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?

  • Anonymous
    July 27, 2011
    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?