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 800×600, always worked fine, but larger images (e.g. 2400×1800) 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 (800×1200) 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
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.InterpolationModeisn’t set to something like
NearestNeighborwill 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.)
Bilinearis usually a good compromise. You should never use anything but
NearestNeighborif the bitmap size matches the area you’re drawing to, in pixels.
- Always draw into the
Graphicsobject given to you in
- 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
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.Timeras a framerate/tick timer won’t work very well. These are implemented using Win32’s
SetTimerand result in WM_TIMER messages which then result in the
Timer.Tickevent 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.Timerand then using
Control.Invoke()(to make sure you’re on the right thread!) and calling
- In general, do not use
Control.CreateGraphics(). (corollary to ‘always draw in
OnPaint()‘ and ‘always use the
Graphicsgiven to you by
- 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.
UserControl, will either not add any value for you or will add additional overhead. (BTW
PictureBoxis often misunderstood. You will probably almost never want to use it.)
Hope that helps.