Skip to content

Commit

Permalink
Implement AnimationSpeedRatio & AnimationDuration
Browse files Browse the repository at this point in the history
  • Loading branch information
thomaslevesque committed Jan 5, 2020
1 parent 6868899 commit 72ec92d
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 9 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
101 changes: 96 additions & 5 deletions WpfAnimatedGif/ImageBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static void SetAnimatedSource(Image obj, ImageSource value)
"AnimatedSource",
typeof(ImageSource),
typeof(ImageBehavior),
new UIPropertyMetadata(
new PropertyMetadata(
null,
AnimatedSourceChanged));

Expand Down Expand Up @@ -85,9 +85,75 @@ public static void SetRepeatBehavior(Image obj, RepeatBehavior value)
"RepeatBehavior",
typeof(RepeatBehavior),
typeof(ImageBehavior),
new UIPropertyMetadata(
new PropertyMetadata(
default(RepeatBehavior),
RepeatBehaviorChanged));
AnimationPropertyChanged));

/// <summary>
/// Gets the value of the <c>AnimationSpeedRatio</c> attached property for the specified object.
/// </summary>
/// <param name="obj">The element from which to read the property value.</param>
/// <returns>The speed ratio for the animated image.</returns>
public static double? GetAnimationSpeedRatio(DependencyObject obj)
{
return (double?)obj.GetValue(AnimationSpeedRatioProperty);
}

/// <summary>
/// Sets the value of the <c>AnimationSpeedRatio</c> attached property for the specified object.
/// </summary>
/// <param name="obj">The element on which to set the property value.</param>
/// <param name="value">The speed ratio of the animated image.</param>
/// <remarks>The <c>AnimationSpeedRatio</c> and <c>AnimationDuration</c> properties are mutually exclusive, only one can be set at a time.</remarks>
public static void SetAnimationSpeedRatio(DependencyObject obj, double? value)
{
obj.SetValue(AnimationSpeedRatioProperty, value);
}

/// <summary>
/// Identifies the <c>AnimationSpeedRatio</c> attached property.
/// </summary>
public static readonly DependencyProperty AnimationSpeedRatioProperty =
DependencyProperty.RegisterAttached(
"AnimationSpeedRatio",
typeof(double?),
typeof(ImageBehavior),
new PropertyMetadata(
null,
AnimationPropertyChanged));

/// <summary>
/// Gets the value of the <c>AnimationDuration</c> attached property for the specified object.
/// </summary>
/// <param name="obj">The element from which to read the property value.</param>
/// <returns>The duration for the animated image.</returns>
public static Duration? GetAnimationDuration(DependencyObject obj)
{
return (Duration?)obj.GetValue(AnimationDurationProperty);
}

/// <summary>
/// Sets the value of the <c>AnimationDuration</c> attached property for the specified object.
/// </summary>
/// <param name="obj">The element on which to set the property value.</param>
/// <param name="value">The duration of the animated image.</param>
/// <remarks>The <c>AnimationSpeedRatio</c> and <c>AnimationDuration</c> properties are mutually exclusive, only one can be set at a time.</remarks>
public static void SetAnimationDuration(DependencyObject obj, Duration? value)
{
obj.SetValue(AnimationDurationProperty, value);
}

/// <summary>
/// Identifies the <c>AnimationDuration</c> attached property.
/// </summary>
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.RegisterAttached(
"AnimationDuration",
typeof(Duration?),
typeof(ImageBehavior),
new PropertyMetadata(
null,
AnimationPropertyChanged));

/// <summary>
/// Gets the value of the <c>AnimateInDesignMode</c> attached property for the specified object.
Expand Down Expand Up @@ -348,7 +414,7 @@ static void ImageControlUnloaded(object sender, RoutedEventArgs e)
controller.Dispose();
}

private static void RepeatBehaviorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
private static void AnimationPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
Image imageControl = o as Image;
if (imageControl == null)
Expand Down Expand Up @@ -500,15 +566,40 @@ private static ObjectAnimationUsingKeyFrames GetAnimation(Image imageControl, Bi
{
KeyFrames = cacheEntry.KeyFrames,
Duration = cacheEntry.Duration,
RepeatBehavior = GetActualRepeatBehavior(imageControl, cacheEntry.RepeatCountFromMetadata)
RepeatBehavior = GetActualRepeatBehavior(imageControl, cacheEntry.RepeatCountFromMetadata),
SpeedRatio = GetActualSpeedRatio(imageControl, cacheEntry.Duration)
};

AnimationCache.AddControlForSource(source, imageControl);
return animation;
}

return null;
}

private static double GetActualSpeedRatio(Image imageControl, Duration naturalDuration)
{
var speedRatio = GetAnimationSpeedRatio(imageControl);
var duration = GetAnimationDuration(imageControl);

if (speedRatio.HasValue && duration.HasValue)
throw new InvalidOperationException("Cannot set both AnimationSpeedRatio and AnimationDuration");

if (speedRatio.HasValue)
return speedRatio.Value;

if (duration.HasValue)
{
if (!duration.Value.HasTimeSpan)
throw new InvalidOperationException("AnimationDuration cannot be Automatic or Forever");
if (duration.Value.TimeSpan.Ticks <= 0)
throw new InvalidOperationException("AnimationDuration must be strictly positive");
return naturalDuration.TimeSpan.Ticks / (double)duration.Value.TimeSpan.Ticks;
}

return 1.0;
}

private static BitmapSource ClearArea(BitmapSource frame, FrameMetadata metadata)
{
DrawingVisual visual = new DrawingVisual();
Expand Down

0 comments on commit 72ec92d

Please sign in to comment.