Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm currently working on a game and I wish to have a main menu with background image.

However, I find the method Graphics.DrawImage() really slow. I have made some measurement. Let's assume that MenuBackground is my resource image with resolution 800 x 1200 pixels. I will draw it onto another 800 x 1200 bitmap (I render everything to a buffer bitmap first, then I scale it and finally draw it onto screen - that's how I deal with the possibility of multiple players' resolutions. But it shouldn't affect it in any way, see the next paragraph).

So I've measured the following code:

Stopwatch SW = new Stopwatch();
SW.Start();
// First let's render background image into original-sized bitmap:
OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground,
   new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight));
SW.Stop();
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds");

The result is quiet surprising to me - the Stopwatch measures something between 40 - 50 milliseconds. And because the background image is not the only thing to be drawn, the whole menu takes about over 100 ms to display, which implicates observable lag.

I have tried to draw it to Graphics object given by Paint event, but the result was 30 - 40 milliseconds - not much changed.

So, does it mean, that Graphics.DrawImage() is unusable for drawing bigger images? If so, what should I do to improve the performance of my game?

If by "unusable" you mean "slow as a snail in molasses", then yes. As others have said, give XNA a try. Stick to 2D sprite rendering until you're used to the XNA framework, then move towards 3D if you're so inclined. – CodeHxr Jun 13, 2012 at 18:14

Yes, it is too slow.

I ran into this problem several years ago while developing Paint.NET (right from the start, actually, and it was rather frustrating!). Rendering performance was abysmal, as it was always proportional to the size of the bitmap and not the size of the area that it was told to redraw. That is, framerate went down as the size of the bitmap went up, and framerate never went up as the size of the invalid/redraw area went down when implementing OnPaint() and calling Graphics.DrawImage(). A small bitmap, say 800x600, always worked fine, but larger images (e.g. 2400x1800) were very slow. (You can assume, for the preceding paragraph anyway, that nothing extra was going on, such as scaling with some expensive Bicubic filter, which would have adversely affected performance.)

It is possible to force WinForms into using GDI instead of GDI+ and avoid even the creation of a Graphics object behind the scenes, at which point you can layer another rendering toolkit on top of that (e.g. Direct2D). However, it's not simple. I do this in Paint.NET, and you can see what's required by using something like Reflector on the class called GdiPaintControl in the SystemLayer DLL, but for what you're doing I'd consider it a last resort.

However, the bitmap size you're using (800x1200) should still work OK enough in GDI+ without having to resort to advanced interop, unless you're targeting something as low as a 300MHz Pentium II. Here are some tips that might help out:

  • If you are using an opaque bitmap (no alpha/transparency) in the call to Graphics.DrawImage(), and especially if it's a 32-bit bitmap with an alpha channel (but you know it's opaque, or you don't care), then set Graphics.CompositingMode to CompositingMode.SourceCopy before calling DrawImage() (be sure to set it back to the original value after, otherwise regular drawing primitives will look very ugly). This skips a lot of extra blending math per-pixel.
  • Make sure Graphics.InterpolationMode isn't set to something like InterpolationMode.HighQualityBicubic. Using NearestNeighbor will be the fastest, although if there's any stretching it may not look very good (unless it's stretching by exactly 2x, 3x, 4x, etc.) Bilinear is usually a good compromise. You should never use anything but NearestNeighbor if the bitmap size matches the area you're drawing to, in pixels.
  • Always draw into the Graphics object given to you in OnPaint().
  • Always do your drawing in OnPaint. If you need to redraw an area, call Invalidate(). If you need the drawing to happen right now, call Update() after Invalidate(). This is a reasonable approach since WM_PAINT messages (which results in a call to OnPaint()) are "low priority" messages. Any other processing by the window manager will be done first, and thus you could end up with lots of frame skipping and hitching otherwise.
  • Using a System.Windows.Forms.Timer as a framerate/tick timer won't work very well. These are implemented using Win32's SetTimer and result in WM_TIMER messages which then result in the Timer.Tick event being raised, and WM_TIMER is another low priority message which is sent only when the message queue is empty. You're better off using System.Threading.Timer and then using Control.Invoke() (to make sure you're on the right thread!) and calling Control.Update().
  • In general, do not use Control.CreateGraphics(). (corollary to 'always draw in OnPaint()' and 'always use the Graphics given to you by OnPaint()')
  • I recommend not using the Paint event handler. Instead, implement OnPaint() in the class you're writing which should be derived from Control. Deriving from another class, e.g. PictureBox or UserControl, will either not add any value for you or will add additional overhead. (BTW PictureBox is often misunderstood. You will probably almost never want to use it.)
  • Hope that helps.

    you deserve tons of upvotes.For drawing a background grid is there any tip ? TextureBrush seems fast but it doesnt work when you have custom scalling with a zoom factor as it doesnt match – GorillaApe Sep 29, 2014 at 9:27 The best optimization of this post for me was the interpolation mode. Changing Bilinear to NearestNeighbor made the drawing 8 times quicker. – PurkkaKoodari Jun 16, 2016 at 8:43 Can you please clarify your last comment, about PictureBoxes? Why should they not be used, and how are they misunderstood? – Luna Aug 14, 2017 at 14:19 PictureBox is for displaying an Image, nothing more. If you pretend the class is sealed, its use case should be clearer. It's misunderstood because I've often seen code where a new class is created that derives from PictureBox and then the author overrides OnPaint or whatever and does custom rendering. Don't do that -- just derive directly from Control if you want to do custom rendering. And if you just want to show an Image or Bitmap, use PictureBox -- but don't derive a new class from it. Pretend it's sealed. – Rick Brewster Aug 17, 2017 at 4:15

    Although this is an ancient question and WinForms is an ancient Framework, I would like to share what I have just discovered by accident: drawing a Bitmap into a BufferedGraphics and rendering it afterwards to the graphics context provided by OnPaint is way faster than drawing the Bitmap directly to OnPaint's graphics context - at least on my Windows 10 machine.

    That's surprising because intuitively I had assumed that it would be slightly slower to copy data twice (and so I thought that this is usually only justified when one wants to do double-buffering manually). But obviously there is something more sophisticated going on with the BufferedGraphics object.

    So create a BufferedGraphics in the constructor of the Control that shall host the Bitmap (in my case I wanted to draw a fullscreen bitmap 1920x1080):

            using (Graphics graphics = CreateGraphics())
                graphicsBuffer = BufferedGraphicsManager.Current.Allocate(graphics, new Rectangle(0,0,Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height));
    

    and use it in OnPaint (while voiding OnPaintBackground)

        protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}
        protected override void OnPaint(PaintEventArgs e)
            Graphics g = graphicsBuffer.Graphics;
            g.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);
            graphicsBuffer.Render(e.Graphics);
    

    instead of naively defining

        protected override void OnPaintBackground(PaintEventArgs e) {/* just rely on the bitmap to fill the screen */}
        protected override void OnPaint(PaintEventArgs e)
            e.Graphics.DrawImage(someBitmap,0,0,bitmap.Width, bitmap.Height);
    

    See the following screenshots for a comparison of the resulting MouseMove event frequency (I am implementing a very simple bitmap sketching control). At the top is the version where the Bitmap is drawn directly, at the bottom BufferedGraphics is used. I moved the mouse at about the same speed in both cases.

    GDI+ is not a speed demon by any means. Any serious image manipulation usually has to go into the native side of things (pInvoke calls and/or manipulation via a pointer obtained by calling LockBits.)

    Have you looked into XNA/DirectX/OpenGL?. These are frameworks designed for game development and will be orders of magnitude more efficient and flexible than using a UI framework like WinForms or WPF. The libraries all offer C# bindings.

    You could pInvoke into native code, using functions like BitBlt, but there is overhead associated with crossing the managed code boundary as well.

    I would recommend OpenGL/DirectX too. XNA isn't really the BEST choice per se for a variety of reasons. Ease of use, yes - but in the long run it's a bit of a handicap. – Ani Jun 13, 2012 at 18:28 @ananthonline: You're right, they're worth a mention. DirectX is superior to OpenGL if you don't care about cross-platform support. Can you expand on your comment " in the long run [XNA is] a bit of a handicap". I probably have some holes in my knowledge as I'm not a serious game developer, just a hobby of mine. – Ed S. Jun 13, 2012 at 18:50 XNA makes several assumptions to its use. One of the serious limitations I can think of is that multiple rendertargets cannot share a depth buffer, making common scenarios like efficiently doing deferred rendering impossible. See here for more info (forums.create.msdn.com/forums/p/64179/397431.aspx) – Ani Jun 13, 2012 at 18:54 Also - it does not have a definite future on Metro. I don't know if it will be supported outside of projects like MonoGame (monogame.codeplex.com) – Ani Jun 13, 2012 at 18:56 @ananthonline: Thanks. I went to far with the "By far the best" comment. I thought that using DirectX or OpenGL from C# would be cumbersome, but after doing some research it looks like there are some nice C# interfaces I was unaware of. – Ed S. Jun 13, 2012 at 19:01

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.