Skip to content

Commit

Permalink
Merge pull request #69 from thomaslevesque/speed-ratio
Browse files Browse the repository at this point in the history
Add support for controlling animation speed
  • Loading branch information
thomaslevesque authored Jan 5, 2020
2 parents ead8b3c + 72ec92d commit 922c03b
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 116 deletions.
42 changes: 38 additions & 4 deletions WpfAnimatedGif.Demo/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
Expand Down Expand Up @@ -57,21 +59,51 @@
Click="btnOpenUrl_Click"/>
</Grid>

<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Orientation="Horizontal">
<Label Content="Repeat" />
<RadioButton Margin="5" Content="Default" IsChecked="{Binding UseDefaultRepeatBehavior}" />
<RadioButton Margin="5" Content="Forever" IsChecked="{Binding RepeatForever}" />
<RadioButton Margin="5" Content="Specific count" IsChecked="{Binding UseSpecificRepeatCount}" />
<TextBox Margin="5" Text="{Binding RepeatCount}" Width="20" />
<CheckBox Margin="5" Content="Auto start" IsChecked="{Binding AutoStart}" />
<CheckBox Margin="5" Content="Visibility" IsChecked="{Binding GifVisible}" />
</StackPanel>

<StackPanel Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right" Orientation="Horizontal">
<Button Name="btnClearImage" Content="Clear image" Click="btnClearImage_Click" Margin="5,0,0,5" />
<Button Name="btnGC" Content="GC" Click="btnGC_Click" Margin="5,0,5,5" />
</StackPanel>


<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
Orientation="Horizontal">
<CheckBox Margin="5" Content="Auto start" IsChecked="{Binding AutoStart}" />
<CheckBox Margin="5" Content="Visibility" IsChecked="{Binding GifVisible}" />
</StackPanel>

<StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">
<RadioButton IsChecked="{Binding UseDefaultDuration}" Content="Default duration" />
<RadioButton IsChecked="{Binding UseSpeedRatio}"
HorizontalContentAlignment="Stretch">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="Speed ratio" />
<TextBlock DockPanel.Dock="Right"
Width="30"
Text="{Binding SpeedRatio}" />
<Slider Margin="5,0"
Minimum="0.1" Maximum="10"
TickPlacement="BottomRight"
Ticks="1,2,3,4,5,6,7,8,9,10"
IsSnapToTickEnabled="False"
Value="{Binding SpeedRatio, Mode=TwoWay}"/>
</DockPanel>
</RadioButton>
<RadioButton IsChecked="{Binding UseDuration}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Duration" />
<TextBox Text="{Binding Duration}" Width="75" Margin="5,0" />
</StackPanel>
</RadioButton>
</StackPanel>

</Grid>
</GroupBox>
<GroupBox Header="Control" DockPanel.Dock="Bottom">
Expand All @@ -94,6 +126,8 @@
Stretch="None"
gif:ImageBehavior.AnimatedSource="{Binding SelectedImage}"
gif:ImageBehavior.RepeatBehavior="{Binding RepeatBehavior}"
gif:ImageBehavior.AnimationSpeedRatio="{Binding ActualSpeedRatio}"
gif:ImageBehavior.AnimationDuration="{Binding ActualDuration}"
gif:ImageBehavior.AutoStart="{Binding AutoStart}"
gif:ImageBehavior.AnimationCompleted="AnimationCompleted" />

Expand Down
74 changes: 74 additions & 0 deletions WpfAnimatedGif.Demo/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,80 @@ public RepeatBehavior RepeatBehavior
}
}

private double _speedRatio = 1.0;
public double SpeedRatio
{
get => _speedRatio;
set
{
_speedRatio = value;
OnPropertyChanged(nameof(SpeedRatio));
OnPropertyChanged(nameof(ActualSpeedRatio));
}
}

private Duration _duration = TimeSpan.FromSeconds(3);
public Duration Duration
{
get => _duration;
set
{
_duration = value;
OnPropertyChanged(nameof(Duration));
OnPropertyChanged(nameof(ActualDuration));
}
}

private bool _useDefaultDuration = true;
public bool UseDefaultDuration
{
get => _useDefaultDuration;
set
{
_useDefaultDuration = value;
OnPropertyChanged(nameof(UseDefaultDuration));
OnPropertyChanged(nameof(ActualDuration));
OnPropertyChanged(nameof(ActualSpeedRatio));
}
}

private bool _useSpeedRatio = false;
public bool UseSpeedRatio
{
get => _useSpeedRatio;
set
{
_useSpeedRatio = value;
if (value)
{
UseDuration = false;
}
OnPropertyChanged(nameof(UseSpeedRatio));
OnPropertyChanged(nameof(ActualSpeedRatio));
OnPropertyChanged(nameof(ActualDuration));
}
}

private bool _useDuration = false;
public bool UseDuration
{
get => _useDuration;
set
{
_useDuration = value;
if (value)
{
UseSpeedRatio = false;
}
OnPropertyChanged(nameof(UseDuration));
OnPropertyChanged(nameof(ActualDuration));
OnPropertyChanged(nameof(ActualSpeedRatio));
}
}

public Duration? ActualDuration => UseDuration ? Duration : default(Duration?);
public double? ActualSpeedRatio => UseSpeedRatio ? SpeedRatio : default(double?);

private bool _autoStart = true;
public bool AutoStart
{
Expand Down
90 changes: 50 additions & 40 deletions WpfAnimatedGif/AnimationCache.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
Expand All @@ -8,21 +10,18 @@ namespace WpfAnimatedGif
{
static class AnimationCache
{
private class CacheKey
private struct CacheKey
{
private readonly ImageSource _source;
private readonly RepeatBehavior _repeatBehavior;

public CacheKey(ImageSource source, RepeatBehavior repeatBehavior)
public CacheKey(ImageSource source)
{
_source = source;
_repeatBehavior = repeatBehavior;
}

private bool Equals(CacheKey other)
{
return ImageEquals(_source, other._source)
&& Equals(_repeatBehavior, other._repeatBehavior);
return ImageEquals(_source, other._source);
}

public override bool Equals(object obj)
Expand All @@ -35,10 +34,7 @@ public override bool Equals(object obj)

public override int GetHashCode()
{
unchecked
{
return (ImageGetHashCode(_source) * 397) ^ _repeatBehavior.GetHashCode();
}
return ImageGetHashCode(_source);
}

private static int ImageGetHashCode(ImageSource image)
Expand Down Expand Up @@ -99,53 +95,67 @@ private static Uri GetUri(ImageSource image)
}
}

private static readonly Dictionary<CacheKey, ObjectAnimationUsingKeyFrames> _animationCache = new Dictionary<CacheKey, ObjectAnimationUsingKeyFrames>();
private static readonly Dictionary<CacheKey, int> _referenceCount = new Dictionary<CacheKey, int>();
private static readonly Dictionary<CacheKey, AnimationCacheEntry> _animationCache = new Dictionary<CacheKey, AnimationCacheEntry>();
private static readonly Dictionary<CacheKey, HashSet<Image>> _imageControls = new Dictionary<CacheKey, HashSet<Image>>();

public static void IncrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior)
public static void AddControlForSource(ImageSource source, Image imageControl)
{
var cacheKey = new CacheKey(source, repeatBehavior);
int count;
_referenceCount.TryGetValue(cacheKey, out count);
count++;
_referenceCount[cacheKey] = count;
var cacheKey = new CacheKey(source);
if (!_imageControls.TryGetValue(cacheKey, out var controls))
{
_imageControls[cacheKey] = controls = new HashSet<Image>();
}

controls.Add(imageControl);
}

public static void DecrementReferenceCount(ImageSource source, RepeatBehavior repeatBehavior)
public static void RemoveControlForSource(ImageSource source, Image imageControl)
{
var cacheKey = new CacheKey(source, repeatBehavior);
int count;
_referenceCount.TryGetValue(cacheKey, out count);
if (count > 0)
var cacheKey = new CacheKey(source);
if (_imageControls.TryGetValue(cacheKey, out var controls))
{
count--;
_referenceCount[cacheKey] = count;
}
if (count == 0)
{
_animationCache.Remove(cacheKey);
_referenceCount.Remove(cacheKey);
if (controls.Remove(imageControl))
{
if (controls.Count == 0)
{
_animationCache.Remove(cacheKey);
_imageControls.Remove(cacheKey);
}
}
}
}

public static void AddAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation)
public static void Add(ImageSource source, AnimationCacheEntry entry)
{
var key = new CacheKey(source, repeatBehavior);
_animationCache[key] = animation;
var key = new CacheKey(source);
_animationCache[key] = entry;
}

public static void RemoveAnimation(ImageSource source, RepeatBehavior repeatBehavior, ObjectAnimationUsingKeyFrames animation)
public static void Remove(ImageSource source)
{
var key = new CacheKey(source, repeatBehavior);
var key = new CacheKey(source);
_animationCache.Remove(key);
}

public static ObjectAnimationUsingKeyFrames GetAnimation(ImageSource source, RepeatBehavior repeatBehavior)
public static AnimationCacheEntry Get(ImageSource source)
{
var key = new CacheKey(source);
_animationCache.TryGetValue(key, out var entry);
return entry;
}
}

internal class AnimationCacheEntry
{
public AnimationCacheEntry(ObjectKeyFrameCollection keyFrames, Duration duration, int repeatCountFromMetadata)
{
var key = new CacheKey(source, repeatBehavior);
ObjectAnimationUsingKeyFrames animation;
_animationCache.TryGetValue(key, out animation);
return animation;
KeyFrames = keyFrames;
Duration = duration;
RepeatCountFromMetadata = repeatCountFromMetadata;
}

public ObjectKeyFrameCollection KeyFrames { get; }
public Duration Duration { get; }
public int RepeatCountFromMetadata { get; }
}
}
Loading

0 comments on commit 922c03b

Please sign in to comment.