Click here to Skip to main content
16,004,564 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
So I have a project that utilizes a Timer control and an image stored in a resource file.

The Resource file is an image that's 6144 x 768.

I have a string of code that sets a Step variable from 1 to 6.

When the Timer control fires, it takes the image, crops it so only part of it is displayed, then calls the form's Paint event.
The Timer then increments the Step variable by 1, once it gets to 6, the variable is reset.

In this way I've created an animation loop.

Everything works great- except that no matter what the Interval on the Timer control is set to, the animation itself is very, very slow. There's no flickering, no stuttering. It runs at what appears to be a constant speed- it just needs to be... faster, I guess.

What would you suggest as a better alternative to the standard Timer control for this particular purpose?

The user needs to see the animation happening and it runs in a loop, so I don't think a Do While is the answer here.

What I have tried:

This is the code for the paint operation that puts the image on the screen without flickering-

private void FormPaint(object MainScreen, PaintEventArgs e)
{

        var g = e.Graphics;
        Bitmap TransferBitmap = new Bitmap(AnimationProject.Properties.Resources.Title);
        RectangleF cloneRect = new RectangleF(Global.AnimationPosition, 0, 1064, 768);
        System.Drawing.Imaging.PixelFormat format = TransferBitmap.PixelFormat;
        Bitmap cloneBitmap = TransferBitmap.Clone(cloneRect, format);
        g.DrawImage(cloneBitmap, 0, 0, (this.Width + 60), this.Height);


}


And this is the code that the Timer fires off-

C#
private void TitleTimer_Tick(object sender, EventArgs e)
{

    this.Invalidate();

    int InterVal = ((AnimationProject.Properties.Resources.Fan.Width) / 1024) - 1;

    if (Global.AnimationCounter < InterVal)
    {
        Global.AnimationPosition = Global.AnimationPosition + 1024;
        Global.AnimationCounter++;
        
    }

    if (Global.AnimationCounter == InterVal)
    {
       Global.AnimationPosition = 0;
       Global.AnimationCounter = 0;
        
    }

   


}
Posted
Comments
PIEBALDconsult 9-Aug-24 19:16pm    
Which Timer control?
Daniel Davis 2024 9-Aug-24 22:31pm    
System.Windows.Forms.Timer

A bog standard Windows Forms Timer Control.
PIEBALDconsult 9-Aug-24 23:19pm    
OK, I've only ever used System.Timers.Timer -- and that not recently.
I've heard bad things about that other one.
Apparently, there's also System.Threading.Timer :shrug:

Your initial image is just under 19MBs in size. That's a lot of data to be messing with on every frame.

So, don't. If your image has to be a certain size when rendered, create a Bitmap object ONCE at the size you need OUTSIDE the Paint event, then use that object to paint with instead of doing a lot of work on 19MBs of data on every frame.

Oh, and you are leaking resources like crazy. You're not disposing of the Bitmap object you're creating. when you're done with them.
 
Share this answer
 
Comments
Daniel Davis 2024 10-Aug-24 11:00am    
So I've altered my code so most of the heavy lifting isn't done in the paint operation.

The code at form load creates an array of five bitmaps, cuts the appropriate segment from the main animation image, and puts each 1024x768 image into their spots in the array.
So then the timer just changes the reference number and loops when it gets to 5 back to the beginning.
The paint cycle is just grabbing the correct 1024x768 image from the array the Timer asks for and painting that onto the form.
But somehow it's still very slow. I think something is gumming up inside the code that I'll have to step through to check.
Thank you very much for the ideas about optimization. The code IS much more efficient now.
We can't really tell what is going on here - we can't test your code in identical circumstances.

But ... there are a few things you need to know about how windows and .NET works!
The first is that Windows - and timers - aren't real time. You don't get a timer tick event exactly every 1/10th of a second (if your Interval is set to 100ms) but at some point after the 1/10th of a second has expired, and that applies to all forms of Timer. So if the system is busy, you could be delayed a significant period before your code executes.

Secondly, the Paint event is special: it only executes when the rest of the system is idle, it does not execute immediately after the call to Invalidate, or even when the event handler that called Invalidate exits - and multiple Invalidate calls will be combined into a single call so when your system slows down you won't get changes when you expect them.

Both of these will slow the visual effect of the "animation" - and doing complex tasks like sampling parts of an image in the Paint event isn't going to help that because it'll make the Paint event take significant time - prepare the images outside the Paint event (and preferably in advance!) instead of inside it.

You may be able to improve matters by doing the bitmap manipulation in a background thread instead of on the UI thread (and Invalidate is safe to use off the main thread because it doesn't access any controls directly, just sets up the system to issue a Paint event when it feels ready).

But the best solution is not to do this at all, but to prepare the animation before you start displaying it: either as a set of Images so you just draw an existing Image each time your Paint event or Timer fires, or as a GIF type file you can display directly with no further intervention from you.

These would also have the advantage that your app will not run out of memory really quickly: you don't Dispose of the Graphics contexts you create or the "used Images" and the latter with cause the GC to fire up at random intervals while the former will exhaust Windows handles very quickly and they are a very limited resource!
 
Share this answer
 
v2
It actually turns out that what was slowing down the processing more than anything else was...

The system resizing the animation image.

My screen resolution is a native 1920 x 1080. The image in question was (as I previously mentioned) a 6144 x 768 file, cut up into separate 1024 x 768 chunks for each animation frame. So, even when taking each chunk and putting them into a premade Bitmap for every frame and then calling it, the computer still had to take that 1024 x 768 image and resize it to fill the screen.

The end solution is one I never thought of until now-

I made the initial image BIGGER. Since each chunk is now set to that native 1920x1080 resolution, it fills the screen without the computer having to make it bigger.

That means less processing done on each frame, which means less work, which means the timer no longer gets bogged down, which means the fan animation I was creating now operates perfectly. And thanks to all your help, the code is much cleaner as well.

When the program loads, I'm going to have it check to see if the resolution is low (1024x768) or higher (1920x1080) and use the appropriate animation set.

Thanks everyone so much! I am extremely grateful to all of you.
 
Share this answer
 

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



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900