Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / Win32

Direct2D: Hardware Acceleration in Windows 7 Plotting

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
10 May 2009CPOL3 min read 103.4K   1.9K   40   23
Draw your vectors using a new, ActiveX based hardware accelerated interface.

Introduction

GDI+ has many features all right, but it is somewhat slow. Microsoft introduced Direct2D in Windows 7, a featured hardware-accelerated API that you can now use for applications. My first usage of Direct2D was in my Turbo Play project, which without it takes 28ms to draw screen. With it, it takes < 1 ms!

The good thing about Direct2D is that it can be easily combined with GDI, GDI+, or Direct3D.

You Need

Overview

Direct2D is an ActiveX object that can draw to an HWND or to an HDC. For this to work, the steps are:

  • Instantiate an ID2D1Factory by creating D2D1CreateFactory() from D2D1.DLL (I suggest you link to this dynamically using LoadLibrary/GetProcAddress so your code runs in any Windows version).
  • Call ID2D1Factory :: CreateHwndRenderTarget() to target an HWND, or call ID2D1Factory :: CreateDCRenderTarget() to target an HDC.
  • Use the returned ID2D1RenderTarget interface to draw:
    • Call ID2D1RenderTarget::BeginDraw()
    • Draw stuff
    • Call ID2D1RenderTarget::EndDraw()

Selecting a Brush to Draw

The following function creates a Direct2D Brush from a given ARGB color:

C++
ID2D1SolidColorBrush* GetD2SolidBrush(ID2D1RenderTarget* pTR,unsigned long c)
{
    if (!pRT)
        return 0;
    ID2D1SolidColorBrush* b = 0;
    D2D1_COLOR_F cc;
    cc.a = GetAValue(c)/255.0f;
    if (cc.a == 0)
        cc.a = 1.0f; 
    cc.r = GetRValue(c)/255.0f;
    cc.g = GetGValue(c)/255.0f;
    cc.b = GetBValue(c)/255.0f;
    pRT->CreateSolidColorBrush(cc,&b);
    return b;
}

Draw Lines, Rectangles, Ellipses

Use the functions exported from ID2D1RenderTarget, like DrawEllipse(), DrawRectangle(), DrawLine(), FillEllipse(), and FillRectangle().

Draw Polygons

The following function demonstrates how to create a polygon from a given set of POINT*s. You simply create a path geometry (ID2DFactory :: CreatePathGeometry()), open it (ID2D1PathGeometry::Open()) to get its sink, call ID2D1GeometrySink ::BeginFigure, add items (Line, Lines, Bezier curves etc.), call ID2D1GeometrySink::EndFigure(), then ID2D1PathGeometry::Close(), and finally, you pass the geometry object to ID2D1RenderTarget::DrawGeometry.

C++
void Polygon(POINT*p,int n,bool Close)
{    
    // Convert POINT to D2D1_POINT_2F
    D2D1_POINT_2F* pt =  new D2D1_POINT_2F[n];
    for(int i = 0 ; i < n ; i++)
    {
        pt[i].x = (FLOAT)p[i].x;
        pt[i].y = (FLOAT)p[i].y;
    }
 
    ID2D1SolidColorBrush* b = GetD2SolidBrush(c);
    ID2D1PathGeometry* pg = 0;
    ID2D1GeometrySink* pgs = 0;
    pD2DFactory->CreatePathGeometry(&pg);
    if (pg)
    {
        pg->Open(&pgs);
        if (pgs)
        {
            D2D1_POINT_2F fb;
            fb.x = (FLOAT)pt[0].x;
            fb.y = (FLOAT)pt[0].y;
            // Use D2D1_FIGURE_BEGIN_FILLED for filled
            D2D1_FIGURE_BEGIN fg = D2D1_FIGURE_BEGIN_HOLLOW;
            D2D1_FIGURE_END fe;
            if (Close)
                fe = D2D1_FIGURE_END_CLOSED;
            else 
                fe = D2D1_FIGURE_END_OPEN;
            pgs->BeginFigure(fb,fg);
            for(int i = 1 ; i < n ; i++)
            {
                D2D1_POINT_2F fu;
                fu.x = pt[i].x;
                fu.y = pt[i].y;
                pgs->AddLine(fu);
            }
            pgs->EndFigure(fe);
            pgs->Close();
            pgs->Release();
        }
        if (b)
            pRT->DrawGeometry(pg,b,1);
        pg->Release();
        if (b)
            b->Release();
        delete[] pt;
    }

Draw Images

Direct2D loads a bitmap from a Windows Imaging component. The simple function below will use an existing HBITMAP (which must be a 32-bit one!) to draw:

C++
void Image(int x1,int y1,HBITMAP hB,float Op = 1.0f);
{
    BITMAP bo;
    GetObject(hB,sizeof(bo),&bo);
    WICBitmapAlphaChannelOption ao = WICBitmapUseAlpha;
    IWICBitmap* wb = 0;
    pImageFactory->CreateBitmapFromHBITMAP(hB,0,ao,&wb);
    if (!wb)
        return;
    ID2D1Bitmap* b = 0;
    pRT->CreateBitmapFromWicBitmap(wb,0,&b);
    if (!b)
    {
        // Convert it
        IWICFormatConverter* spConverter = 0;
        pImageFactory->CreateFormatConverter(&spConverter);
        if (spConverter)
        {
            spConverter->Initialize(wb,GUID_WICPixelFormat32bppPBGRA, 
              WICBitmapDitherTypeNone,NULL,0.f, 
              WICBitmapPaletteTypeMedianCut);
            pRT->CreateBitmapFromWicBitmap(spConverter,0,&b);
            spConverter->Release();
        }
        wb->Release();
    }
    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();
    }
}

Draw Text

Text in Direct2D is written through DirectWrite (to be continued...), another new Direct* API in Windows 7. The steps are:

  • Have the IDWriteFactory (DWriteCreateFactory from DWRITE.DLL).
  • Set the font by calling IDWriteFactory::CreateTextFormat() with font parameters, to get the IDWriteTextFormat*. (See my SetFont() function.)
  • Call IDWriteTextFormat members to set the format - my code calls SetTextAlignment/SetParagraphAlignment to set the alignment.
  • Call ID2D1RenderTarget::DrawText.

To measure the text dimensions, you have to create IDWriteTextLayout* (IDWriteFactory::CreateTextLayout) which represents the formatted text's attributes (for more, see my TextSize() function).

The Code

The code is part of my cross-platform drawing library, which is not complete (GDI/GDI+ stuff has been stripped from it to focus on the D2D code). You can use the mydraw.h functions:

C++
// Members
void Line(int x1,int y1,int x2,int y2,int th = 1,unsigned long c = AXRGB(0xFF,0,0,0), 
          unsigned int PenStyle = PS_SOLID);
void Rect(RECT&ar,int th = 1,unsigned long c = AXRGB(0xFF,0,0,0), 
          unsigned int PenStyle = PS_SOLID,bool Elp = false);
void FilledRect(RECT&ar,unsigned long c = AXRGB(0xFF,0,0,0),bool Elp = false);
void Polygon(POINT*p,int n,bool Close,int th = 1, 
             unsigned long c = AXRGB(0xFF,0,0,0), 
             unsigned int PenStyle = PS_SOLID);
void FilledPolygon(POINT*p,int n,bool Close, 
                   unsigned long c = AXRGB(0xFF,0,0,0));
void Ellipse(RECT&ar,int th = 1,unsigned long c = AXRGB(0xFF,0,0,0), 
             unsigned int PenStyle = PS_SOLID);
void FilledEllipse(RECT&ar,unsigned long c = AXRGB(0xFF,0,0,0));
void Rect(int x1,int y1,int wi,int he,int th = 1, 
          unsigned long c = AXRGB(0xFF,0,0,0), 
          unsigned int PenStyle = PS_SOLID,bool Elp = false);
void Ellipse(int x1,int y1,int wi,int he,int th = 1, 
             unsigned long c = AXRGB(0xFF,0,0,0), 
             unsigned int PenStyle = PS_SOLID);
void FilledRect(int x1,int y1,int wi,int he, 
                unsigned long c = AXRGB(0xFF,0,0,0),bool Elp = false);
void FilledEllipse(int x1,int y1,int wi,int he, 
                   unsigned long c = AXRGB(0xFF,0,0,0));
unsigned long TextSize(const wchar_t* txt,int l, 
                       unsigned long al,unsigned long lal);
void DrawText(const wchar_t* txt,int l,int x,int y,int wi,int he, 
              unsigned long al,unsigned long lal, 
              unsigned long c = AXRGB(0xFF,0,0,0),int BreakMode = 1);
void DrawText(const wchar_t* txt,int l,RECT&,unsigned long al, 
              unsigned long lal,unsigned long c = AXRGB(0xFF,0,0,0), 
              int BreakMode = 1);
void SetFont(HFONT hF1);
void Image(int x1,int y1,LPWSTR fil,float Op = 1.0f);
void Image(int x1,int y1,HINSTANCE h,LPWSTR n, 
           LPWSTR typ = RT_BITMAP,float Op = 1.0f);
void Image(int x1,int y1,HBITMAP hB,float Op = 1.0f, bool HasAlpha = 0);
void Image(int x1,int y1,Gdiplus::Bitmap* b,bool HasAlpha = 0);
HRESULT LoadResourceImage(HINSTANCE h,PCWSTR resourceName, 
        PCWSTR resourceType,void**ppBitmap);
HRESULT LoadFileImage(const LPWSTR f,void**ppBitmap);

to draw stuff. Load test32.sln and compile it, and select the drawing mode from the menu. A performance counter is also included.

Todo...

  • Add interoperability between Direct2D, Direct3D , GDI+, and GDI
  • Add brush strokes
  • Add complex geometry

History

  • 08 - 5 - 2009: Update for RC1.
  • 25 - 4 - 2009: First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Greece Greece
I'm working in C++, PHP , Java, Windows, iOS, Android and Web (HTML/Javascript/CSS).

I 've a PhD in Digital Signal Processing and Artificial Intelligence and I specialize in Pro Audio and AI applications.

My home page: https://www.turbo-play.com

Comments and Discussions

 
QuestionDirect2D isn't rendering your IMG Pin
Sukar7-Jun-12 8:56
Sukar7-Jun-12 8:56 
AnswerRe: Direct2D isn't rendering your IMG Pin
Michael Chourdakis7-Jun-12 10:04
mvaMichael Chourdakis7-Jun-12 10:04 
Questiondoes it work just on windows 7? Pin
druscelli7-May-12 0:00
druscelli7-May-12 0:00 
AnswerRe: does it work just on windows 7? Pin
Michael Chourdakis7-May-12 0:08
mvaMichael Chourdakis7-May-12 0:08 
GeneralRe: does it work just on windows 7? Pin
druscelli7-May-12 0:12
druscelli7-May-12 0:12 
GeneralRe: does it work just on windows 7? Pin
Michael Chourdakis7-May-12 0:16
mvaMichael Chourdakis7-May-12 0:16 
GeneralRe: does it work just on windows 7? Pin
druscelli7-May-12 0:52
druscelli7-May-12 0:52 
QuestionAre you sure about this? Pin
maryanny4-Dec-09 7:28
maryanny4-Dec-09 7:28 
QuestionHow to add Textbox? Pin
cloudking119665-Aug-09 1:33
cloudking119665-Aug-09 1:33 
AnswerRe: How to add Textbox? Pin
Michael Chourdakis5-Aug-09 2:39
mvaMichael Chourdakis5-Aug-09 2:39 
GeneralRe: How to add Textbox? Pin
cloudking119665-Aug-09 23:45
cloudking119665-Aug-09 23:45 
Generalmain.rc(10): error RC2135: file not found: x86.manifest Pin
tyjiang11-Jul-09 15:07
tyjiang11-Jul-09 15:07 
GeneralRe: main.rc(10): error RC2135: file not found: x86.manifest Pin
Michael Chourdakis12-Jul-09 6:07
mvaMichael Chourdakis12-Jul-09 6:07 
GeneralRe: main.rc(10): error RC2135: file not found: x86.manifest ---- This content has been removed. Pin
Jo su hyeon30-Jan-12 19:07
Jo su hyeon30-Jan-12 19:07 
GeneralRe: main.rc(10): error RC2135: file not found: x86.manifest ---- This content has been removed. Pin
Michael Chourdakis30-Jan-12 20:00
mvaMichael Chourdakis30-Jan-12 20:00 
QuestionWindows 7 Pin
Paul Selormey10-May-09 16:25
Paul Selormey10-May-09 16:25 
AnswerRe: Windows 7 Pin
Paul Selormey11-May-09 15:21
Paul Selormey11-May-09 15:21 
GeneralRe: Windows 7 Pin
Michael Chourdakis11-May-09 20:27
mvaMichael Chourdakis11-May-09 20:27 
GeneralRe: Windows 7 PinPopular
Paul Selormey11-May-09 20:50
Paul Selormey11-May-09 20:50 
GeneralRe: Windows 7 Pin
Michael Chourdakis12-May-09 2:11
mvaMichael Chourdakis12-May-09 2:11 
Well OK, but now it is 2009 and besides, my article is scientific, not economic. I am trying to make available to everyone interested what Windows 7 has to offer, but whether someone wants to advance to today's technologies or not is not an issue. MS is interested in money, and if the Direct2D is also made available in Windows 2000, no body will buy Windows 7 and we won't have anything or anyone to teach about it.

Best Regards.
GeneralRe: Windows 7 Pin
Paul Selormey12-May-09 2:37
Paul Selormey12-May-09 2:37 
GeneralRe: Windows 7 PinPopular
Michael Chourdakis12-May-09 5:24
mvaMichael Chourdakis12-May-09 5:24 
GeneralRe: Windows 7 Pin
Paul Selormey12-May-09 9:38
Paul Selormey12-May-09 9:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.