Is Graphics.DrawImage too slow for bigger images?
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 setGraphics.CompositingMode
toCompositingMode.SourceCopy
before callingDrawImage()
(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 likeInterpolationMode.HighQualityBicubic
. UsingNearestNeighbor
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 butNearestNeighbor
if the bitmap size matches the area you're drawing to, in pixels. - Always draw into the
Graphics
object given to you inOnPaint()
. - Always do your drawing in
OnPaint
. If you need to redraw an area, callInvalidate()
. If you need the drawing to happen right now, callUpdate()
afterInvalidate()
. This is a reasonable approach since WM_PAINT messages (which results in a call toOnPaint()
) 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'sSetTimer
and result in WM_TIMER messages which then result in theTimer.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 usingSystem.Threading.Timer
and then usingControl.Invoke()
(to make sure you're on the right thread!) and callingControl.Update()
. - In general, do not use
Control.CreateGraphics()
. (corollary to 'always draw inOnPaint()
' and 'always use theGraphics
given to you byOnPaint()
') - I recommend not using the Paint event handler. Instead, implement
OnPaint()
in the class you're writing which should be derived fromControl
. Deriving from another class, e.g.PictureBox
orUserControl
, will either not add any value for you or will add additional overhead. (BTWPictureBox
is often misunderstood. You will probably almost never want to use it.)
Hope that helps.
gdi+ Graphics::DrawImage really slow~~
You have a screen of 4000 x 3000 resolution? Wow!
If not, you should draw only the visible part of the image, it would be much faster...
[EDIT after first comment] My remark is indeed a bit stupid, I suppose DrawImage will mask/skip unneeded pixels.
After your edit (showing StretchDIBits), I guess a possible source of speed difference might come from the fact that StretchDIBits is hardware accelerated ("If the driver cannot support the JPEG or PNG file image" is a hint...) while DrawImage might be (I have no proof for that!) coded in C, relying on CPU power instead of GPU's one...
If I recall correctly, DIB images are fast (despite being "device independent"). See High Speed Win32 Animation: "use CreateDIBSection to do high speed animation". OK, it applies to DIB vs. GDI, in old Windows version (1996!) but I think it is still true.
[EDIT] Maybe Bitmap::GetHBITMAP function might help you to use StretchDIBits (not tested...).
Graphics.DrawImage alternatives for large images
Sounds to me you are focusing on the wrong problem. Painting the image is slow, not painting the "cross-hairs".
Large images can certainly be very expensive when you don't help. And System.Drawing makes it very easy to not help. Two basic things you want to do to make the image paint faster, getting it more than 20 times faster is quite achievable:
avoid forcing the image painting code to rescale the image. Instead do it just once so the image can be drawn directly one-to-one without any rescaling. Best time to do so is when you load the image. Possibly again in the control's Resize event handler.
pay attention to the pixel format of the image. The fastest one by a long shot is the pixel format that's directly compatible with the way the image needs to be stored in the video adapter. So the image data can be directly copied to video RAM without having to adjust each individual pixel. That format is PixelFormat.Format32bppPArgb on 99% of all modern machines. Makes a huge difference, it is ten times faster than all the other ones.
A simple helper method that accomplishes both without otherwise dealing with the aspect ratio:
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}
Graphics are too slow in my project
I don't think you are going to get any easy solutions. I can offer a few tips and opinions:
- You seem to be creating a new
BitMap
every time you paint the screen. This is definitely not a good idea, as large bitmaps are absolutely huge in terms of memory. What you probably want to do is create one when your game loads, and then simply clear it and repaint it at every frame. I think this is probably one of the bigger performance issues you have. - There are a number of optimisations you could make afterwards. E.g. you are "rendering" the image that you will end up painting to the screen on the user interface thread. If the rendering process takes long, this will be noticeable. Typically this work happens on a background thread, and then the UI thread just checks if it can repaint using the new image. (I am simplifying things greatly here).
- For graphics intensive applications, WinForms is not a particularly good environment, as others have pointed out. You will not get any hardware acceleration at all. Moving to XNA is one option, but if your application is also quite rich in terms of standard WinForms screens and controls, this is probably not an easy option. Another suggested alternative might be WPF, where you might be able to get away with using transformations to move things around, which are hardware accelerated, and are not too dissimilar to a WinForms application (well, you don't need to implement your own buttons, etc).
Hope this helps a bit.
How to improve the performance of g.drawImage() method for resizing images
You can use ImageMagick to create thumbnails.
convert -define jpeg:size=500x180 hatching_orig.jpg -auto-orient \
-thumbnail 250x90 -unsharp 0x.5 thumbnail.gif
To use it from Java you can try JMagick which provides a Java (JNI) interface to ImageMagick. Or you can simply invoke the ImageMagick commands directly using Runtime.exec
or ProcessBuilder
.
Related Topics
Why "Decimal" Is Not a Valid Attribute Parameter Type
How to Get Next (Or Previous) Enum Value in C#
Rotate a Graphics Bitmap at Its Center
Collection Was Modified; Enumeration May Not Execute Error When Removing a Listitem from a Listbox
Allow Access Permission to Write in Program Files of Windows 7
Write Device Platform Specific Code in Xamarin.Forms
How to Get the Text of a Messagebox When It Has an Icon
Best Way to Switch Behavior Based on Type
Pass Multiple Complex Objects to a Post/Put Web API Method
How Are Nullable Types Implemented Under the Hood in .Net
What Are the Default Schedulers for Each Observable Operator
How to Find Certificate by Its Thumbprint in C#
How to Compare Lists in Unit Testing
Visual Studio 2015 Break on Unhandled Exceptions Not Working
How to Download/Upload Files From/To Sharepoint 2013 Using Csom