Click here to Skip to main content
6,305,776 members and growing! (15,716 online)
Email Password   helpLost your password?
Multimedia » GDI+ » General     Intermediate

Drawing Speed in GDI+

By Alex Fr

This article examines GDI+ drawing speed
VC6Win2K, WTL, GDI+, Dev
Posted:9 Aug 2001
Views:165,446
Bookmarked:55 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
19 votes for this article.
Popularity: 5.05 Rating: 3.95 out of 5
1 vote, 6.7%
1
2 votes, 13.3%
2
1 vote, 6.7%
3
2 votes, 13.3%
4
9 votes, 60.0%
5

Introduction

GDI+ gives developers many advanced graphic functions and looks more friendly than GDI. I wanted to answer the question 'how fast is GDI+?' and 'what can we do to get smooth and fast redrawing?'.

To estimate drawing speed I checked the "Show window contents while dragging" in the Control Panel - Display Properties dialog, Effects tag. I then ran the program which looks like this:

Sample Image - GDIPlusSpeed.jpg

I made two tests

  1. resize window
  2. move some other window over tested window (for example, About box).

Redrawing should be both smooth and fast.

For the demo project I used Windows Template Library (WTL) which is available in the Microsoft Platform SDK. WTL is my preferable framework for writing small Windows programs without MFC. To compile the demo project add the WTL Include directory to the list of Visual C++ Include directories. For GDI+ use the Microsoft SDK Include\Prerelease and Lib\Prerelease directories.

The Drawing code is very simple - horizontal and vertical lines on white background. The Window doesn't erase the background itself and the client area is filled using GDI+. To get the exact time of redrawing I use my class CTimeCounter which prints to the  Debug stream the execution time of the various program sections.

I made four demo projects - from step 1 (drawing directly to window) to step 4 (using cashed bitmap). All projects are created using the WTL Application Wizard, SDI application without View class.

For each step I write the execution time. It depends on many factors, such as operating system, computer and graphic card speed, program configuration, window size etc., but I believe the proportions between execution speed on various steps will be the same in any case.

Code fragments and execution time

Step 1. Drawing directly to window

class CMainFrame : public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>, public CMessageFilter, public CIdleHandler { // ... LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/,
BOOL& /*bHandled*/) { // Initialize GDI+ Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL); // ... } // release GDI+ LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { Gdiplus::GdiplusShutdown(m_gdiplusToken); return 0; } // cause to redraw all client area while resizing LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { Invalidate(); return 0; } // don't redraw background LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled) { return 0; } // draw lines LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { SHOW_TIME_START(t, _T("OnPaint")); // use CTimeCounter to get
// execution time
CPaintDC dc(m_hWnd); Graphics graphics(dc.m_hDC); RECT rect; GetClientRect(&rect); int nWidth = rect.right - rect.left + 1; int nHeight = rect.bottom - rect.top + 1; // fill background SolidBrush solidBrush(Color(255, 255, 255)); graphics.FillRectangle(&solidBrush, 0, 0, nWidth, nHeight); Pen pen(Color(255, 0, 0, 255)); int nStep, i; int nLines = 20; // draw horizontal lines nStep = nHeight / nLines; for ( i = 0; i < nLines; i++ ) { graphics.DrawLine(&pen, 0, nStep*i, nWidth, nStep*i); } // draw vertical lines nStep = nWidth / nLines; for ( i = 0; i < nLines; i++ ) { graphics.DrawLine(&pen, nStep*i, 0, nStep*i, nHeight); } SHOW_TIME_END(t); // TRACE execution time here return 0; } protected: ULONG_PTR m_gdiplusToken; };
Execution time in Output window:
  • OnPaint 300 ms

Step 2 - Using off-screen graphics

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SHOW_TIME_START(t, _T("OnPaint"));

        CPaintDC dc(m_hWnd);

        RECT rect;
        GetClientRect(&rect);
        int nWidth = rect.right - rect.left + 1;
        int nHeight = rect.bottom - rect.top + 1;
        
        Graphics graphics(dc.m_hDC);

        SHOW_TIME_START(t1, _T("Prepare off-screen bitmap"));

        // memory bitmap 

        Bitmap* pMemBitmap = new Bitmap(nWidth, nHeight);

        // offscreen graphics

        Graphics* pMemGraphics = Graphics::FromImage(pMemBitmap);


        // draw to memory bitmap

        Draw(pMemGraphics, nWidth, nHeight);

        SHOW_TIME_END(t1);

        // draw from memory bitmap to window

        graphics.DrawImage(pMemBitmap, 0, 0);

        delete pMemGraphics;
        delete pMemBitmap;

        SHOW_TIME_END(t);

        return 0;
    }

    // draw lines to Graphics

    void Draw(Graphics* pGraphics, int nWidth, int nHeight)
    {
        // fill background

        SolidBrush solidBrush(Color(255, 255, 255));
        pGraphics->FillRectangle(&solidBrush, 0, 0, nWidth, nHeight);
        
        Pen pen(Color(255, 0, 0, 255));
        
        int nStep, i;
        int nLines = 20;
        
        // draw horizontal lines

        nStep = nHeight / nLines;
        
        for ( i = 0; i < nLines; i++ )
        {
            pGraphics->DrawLine(&pen, 0, nStep*i,  nWidth, nStep*i);
        }
        
        
        // draw vertical lines

        nStep = nWidth / nLines;
        
        for ( i = 0; i < nLines; i++ )
        {
            pGraphics->DrawLine(&pen, nStep*i, 0, nStep*i, nHeight);
        }
    }

Execution time in the Output window:
  • OnPaint 130 ms
  • Prepare off-screen bitmap 40 ms

Preparing the off-screen bitmap is part of the drawing. That means window redrawing takes 130 ms, 40 ms of them is preparing the off-screen bitmap.

Step 3 - Using off-screen graphics as class member

In this step there are two redrawing times. Full redrawing is done when the window is resizing. Redrawing without resizing takes less time, because the off-screen graphics with the required size is ready for use.

    CMainFrame()
    {
        m_pMemBitmap = NULL;
        m_pMemGraphics = NULL;
    }

    // clean-up operations

    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        // release off-screen bitmap and graphics

        if ( m_pMemGraphics )
            delete m_pMemGraphics;

        if ( m_pMemBitmap )
            delete m_pMemBitmap;

        // ...

    }

    // cause to redraw all window client area while resizing

    LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        Invalidate();
        SetDirty();
        return 0;
    }

    // cause to create offscreen graphics when window will be redrawn

    void SetDirty()
    {
        if ( m_pMemGraphics )
        {
            delete m_pMemGraphics;
            m_pMemGraphics = NULL;
        }

        if ( m_pMemBitmap )
        {
            delete m_pMemBitmap;
            m_pMemBitmap = NULL;
        }
    }

    // redraw window

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SHOW_TIME_START(t, _T("OnPaint"));

        CPaintDC dc(m_hWnd);

        RECT rect;
        GetClientRect(&rect);
        int nWidth = rect.right - rect.left + 1;
        int nHeight = rect.bottom - rect.top + 1;
        
        Graphics graphics(dc.m_hDC);

        if ( ! m_pMemBitmap )
        {
            SHOW_TIME_START(t1, _T("Prepare off-screen bitmap"));

            CreateOffScreeenGraphics(nWidth, nHeight);

            SHOW_TIME_END(t1);
        }

        // draw from memory bitmap to window

        graphics.DrawImage(m_pMemBitmap, 0, 0);


        SHOW_TIME_END(t);

        return 0;
    }

    // create off-screen graphics and draw to it

    void CreateOffScreeenGraphics(int nWidth, int nHeight)
    {
        // Create off-screen bitmap

        m_pMemBitmap =  new Bitmap(nWidth, nHeight);

        // Create off-screen graphics

        m_pMemGraphics = Graphics::FromImage(m_pMemBitmap);

        // draw to off-screen graphics

        Draw(m_pMemGraphics, nWidth, nHeight);
    }


    // draw lines to Graphics

    void Draw(Graphics* pGraphics, int nWidth, int nHeight)
    {
        // ...

    }

protected:
    ULONG_PTR m_gdiplusToken;
    Bitmap* m_pMemBitmap;
    Graphics* m_pMemGraphics;

    // ...



Execution time in the Output window
  • OnPaint 130 ms
  • Prepare off-screen bitmap 40 ms
  • Without resizing (draw from off-screen graphics to screen): OnPaint 70 ms

Step 4 - using CachedBitmap

While using a cashed bitmap we should be ready for the rare case when the Display Properties are changed. The Program checks the return value of DrawCachedBitmap function and creates the CachedBitmap object again if necessary.

    CMainFrame()
    {
        m_pCachedBitmap = NULL;
        // ...

    }

    // clean-up operations

    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        if ( m_pCachedBitmap )
            delete m_pCachedBitmap;

        // ...

    }

    // redraw window

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        SHOW_TIME_START(t, _T("OnPaint"));

        CPaintDC dc(m_hWnd);

        RECT rect;
        GetClientRect(&rect);
        int nWidth = rect.right - rect.left + 1;
        int nHeight = rect.bottom - rect.top + 1;
        
        Graphics graphics(dc.m_hDC);

        if ( ! m_pMemBitmap )
        {
            SHOW_TIME_START(t1, _T("Prepare off-screen bitmap"));

            CreateOffScreeenGraphics(nWidth, nHeight, &graphics);

            SHOW_TIME_END(t1);
        }

        // draw from cached bitmap to window

        if ( graphics.DrawCachedBitmap(m_pCachedBitmap, 0, 0) != Ok )
        {
            // make bitmap again (display parameters are changed)

            SetDirty();
            CreateOffScreeenGraphics(nWidth, nHeight, &graphics);
            graphics.DrawCachedBitmap(m_pCachedBitmap, 0, 0);
        }


        SHOW_TIME_END(t);

        return 0;
    }

    // create off-screen graphics and draw to it

    void CreateOffScreeenGraphics(int nWidth, int nHeight, Graphics* pGraphics)
    {
        // Create off-screen bitmap

        m_pMemBitmap =  new Bitmap(nWidth, nHeight);

        // Create off-screen graphics

        m_pMemGraphics = Graphics::FromImage(m_pMemBitmap);

        // draw to off-screen graphics

        Draw(m_pMemGraphics, nWidth, nHeight);

        // Create cashed bitmap

        m_pCachedBitmap = new CachedBitmap(m_pMemBitmap, pGraphics);

    }
    // draw lines to Graphics

    void Draw(Graphics* pGraphics, int nWidth, int nHeight)
    {
        // ...

    }

    // cause to create offscreen graphics when window will be redrawn

    void SetDirty()
    {
        // ...


        if ( m_pCachedBitmap )
        {
            delete m_pCachedBitmap;
            m_pCachedBitmap = NULL;
        }
    }

protected:
    CachedBitmap* m_pCachedBitmap;

    // ...

Execution time in Output window:
  • OnPaint 130 ms
  • Prepare off-screen bitmap 110 ms
  • Without resizing (draw from cached bitmap to screen): OnPaint 20 ms

Conclusion

Using CashedBitmap in GDI+ allows to get acceptable drawing speed. Maybe in Windows XP results will be better.

Notes

I didn't find in GDI+ Windows API functions like:
  • GetClipBox, which allows to minimize drawing operations;
  • GetBitmapBits and SetBitmapBits which are very useful in image processing.

Does anyone has some idea about this?

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Alex Fr


Member

Occupation: Software Developer
Location: Israel Israel

Other popular GDI+ articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 52 (Total in Forum: 52) (Refresh)FirstPrevNext
Generallaggard GDI+ PinmemberSeungwoo Oh17:42 3 May '08  
Generalwhere is "atlapp.h" PinmemberBrillianxe20:21 26 Dec '04  
GeneralRe: where is "atlapp.h" PinmemberBrillianxe20:34 26 Dec '04  
GeneralRe: where is "atlapp.h" PinsussAnonymous11:45 28 Jan '05  
GeneralStep 2's OnPaint time is slower than Step1? PinmemberKi C Jung11:52 15 Nov '04  
GeneralRe: Step 2's OnPaint time is slower than Step1? Pinmemberinshua16:59 17 Jul '07  
GeneralWorking with Controls PinsussAnonymous6:13 4 Aug '04  
GeneralResolution problem with offscreen method PinmemberDjauh4:45 22 Jun '04  
GeneralRe: Resolution problem with offscreen method PinmemberJorgen E.14:33 6 Jul '04  
GeneralRe: Resolution problem with offscreen method PinsussAnonymous6:01 4 Aug '04  
GeneralProgram Crash PinmemberJorgen E.7:12 23 May '04  
GeneralRe: Program Crash PinmemberAlex Farber21:05 23 May '04  
GeneralRe: Program Crash PinmemberJorgen E.9:53 24 May '04  
GeneralCreating Graphics Object Only Once ? PinsussAnonymous12:06 21 Apr '04  
GeneralRe: Creating Graphics Object Only Once ? PinmemberAlex Farber19:14 21 Apr '04  
Generalimage processing Pinsussprit8:21 14 Apr '03  
GeneralErasing drawing objects.. Pinmemberbaba19:12 25 Nov '02  
Generalwhere atlres.h ?? Pinmemberghj197620:08 17 Nov '02  
GeneralProblems with SelectObject PinsussPeter Schuhmann4:27 27 Jul '02  
GeneralOdd behaviour PinmemberBox20204:02 11 Jul '02  
GeneralRe: Odd behaviour PinmemberAlex Farber20:04 13 Jul '02  
GeneralRe: Odd behaviour PinsussAnonymous9:49 12 Jun '03  
GeneralGDI performance PinmemberAnonymous8:20 1 May '02  
GeneralRe: loading bitmap in CachedBitmap gobbles memory PinmemberAlex Farber3:38 7 Mar '02  
GeneralMemory leaks with CachedBitmap PinmemberNicolas Cadilhac11:33 21 Feb '02  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 9 Aug 2001
Editor: Chris Maunder
Copyright 2001 by Alex Fr
Everything else Copyright © CodeProject, 1999-2009
Web11 | Advertise on the Code Project