Graphics.DrawImage - why it's so slow?
-
I have some image preview control based on a
Panel
which remembers current preview image position. ItsOnPaint
method does nothing except drawing viewed part of the image using thisDrawImage
override:Graphics.DrawImage(Image, Rectangle, Rectangle, GraphicsUnit)
This draws only desired part of an image. The preview control is very small, so the whole painting should be fast, but it isn't. Drawing 50x50 px rectangular part from one image to second - for some unknown reason - depends on the size of the source image :confused: When this method draws little rectangle from 7 Mpx image, it tooks about 200 ms - that's not real-time, when image is moved and the control is repeatedly invalidated. I saw similar preview controls, where the scrolling of image were smooth and doesn't depend on image size. These controls were part of the MFC apps. Then I tried professional .NET component for image previews, which was also slow. I'm confused - why can't this simple task be quick? I spent a life solving this problem, so answer would be appreciated. -
I have some image preview control based on a
Panel
which remembers current preview image position. ItsOnPaint
method does nothing except drawing viewed part of the image using thisDrawImage
override:Graphics.DrawImage(Image, Rectangle, Rectangle, GraphicsUnit)
This draws only desired part of an image. The preview control is very small, so the whole painting should be fast, but it isn't. Drawing 50x50 px rectangular part from one image to second - for some unknown reason - depends on the size of the source image :confused: When this method draws little rectangle from 7 Mpx image, it tooks about 200 ms - that's not real-time, when image is moved and the control is repeatedly invalidated. I saw similar preview controls, where the scrolling of image were smooth and doesn't depend on image size. These controls were part of the MFC apps. Then I tried professional .NET component for image previews, which was also slow. I'm confused - why can't this simple task be quick? I spent a life solving this problem, so answer would be appreciated.Here are some possible suggestions i've encountered with similar Stuff: - Is the OnPaint method called very often? (stupid thing i once did number 1 :rolleyes:) - Try using a label instead of a panel. A label is double-buffered (if i remember right) and a panle isn't. Better yet, make your own custom control with the styles DoubleBuffer, AllPaintingInWmPaint and UserPaint on. - Make sure the source rectangle and the destination rectangle are the same width and height, so the GDI doesn't have to schale the screen image. - It can also be a problem as a result of the big image (i think 7 MB is a lot). If it's possible, try drawing a bitmap with a pixel format of Format32bppPArgb, it is said to be the fastest. I hope this helps!
"..Commit yourself to quality from day one..it's better to do nothing at all than to do something badly.." -- Mark McCormick
|| Fold With Us! || Pensieve || VG.Net ||
-
I have some image preview control based on a
Panel
which remembers current preview image position. ItsOnPaint
method does nothing except drawing viewed part of the image using thisDrawImage
override:Graphics.DrawImage(Image, Rectangle, Rectangle, GraphicsUnit)
This draws only desired part of an image. The preview control is very small, so the whole painting should be fast, but it isn't. Drawing 50x50 px rectangular part from one image to second - for some unknown reason - depends on the size of the source image :confused: When this method draws little rectangle from 7 Mpx image, it tooks about 200 ms - that's not real-time, when image is moved and the control is repeatedly invalidated. I saw similar preview controls, where the scrolling of image were smooth and doesn't depend on image size. These controls were part of the MFC apps. Then I tried professional .NET component for image previews, which was also slow. I'm confused - why can't this simple task be quick? I spent a life solving this problem, so answer would be appreciated.In addition to what Marc said... Instead of redrawing the image from the same 7M pixel image (huge!), have your control draw the thumbnail image to an internal buffer first, the same size as the preview window. Then when your preview has to draw, it can more quickly draw it from the internal buffer version, which should be pretty small. I've had to do this a few times in some custom controls that I wrote. Instead of calculating the same image over and over again in order to redraw a control image that doesn't change much, I monitor the Resize events of my control and redraw the control image to an internal buffer. Then in the Paint event for the control, I draw the control image from the buffer. For example, take a clock face. Most of what you see doesn't change, except for the hand positions. But, to get smoothly turning clock hands, you have to redraw the control about every 30-50 ms at least. Since calculating the positions of the various bits of a clock face can be time consuming, a clock face image should be calculated and drawn once to a cached image. When the control has to redraw itself (every 30ms), it merely has to draw the clock face from the cache, then calculate and draw only the hand positions. When the size of the control changes, you recalculate and redraw the cache image to match the size of the control. RageInTheMachine9532 "...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
-
In addition to what Marc said... Instead of redrawing the image from the same 7M pixel image (huge!), have your control draw the thumbnail image to an internal buffer first, the same size as the preview window. Then when your preview has to draw, it can more quickly draw it from the internal buffer version, which should be pretty small. I've had to do this a few times in some custom controls that I wrote. Instead of calculating the same image over and over again in order to redraw a control image that doesn't change much, I monitor the Resize events of my control and redraw the control image to an internal buffer. Then in the Paint event for the control, I draw the control image from the buffer. For example, take a clock face. Most of what you see doesn't change, except for the hand positions. But, to get smoothly turning clock hands, you have to redraw the control about every 30-50 ms at least. Since calculating the positions of the various bits of a clock face can be time consuming, a clock face image should be calculated and drawn once to a cached image. When the control has to redraw itself (every 30ms), it merely has to draw the clock face from the cache, then calculate and draw only the hand positions. When the size of the control changes, you recalculate and redraw the cache image to match the size of the control. RageInTheMachine9532 "...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
In fact, I'm afraid that using buffer will enhance redrawing when image position doesn't change, but when user will need to shift image viewport by a "hand tool", the redrawing buffer with drawing it on control will take slightly more time than redrawing the control directly. However, the buffer idea is good, because there can be more OnPaint calls, not only on mouse moves... Another idea was to use a three times larger buffer than the control. When user drags mouse over control, the cursor is locked inside the control bounds and
OnPaint
works just with the buffer. When user releases mouse button, the buffer will update. This can be a little frustrating, when user needs to scroll whole large image (more dragging), but this can be also fast. I'll try these approaches, thank you very much for the new ideas :). -
In fact, I'm afraid that using buffer will enhance redrawing when image position doesn't change, but when user will need to shift image viewport by a "hand tool", the redrawing buffer with drawing it on control will take slightly more time than redrawing the control directly. However, the buffer idea is good, because there can be more OnPaint calls, not only on mouse moves... Another idea was to use a three times larger buffer than the control. When user drags mouse over control, the cursor is locked inside the control bounds and
OnPaint
works just with the buffer. When user releases mouse button, the buffer will update. This can be a little frustrating, when user needs to scroll whole large image (more dragging), but this can be also fast. I'll try these approaches, thank you very much for the new ideas :).ltinka wrote:
Another idea was to use a three times larger buffer than the control. When user drags mouse over control, the cursor is locked inside the control bounds and OnPaint works just with the buffer. When user releases mouse button, the buffer will update. This can be a little frustrating, when user needs to scroll whole large image (more dragging), but this can be also fast.
Instead of tieing the user to a small section of the image per move, if they reach the edge of the bufffer cause an update then. A periodic delay would be much less bothersome for the user than having to repeatedly grab and let go to scroll a large distance.