Skip to main content
Email Password   helpLost your password?

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:

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:

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

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:

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:

Does anyone has some idea about this?

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generallaggard GDI+ Pin
Seungwoo Oh
17:42 3 May '08  
Generalwhere is "atlapp.h" Pin
Brillianxe
20:21 26 Dec '04  
GeneralRe: where is "atlapp.h" Pin
Brillianxe
20:34 26 Dec '04  
GeneralRe: where is "atlapp.h" Pin
Anonymous
11:45 28 Jan '05  
GeneralStep 2's OnPaint time is slower than Step1? Pin
Ki C Jung
11:52 15 Nov '04  
GeneralRe: Step 2's OnPaint time is slower than Step1? Pin
inshua
16:59 17 Jul '07  
GeneralWorking with Controls Pin
Anonymous
6:13 4 Aug '04  
GeneralResolution problem with offscreen method Pin
Djauh
4:45 22 Jun '04  
GeneralRe: Resolution problem with offscreen method Pin
Jorgen E.
14:33 6 Jul '04  
GeneralRe: Resolution problem with offscreen method Pin
Anonymous
6:01 4 Aug '04  
GeneralProgram Crash Pin
Jorgen E.
7:12 23 May '04  
GeneralRe: Program Crash Pin
Alex Farber
21:05 23 May '04  
GeneralRe: Program Crash Pin
Jorgen E.
9:53 24 May '04  
GeneralCreating Graphics Object Only Once ? Pin
Anonymous
12:06 21 Apr '04  
GeneralRe: Creating Graphics Object Only Once ? Pin
Alex Farber
19:14 21 Apr '04  
Generalimage processing Pin
prit
8:21 14 Apr '03  
GeneralErasing drawing objects.. Pin
baba
19:12 25 Nov '02  
Generalwhere atlres.h ?? Pin
ghj1976
20:08 17 Nov '02  
GeneralProblems with SelectObject Pin
Peter Schuhmann
4:27 27 Jul '02  
GeneralOdd behaviour Pin
Box2020
4:02 11 Jul '02  
GeneralRe: Odd behaviour Pin
Alex Farber
20:04 13 Jul '02  
GeneralRe: Odd behaviour Pin
Anonymous
9:49 12 Jun '03  
GeneralGDI performance Pin
Anonymous
8:20 1 May '02  
GeneralRe: loading bitmap in CachedBitmap gobbles memory Pin
Alex Farber
3:38 7 Mar '02  
GeneralMemory leaks with CachedBitmap Pin
Nicolas Cadilhac
11:33 21 Feb '02  


Last Updated 9 Aug 2001 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009