Click here to Skip to main content
Rate this: bad
Please Sign up or sign in to vote.
See more: C# GDI+
Hi all!
I recently created a control that will zoom in and out of an image and will scroll on it (something like on google earth), but I cannot get a smooth rendering when it begins to animate.
By the way I used a for-loop for changing parameters and then refresh or invalidate a piece of the image and set the double buffer to true.
My for-loop statement is tuned but what should I do if I want to get a smoother rendering?
Am I going in the wrong direction?
Maybe I should use some unmanaged code?
Thanks in advance!
Posted 18-Jan-11 0:13am
Edited 19-Jan-11 22:40pm
Dalek Dave432.9K
Dalek Dave at 20-Jan-11 3:40am
Edited for Readability.
Rate this: bad
Please Sign up or sign in to vote.

Solution 1

It is not exactly clear how you implemented it. Does your loop have some sort of frame limiter for example? Is the loop executed with a maximum of 30 times (frames) a second or are you hogging the processor like crazy? Using a refresh directly is not advisable and should stick with invalidate only. If that means that no screen updates are shown, it means that you prevent the normal program flow of updating your screen.
Another important thing is to do the processing in another loop. This not even helps when using a multi processor environment but keeps the message loop active during processing. When a user is requesting to zoom, you can then immediately set some flag indicating that the processing can be aborted. By skipping that unnecessary processing the program becomes more responsive to the user and processes request immediately. Don't forget to yield (or sleep(0)) within the thread and check the indicator flag. Also limit the thread from processing more than 30 frames per second and use the sleep method to release processor time:
Sleep(Max((1000 / 30) - ({time in milliseconds when done} - {time in milliseconds when started}), 0));
The Max method is used to ensure that no negative numbers are used. It is also easy to detect if the value is negative all the time and react to that by lowering the frames per second.
Good luck!

2D "Nim" Game Development and Animation in C# (Part 2)[^]
2D Animated Charts[^]
Rotating Sprite Objects on DirectDraw Wrapper for C#[^]
Introduction to DirectDraw and Surface Blitting[^]
Manfred R. Bihy at 18-Jan-11 14:56pm
Moved from OP's answer:
dear nijboer it was helpful but is there any way to cash Image to force it to draw more quicker?
E.F. Nijboer at 19-Jan-11 7:55am
About caching. I don't know exactly what your program does. But if the image itself is not an animation itself but is just scrolling as one large image than you can reuse most of the image by moving it and update only a small part of the screen. let's say your view port is 800x600 and you scroll to the left 10 pixels on every frame update. You then simply take (10,0)-(800,600) and move that to (0,0)-(790,600) and only need to update (790,0)-(800,600) with a new part of the image. You can use the bitblt function for moving the image part that can be reused. Could this provide some solution?
E.F. Nijboer at 19-Jan-11 8:22am
Also added some links that might help you out.
Espen Harlinn at 19-Jan-11 8:32am
5+ Good links, not sure OP is willing to make the effort though
Dalek Dave at 20-Jan-11 3:40am
Very Good answer, with good links.
Rate this: bad
Please Sign up or sign in to vote.

Solution 2

E.F. Nijboer is on to something - a for type of loop is probably not a good idea, and like the man says: don't do a refresh.
You can try the following:
Use a timer to invalidate the screen area occupied by your image, and only paint during the Paint event[^] or override the OnPaint method[^]
This will keep your application responsive to other user actions, and it's basically how the "old" windows api was designed to work. Multiple invalidations will be combined into a single WM_PAINT if the system for some reason is unable to keep up with the "animation frame rate".
If you hvan't put to much work into this at this stage, consider moving your solution to WPF, where stuff like this finds ready implementations in the framework.
If you are using a PictureBox for your image you can solve your problem using the approach outlined in this article:[^]
It's essentially using the approach I outlined above. Setting the PictureBox.Image property invalidates the PictureBox control.
If it's worth the effort you can use DirectX or TAO[^] to get hardware accelerated performance. Quadratic surface, image as texture, and so on...

Espen Harlinn
SAKryukov at 18-Jan-11 10:58am
My 5.
Espen Harlinn at 18-Jan-11 11:01am
Well, thank you SAKryukov :)
Manfred R. Bihy at 18-Jan-11 14:57pm
Moved from OP's answer:
Thanks Harlinn but i used all of these (except change format to 32bppPArgb) and this problem still persists (animated zoom in/out is not smooth)
Any other suggestion ?
Manfred R. Bihy at 18-Jan-11 14:58pm
Espins comment to OP's "answer":
How much effort are you willing to put into this? I usually try to provide simple answers, since that's usually what people actually want. Are you developing your own custom component?
Espen Harlinn at 18-Jan-11 15:03pm
If he is willing to spend a couple of days on it, he could create something using TAO or DirectX ...
Manfred R. Bihy at 18-Jan-11 15:12pm
So now you're commenting on your own comments. Can you explain Sherlock?
Espen Harlinn at 18-Jan-11 15:18pm
A technical glitch, probably a bug in the soft tissue computational matrix :) BTW: Sherlock might be comming back soon, at least I hope he will, or we will all be lost. Mycroft can't be bothered at all, but then that's not unusual :)
Dalek Dave at 20-Jan-11 3:40am
Good Call.
Espen Harlinn at 20-Jan-11 12:26pm
Thanks Dalek!
Rate this: bad
Please Sign up or sign in to vote.

Solution 5

To optimize performance and keep animation smooth you need two things:
1) Keep using double buffering;
2) Do not use Refresh (of course), but don't use Invalidate() as well: use Invalidate with parameters: invalidate only a part of your scene.
Oh, yes, if you can -- move to WPF -- much better for animation (of different sorts, not only using available animation-related library classes).
As Marcus added a note on the use of thread (BackgroundWorker or explicitly created thread) -- thank you Markus -- I want to add a note.
The body of the animation thread should do two things: it should periodically update the data used in Paint event and then perform invalidation. Important to remember: Invalidate method should be called on UI thread, so the animation thread should perform inter-thread invocation:
MyCanvasControl.Invoke(new System.Action<Control>(
    (Control control) => {
    }), MyCanvasControl);
Note, that is the rendering data is updated due to the UI user, invalidation should be done immediately on UI thread through a direct call to Invalidate, no use of any thread synchronization primitive needed: Invoke takes care of it.
Finally, it's the best to avoid using of the timer.
Espen Harlinn at 18-Jan-11 10:31am
5+ Heading in the same direction :)
Marcus Kramer at 18-Jan-11 12:03pm
Definitely the right direction. I think he would be wise to use some background workers to do his state and data handling, and another for his UI using double buffering techniques, so that they are somewhat independent of each other.
SAKryukov at 18-Jan-11 13:57pm
Thank you Marcus, this is another good point. I would say: it's the best to avoid use of the timer. Actually, I will add the note to my answer...
Espen Harlinn at 18-Jan-11 14:50pm
Actually I think this one of those places where the standard WM_TIMER has a lot going for it. If the system is busy, it will hurt the animation, otherwise - when there is cycles to burn it will be able to take advantage of the available processing power.
SAKryukov at 18-Jan-11 15:00pm
You see, as to timing, thread provides flexibility is saving time. The thread is sleeping big part of its activity. The actual sleep time is a bit more that specified and is extended it the CPUs are more busy. This is good, compared to the timer. The timer event will be fired anyway, but there is a change that a previous handler is not. Not only this is dangerous (not so bad with thorough programming, but why bother?); this is also more wasteful in terms of CPU time. Please, no timers -- a thread is better.
Espen Harlinn at 18-Jan-11 15:10pm
All I would do in a timer event (WM_TIMER) handler, for a GDI+ based solution, is to invlalidate the rectangle occupied by the bitmap - no painting. Painting would only be done as part of the OnPaint handling ...
SAKryukov at 18-Jan-11 17:20pm
That's what happens with thread. Thread trigger invalidation via Invoke, UI thread performs invalidation; but the WM_PAINT (it you prefer this level) happens when it happens and does the real rendering. Invalidation itself is have nothing to do with painting.
Dalek Dave at 20-Jan-11 3:41am
Helpful to OP.
Rate this: bad
Please Sign up or sign in to vote.

Solution 6

i did all of these
but i want more some thing more than double buffering
this line of code in OnPaint method make computer to its knees
e.Graphics.DrawImage(Picture, ClientRectangle, rect, GraphicsUnit.Pixel);
i should refresh all the screen for zoom in/out (am i right?)
and it should be refreshed 20Times in a loop
please tell me what is renderer? is that any relation with my problem?
Espen Harlinn at 19-Jan-11 8:57am
Only invalidate the rectangle occupied by the image using Control.Invalidate
Refresh is way too expensive. Windows tries it's best to optimize painting - See WM_PAINT
E.F. Nijboer at 19-Jan-11 9:16am
Are you doing the actual processing in a thread like I mentioned? And can it abort? The user might zoom fast in small steps while your program tries to create all unnecessary frames in between. Aborting/updating in between would be faster this way. If you do not use multi threaded then each step is a message that is processed and drawn to the screen in the paint event. That can take up quite some time and builds up more and more work. Those in between are not actually needed and can be skipped because only the last one (the zoom actual state) is important and should be shown on screen.
Another trick is to simply wait for about 100 or 200 milliseconds after the user requested a zoom action before start painting again and reset this time if the user wants to zoom any further. With this you wait until the user is done zooming (using the scrollwheel for example) before start processing and painting.
Espen Harlinn at 19-Jan-11 10:05am
I guess that he is trying to do this with quite large files - not exactly on the level of sprite animations that GDI+ can comfortably execute at 20+ fps.
E.F. Nijboer at 19-Jan-11 12:19pm
It should be perfectly possible for the same reason it is possible to move around screens in window XP without any holdup or stuttering. If it is just moving around a large image it should be no problem.
Espen Harlinn at 19-Jan-11 14:35pm
You're absolutely right - it's the probably the scaling thats killing the performance
E.F. Nijboer at 20-Jan-11 3:26am
Although StretchBlt isn't the best approach it also has a memory leak in windows Vista:
Here some sample implementation of stretching that could get scaling on the right track:
Rate this: bad
Please Sign up or sign in to vote.

Solution 7

If your having trouble with the speed of your rendering so that the animation looks jumpy, you can use XNA to render onto that panel. It's a little more effort rendering everything with textures and 2D quads but the end result is worth it.
But simply put create a panel that you want to render onto, and initialise a GraphicsDevice withe the window handle of that panel.
PresentationParameters pp = new PresentationParameters();
pp.BackBufferCount = 1;
pp.IsFullScreen = false;
pp.SwapEffect = SwapEffect.Copy;
pp.BackBufferWidth = width of render area;
pp.BackBufferHeight = size of render area;
pp.PresentationInterval = PresentInterval.One;
pp.BackBufferFormat = SurfaceFormat.Color;
pp.MultiSampleType = MultiSampleType.None;
GraphicsDevice m_device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware, myPane.Handle, pp);
After that you are free to use your graphics device to render directly onto your panel at any time with hardware acceleration, although you should probably use it from the OnPaint of the panel, so that you can redraw when necessary.
Rate this: bad
Please Sign up or sign in to vote.

Solution 8

i am using vs2008 .net framework 3.5 sp3 but here is no reference for microsoft.xna.framework. or something like that.
so should i download "Microsoft XNA Game Studio 3.1"? u know it tokes 73mb!!! this means 24 hour downloading for me (in this desert) Big Grin | :-D
how can i have only xna dlls and just run it?
sorry if it`s a foolish question... Poke tongue | ;-P
Rate this: bad
Please Sign up or sign in to vote.

Solution 9

dear Nijboer & Harlinn & Genius and others
1) i should animate the zoom in/out (not only drawing the zoomed image) because "boss said" Sigh | :sigh:
2) i used Timer because thread managing was hard to implement for me (i am sill working on it but it *should be thread safe *using Invoke and *terminate thread after OnMouseKeyUp and some other issues)
3) i inflate a rectangle on my zoomable image and use draw image to only show the inflated rectangle on my control
4) after all of these it going harder (i should run sum threads to draw cars on my image)!!!
i am thinking to change total solution with XNA or some thing like that
by the way , is WPF a better solution than XNA? (please attention that it should deal with many many threads , mouse and keyboard events) and totally it going harder to manage
thanks all Blush | :O

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

  Print Answers RSS
0 OriginalGriff 490
1 Maciej Los 305
2 Richard MacCutchan 270
3 Mathew Soji 220
4 BillWoodruff 210
0 OriginalGriff 8,834
1 Sergey Alexandrovich Kryukov 7,477
2 DamithSL 5,689
3 Maciej Los 5,329
4 Manas Bhardwaj 4,986

Advertise | Privacy | Mobile
Web04 | 2.8.1411028.1 | Last Updated 24 Jan 2011
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid

CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100