Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent divide-by-zero in ImageAnimator for images with 0 delay between frames #56223

Merged
merged 2 commits into from
Jul 24, 2021
Merged

Prevent divide-by-zero in ImageAnimator for images with 0 delay between frames #56223

merged 2 commits into from
Jul 24, 2021

Conversation

jeffhandley
Copy link
Member

Fixes #55972

Per the GIF spec, a frame delay can have a value of 0. That was historically used for 2 purposes:

  1. Allow the animation to progress as fast as the renderer chose to animate
  2. Utilize the GIF spec feature that has animation wait for user input (mouse click, key press)

While the latter of those features is archaic, we still need to handle 0-delay frames. We assign them our animation delay speed to be "1 tick". It's possible the frame could get skipped if a tick takes longer than the tick time, but this gives a uniform speed for GIFs where all frame delays are 0.

To prevent the divide-by-zero:

  1. If a frame delay is <= 0, apply the tick speed
  2. Ensure the total animation time increases with each frame, guarding against overflows
  3. Update the ShouldAnimate property to check that the total animation time is > 0

@ghost
Copy link

ghost commented Jul 23, 2021

Tagging subscribers to this area: @safern, @tarekgh
See info in area-owners.md if you want to be subscribed.

Issue Details

Fixes #55972

Per the GIF spec, a frame delay can have a value of 0. That was historically used for 2 purposes:

  1. Allow the animation to progress as fast as the renderer chose to animate
  2. Utilize the GIF spec feature that has animation wait for user input (mouse click, key press)

While the latter of those features is archaic, we still need to handle 0-delay frames. We assign them our animation delay speed to be "1 tick". It's possible the frame could get skipped if a tick takes longer than the tick time, but this gives a uniform speed for GIFs where all frame delays are 0.

To prevent the divide-by-zero:

  1. If a frame delay is <= 0, apply the tick speed
  2. Ensure the total animation time increases with each frame, guarding against overflows
  3. Update the ShouldAnimate property to check that the total animation time is > 0
Author: jeffhandley
Assignees: -
Labels:

area-System.Drawing

Milestone: -


/// <summary>
/// Whether animation should progress, respecting the image's animation support
/// and if there are animation frames or loops remaining.
/// </summary>
private bool ShouldAnimate => Animated ? (_loopCount == 0 || _loop <= _loopCount) : false;
private bool ShouldAnimate => TotalAnimationTime > 0 ? (_loopCount == 0 || _loop <= _loopCount) : false;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the behavior at line 160, preventing animation from occurring if the total animation time is <= 0.

@jeffhandley jeffhandley requested a review from safern July 23, 2021 18:31
lastEndTime += delay > 0 ? delay : ImageAnimator.AnimationDelayMS;

// Guard against overflows
if (lastEndTime < _totalAnimationTime)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for cases where delay is negative? does that ever happen/is it legal or are you just being careful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if just being careful, could you instead simply force delay to be no less than zero? then I think you wouldn't need _totalAnimationTime?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm being careful mostly. Here's how it comes together though.

  1. If the delay is <= 0, then apply the default animation delay
  2. If the accumulated last end time overflows, then collapse all of the remaining frames into that instant (essentially skipping any frames beyond the overflow of animation time)
  3. Ensure that TotalAnimationTime is a non-zero value if any frames are to be animated
  4. Eliminate the array access from TotalAnimationTime, replacing that with _totalAnimationTime

@jeffhandley jeffhandley self-assigned this Jul 24, 2021
@AraHaan
Copy link
Member

AraHaan commented Jul 24, 2021

Speaking of animated images, I wonder when System.Drawing.Common's ImageAnimator can pick up png images with multiple frames as animated?

I guess I could try to see if I can propose an api to help provide support for animated png's if possible on the .NET side of things in .NET 7 if that is required then somehow get the image animator to actually check for it all.

And yes this is a known issue with people trying to manually hack in their own "Image animator" for their picture boxes as well.

@danmoseley
Copy link
Member

Unrelated failures.

Copy link
Member

@safern safern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for fixing so quickly!

@jeffhandley jeffhandley deleted the jeffhandley/imageanimator/0 branch July 26, 2021 22:12
@ghost ghost locked as resolved and limited conversation to collaborators Aug 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Animated Gif - division by zero
4 participants