2
\$\begingroup\$

I have written the following control which allows loading a gif from embedded resource, file, or raw Image. It allows for more granular control over the direction and frames per second of animated image types.

The problem I face, is it is quite laggy during the frame switching (unlike the PictureBox control which doesn't offer any animation control but does animate gifs)

Given this code and the objective, what do you think could be done to improve it's performance. Presently, it lags when animation is set to 10 FPS, and ideally, I would like user control up to 200 FPS.

public partial class ImageBox : UserControl
{
    private FrameDimension dimension;
    private int frameCount;
    private int currentFrame = -1;
    private int step = 1;

    private Timer timer;

    public int FrameCount { get { return FrameCount; } }

    public bool Animate { get { return timer.Enabled; } set { timer.Enabled = value; } }

    public enum AnimationDirectionEnum
    {
        Forward,
        Reverse
    }

    public AnimationDirectionEnum AnimateDirection { get; set; } 

    private int _FPS;
    public int FPS
    {
        get { return _FPS; }
        set
        {
            if (value >= 0 && value <= 200)
            {
                _FPS = value;
                if (_FPS > 0)
                {
                    timer.Interval = 1000 / value;
                }
                else
                {
                    timer.Interval = 2000;
                    timer.Enabled = false;
                }
            }
        }
    }

    public void LoadImage(FileInfo path)
    {
        if(File.Exists(path.ToString()))
        {
            LoadImage(Image.FromFile(path.ToString()));
        }
    }

    public void LoadImage(string resource)
    {
        Stream res = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource);
        LoadImage(Image.FromStream(res));
    }

    public void LoadImage(Image image)
    {
        currentFrame = -1;
        BackgroundImage = image; //initialize
        dimension = new FrameDimension(BackgroundImage.FrameDimensionsList[0]); //gets the GUID
        frameCount = BackgroundImage.GetFrameCount(dimension); //total frames in the animation
    }

    public ImageBox()
    {
        DoubleBuffered = true;
        InitializeComponent();
        timer = new Timer();
        _FPS = 10;
        timer.Enabled = false;
        timer.Interval = 100;
        timer.Tick += AnimateFrame;
        currentFrame = -1;
    }

    private void AnimateFrame(object sender, EventArgs e)
    {

        currentFrame += step;

        //if the animation reaches a boundary...
        if (currentFrame >= frameCount || currentFrame < 1)
        {
            if (AnimateDirection == AnimationDirectionEnum.Reverse)
            {
                step *= -1; //...reverse the count
                currentFrame += step; //apply it
            }
            else
            {
                currentFrame = 0; //...or start over
            }
        }
        BackgroundImage.SelectActiveFrame(dimension, currentFrame);
        Invalidate();
        //Refresh();
    }

    public Image GetFrame(int index)
    {
        if(index <= frameCount && index >= 0 && BackgroundImage != null)
        {
            BackgroundImage.SelectActiveFrame(dimension, index); //find the frame
            return (Image)BackgroundImage.Clone(); //return a copy of it
        } else
        {
            return null;
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Is there a reason you're targeting 200 FPS? Such a high number is never required and I smell a misguidance in your design. \$\endgroup\$ – Mast Aug 20 '16 at 17:11
  • \$\begingroup\$ @Mast - it is just an arbitrary maximum. The number is based on my average FPS in certain games (so I certainly have the equipment to manage it). Instead of focusing on the peak FPS objective, lets try and focus on why even at 10 FPS, it is laggy. A hint, the control is locking the ui when switching the image for about 50-150ms (approx) per frame. \$\endgroup\$ – Kraang Prime Aug 20 '16 at 17:17
  • \$\begingroup\$ Is there a reason you're locking the UI every frame? \$\endgroup\$ – Mast Aug 20 '16 at 19:11
  • 1
    \$\begingroup\$ The default min timer resolution is about 15.6 milliseconds (see: Timer-Resolution.docx), so your 200 fps (5 ms) would require mucking around with PInvoking timeBeginPeriod/timeEndPeriod to set the faster timer interval. You may want to look at how MS wrote the ImageAnimator Class for some ideas as well; it is used by the PictureBox Class. \$\endgroup\$ – TnTinMn Aug 20 '16 at 19:39
  • 2
    \$\begingroup\$ Why are you creating a clone at all for the BackgroundImage? Just set it to the image once. After calling img.SelectActiveFrame, call this.Invalidate to force a repaint. \$\endgroup\$ – TnTinMn Aug 20 '16 at 20:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.