Click here to Skip to main content
15,304,273 members
Articles / Multimedia / GDI+
Tip/Trick
Posted 19 Mar 2010

Tagged as

Stats

176.9K views
41 bookmarked

Rendering fast with GDI+ - What to do and what not to do!

Rate me:
Please Sign up or sign in to vote.
4.95/5 (28 votes)
28 Mar 2010CPOL3 min read
Everyone knows GDI+ is generally slow in rendering, what is not so obvious is what combination of settings is the fastest.I have an app which renders a lot of items, which may be drawn or are bitmap-sourced 'sprites', onto a full page (usually 1024x768). The first attempts ran at around 3...
Everyone knows GDI+ is generally slow in rendering, what is not so obvious is what combination of settings is the fastest.

I have an app which renders a lot of items, which may be drawn or are bitmap-sourced 'sprites', onto a full page (usually 1024x768). The first attempts ran at around 3 frames/sec - with the speedups noted here I got the rate upto 41fps.

My application roughly does the following:

load a background Bitmap: bgndBitmap
load any Bitmaps used for 'sprites': spriteBitmap
create a Bitmap to render into: renderBitmap
create a Graphics object to draw on the Bitmap
while( forever )
{
     draw bgndBitmap
     for( all items... )
         draw part of spriteBitmap

     draw the renderBitmap into a dialog
}


What to do
Always use PixelFormat32bppPARGB

That's pre-multiplied Alpha, even though the particular image or Bitmap may not have any Alpha.

Since the user of my app can use several formats for a background (including .png), then just loading the image using...

Bitmap *LoadABitmap( char *filename )
{
    WCHAR wname[1024];
    MultiByteToWideChar( CP_ACP, 0, filename, -1, wname, 1000 );
    Bitmap *loadBmp= new Bitmap( wname, FALSE );
    if( loadBmp->GetLastStatus() == Ok )
    ...


...could result in one of several formats.
What we want is a 'solid' background in a known format, so I redraw the loaded bitmap into a new bitmap with a fixed format.

PixelFormat32bppRGB would seem appropriate as this would remove any alpha (tranparency) component which might be present - however, this will not result in the fastest rendering.

What we want is PixelFormatPARGB with all the Alpha set to 'solid'.
In my case I just:

Bitmap *newBmp= new Bitmap( loadBmp->GetWidth(), loadBmp->GetHeight(),
                           PixelFormat32bppPARGB );
Graphics *pGraphics= new Graphics( newBmp );

pGraphics->Clear( 0xffffffff );  // clear to solid white

Rect sizeR( 0,0,loadBmp->GetWidth(), loadBmp->GetHeight());
pGraphics->DrawImage( loadBmp, sizeR, 0,0,
                         (int)loadBmp->GetWidth(),
                         (int)loadBmp->GetHeight(),
                         UnitPixel );
delete pGraphics;
delete loadBmp;

return newBmp;


More generally, you could draw the loaded bitmap into an RGB bitmap and then draw the RGB bitmap into a PARGB bitmap.

I do the same sort of thing for the sprite bitmaps, but here I want to preserve any transparency - just get the format to be 'PARGB'.

Set the Graphics options

The following settings are generally the fastest:

Graphics *pGraphics= Graphics::FromHWND( hwndMyPictureWindow, FALSE );

pGraphics->SetCompositingMode( CompositingModeSourceCopy );
pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
pGraphics->SetPixelOffsetMode( PixelOffsetModeNone );
pGraphics->SetSmoothingMode( SmoothingModeNone );
pGraphics->SetInterpolationMode( InterpolationModeDefault );

pGraphics->DrawImage( RenderBitmap, 0, 0 );
delete pOutputGraphics;


Use these settings for 'blitting' bitmaps at 1:1 scale, as in the above example we're simply copying the bitmap to the output window.

However, the results will be somewhat disappointing if you are doing any scaling, rendering of sprites or text.

For drawing sprites and such, these settings are good quality and reasonably fast:

pGraphics->SetCompositingMode( CompositingModeSourceOver );  // 'Over for tranparency
pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
pGraphics->SetSmoothingMode( SmoothingModeHighSpeed );
pGraphics->SetInterpolationMode( InterpolationModeHighQuality );


Do NOT think that, for example, InterpolationModeLowQuality should be faster... it isn't (don't ask me why!).

The fastest settings for drawing sprites with transparency, if you can handle some edge effects, are:

pGraphics->SetCompositingMode( CompositingModeSourceOver );
pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
pGraphics->SetSmoothingMode( SmoothingModeNone );
pGraphics->SetPixelOffsetMode( PixelOffsetModeHalf );
pGraphics->SetInterpolationMode( InterpolationModeNearestNeighbor );


Note:
Some of these settings are interrelated, InterpolationModeNearestNeighbor is only the fastest if you have PixelOffsetModeHalf also set.


Just a word of caution: PixelOffsetModeHalf does have some other effects - GetVisibleClipBounds() will return {1,1,X,Y} rather than {0,0,X,Y}, which will cause LockBits() to fail.


For scaling one whole bitmap into another, I use:

// set quality for scaling
pGraphics->SetCompositingMode( CompositingModeSourceCopy );
pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
pGraphics->SetSmoothingMode( SmoothingModeHighSpeed );
pDestGraphics->SetInterpolationMode( InterpolationModeHighQuality );

if( g_RenderFastest )  // global flag to render fastest, but reduce quality
{
    pGraphics->SetInterpolationMode( InterpolationModeDefault );
    pGraphics->SetSmoothingMode( SmoothingModeNone );
    pGraphics->SetPixelOffsetMode( PixelOffsetModeHalf );
}


For completeness, here's what I set for drawing text with DrawString():

pGraphics->SetCompositingMode( CompositingModeSourceOver );
pGraphics->SetCompositingQuality( CompositingQualityHighSpeed );
pGraphics->SetInterpolationMode( InterpolationModeHighQuality );
pGraphics->SetPixelOffsetMode( PixelOffsetModeHighSpeed );
pGraphics->SetSmoothingMode( SmoothingModeNone );
// Note: TextRenderingHintClearTypeGridFit looks real crap
//       if we're drawing onto a transparent object
//       but now we're not... anyhow, this is OK:
pToGraphics->SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );


What not to do

Don't mix bitmap formats

Rendering an ARGB bitmap onto an RGB bitmap can be slower by more than a factor of 10 compared to two PARGB bitmaps. Generally, it seems that ARGB always causes GDI+ to internally do a load of pre-multiplying and eats the CPU.


Don't even think about threads

I mention this 'cos I tried... (my app actually needs to output to 4 monitors on a single machine) the code worked fine, but GDI+ is not designed for multithreading and at best, if you get the CRITICAL_SECTION's right, almost renders sequentially anyway.

See the notes in MSDN under GDI+ Security Considerations, it's right there at the end (in a locked filing cabinet marked "Beware of the leopard").


Guess...

The options available are not obvious, so if you're doing some investigation, you must profile your code to see what effect each of 'em has. Guessing doesn't work, what may sound like a faster/less quality option probably isn't.


The End

That's the lot, hope it's of some use to someone.

Yours,
Tony Wilk

[mail at tonywilk dot co dot uk]













The End.

License

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

Share

About the Author

tonywilk
Founder
United Kingdom United Kingdom
Developer in just about anything from 6502 machine code thru C, C++ and now things like PHP and javascript. Used to develop hardware and still dabble with electronics and ham radio when I'm not letting off pyrotechnics, shooting or flying my VPM M16 gyroplane.

Comments and Discussions

 
Questionextremely useful Pin
marktopinka31-May-17 11:50
Membermarktopinka31-May-17 11:50 
QuestionThanks Tony! Very interesting fellow! Pin
shooky566-Jan-15 7:23
Membershooky566-Jan-15 7:23 
Tony there is not one thing here that I needed for what I'm currently doing but was captured by your presentation style and the fact the topic was in my interest line anyway.

Excellent work sir! Reading your profile, we have quite similar backgrounds. I built hardware for a Radio Shack Color Computer "back when" that had 24 bit AtoD plus a parallel printer port and analogue and digital IO. Also built and sold components to attach to it including an optical scanner (before they were commercially available), Zerox bought a scanner from me (lol we won't mention what I think they probably did with it!).

A year or so later I wrote assembly flight simulators in 6809 then 80x86. Back then (early 80s) I had to build tables for the transcendental functions (using linear approximations between the hard points). Running the calculations was far too slow. My precision calculates were based on "better than a pixel". Most of the time the approximations were better than 1/5th a pixel width. Anti-aliasing never crossed my mind but I did do specular reflections way before anybody else. The project I was doing it for was a "you build it and we'll buy it" for a real live F-4 cockpit in a War Museum. I was also supposed to add mechanics (which I build parts of but wasn't done yet) to move the cockpit a bit corresponding to the flying. Won't mention the defense contractor but they had massive layoffs and canceled the project!

One of the problems that bothered me back then was the fact that each graphics card required slightly different commands (of course my code talked directly to them). Never figured out how I'd ever keep up with a library. Windows and the DirectX team did figure that out they put the load on the manufacturers (the only sensible place).

But seeing your speed tests reminded me so much of the things I've done (and continue to do). Unrelated to graphics but related to the speed thing. I wrote a 3D imager for GPS files. GPS devices have several output formats but an XML format is one of them. When I did this about 5 years ago, I had a bunch of young guys in the code shop who were certain I'd goofed so 3 or 4 of them set out to prove I'd erred and came back with the same result. What was happening was these history files in GPS would run into the M-bytes. To load and parse them using the XML libraries was taking 20 MINUTES! sheesh! So I wrote a linear parser that would run a 20 minute MicroSoft parse in 6 seconds. But I tested a lot of stuff for speed for that one.

Currently I'm building a carbon fiber fully faired recumbent bike (also an endurance jock albeit well past my prime). My latest coding work has been to use the NASA 4-digit airfoil forumlas to build a shell around the rider (animated) and frame (also designed in the app).

Anyway, thanks a lot for the posting! Thought you may enjoy those "old guy stories". May the Gods of Coding be with you sir.
AnswerRe: Thanks Tony! Very interesting fellow! Pin
tonywilk11-Mar-15 6:21
professionaltonywilk11-Mar-15 6:21 
GeneralMy vote of 5 Pin
YoungShall4-Jul-13 23:36
MemberYoungShall4-Jul-13 23:36 
GeneralMy vote of 5 Pin
Ha Viet23-Nov-12 3:32
MemberHa Viet23-Nov-12 3:32 
GeneralMy vote of 5 Pin
nv38-Jul-12 21:17
Membernv38-Jul-12 21:17 
GeneralReason for my vote of 5 Great tip, deserve full point Pin
ThatsAlok16-Aug-11 3:14
MemberThatsAlok16-Aug-11 3:14 
GeneralGreat! useful tip! Pin
ThatsAlok16-Aug-11 3:14
MemberThatsAlok16-Aug-11 3:14 
GeneralReason for my vote of 5 very usefull Pin
fisker.cui10-Aug-11 8:23
Memberfisker.cui10-Aug-11 8:23 
GeneralReason for my vote of 5 good research and testing of the sub... Pin
Trethbos9-Jul-10 0:12
MemberTrethbos9-Jul-10 0:12 
GeneralUsing a PARGB format helped me a lot! It's anything but obvi... Pin
joerg-smn29-Jun-10 2:24
Memberjoerg-smn29-Jun-10 2:24 
GeneralGLGDI+ Pin
dmbreaker12-Sep-10 10:48
Memberdmbreaker12-Sep-10 10:48 
QuestionIsn't it simpler to use Bitmap::Clone to convert Bitmap to another format? Pin
__stas__13-Jul-10 3:21
Member__stas__13-Jul-10 3:21 
AnswerRe: Isn't it simpler to use Bitmap::Clone to convert Bitmap to another format? Pin
tonywilk13-Jul-10 6:36
professionaltonywilk13-Jul-10 6:36 
GeneralPrint server Configuration help Pin
Sivaprasad S Nair31-Mar-10 4:46
MemberSivaprasad S Nair31-Mar-10 4:46 
Generalvery interesting Pin
Luc Pattyn28-Mar-10 18:08
sitebuilderLuc Pattyn28-Mar-10 18:08 
GeneralRe: very interesting - A speed comparison Pin
tonywilk29-Mar-10 4:41
professionaltonywilk29-Mar-10 4:41 

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.