在现代的图形用户界面(GUI)应用中,动画的展示是提升用户体验的重要手段之一。GIF格式因其简单和兼容性好,常被用于展示动画。然而,处理GIF动画往往伴随着资源消耗和内存泄漏的问题。本文将介绍一种高效处理GIF动画文件的方法,这种方法不仅能够避免内存泄漏,还能优化资源使用,提高应用性能。
在设计解决方案时,采用了.NET Framework中的System.Threading.Timer
类来组织动画帧的连续播放。每一帧都有其特定的延迟时间,通过解析GIF文件来获取这些延迟值。为了实现这一点,创建了一个名为ParseGif
的类。这种方法的结果是,能够一致地改变帧,并预先冻结它们以避免内存泄漏。关于如何预防内存泄漏的更多信息,可以参考文章《在WPF应用中查找内存泄漏》。
解决方案分为两个项目:一个用于动画类的项目,另一个用于测试功能。本文将重点介绍测试项目。没有添加一个动画文件,因为那样不会展示任何内容。决定在RichTextBox
中添加100个表情符号,这在展示性能方面是最佳选择。
以下是加载动画的主要函数LoadSmile
的代码示例:
private static void OnAnimatedBitmapChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
AnimatedImage control = (AnimatedImage)obj;
control.UpdateAnimatedBitmap();
RoutedPropertyChangedEventArgs<Bitmap> e = new RoutedPropertyChangedEventArgs<Bitmap>((Bitmap)args.OldValue, (Bitmap)args.NewValue, AnimatedBitmapChangedEvent);
control.OnAnimatedBitmapChanged(e);
}
public static readonly RoutedEvent AnimatedBitmapChangedEvent = EventManager.RegisterRoutedEvent("AnimatedBitmapChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<Bitmap>), typeof(AnimatedImage));
...
public void LoadSmile(Bitmap bitmap)
{
this.AnimatedBitmap = bitmap;
}
当更改图像时,将执行处理函数。在该函数中,执行并解构GIF文件,并启动计时器以更改帧。
以下是更新动画帧的函数UpdateAnimatedBitmap
的代码示例:
private void UpdateAnimatedBitmap()
{
try
{
int nTimeFrames = GetFramesCount();
_nCurrentFrame = 0;
if (nTimeFrames > 0)
{
MemoryStream stream = new MemoryStream();
AnimatedBitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Gif);
stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
ParseGif(buffer);
_BitmapSources = new List<BitmapSource>(nTimeFrames);
stream.Dispose();
FillBitmapSources(nTimeFrames);
timer = new Timer(OnFrameChanged, null, -1, -1);
StartAnimate();
}
else
{
Bitmap bitmap = new Bitmap(AnimatedBitmap);
_BitmapSources = new List<BitmapSource>(1);
_BitmapSources.Add(CreateBitmapSourceFromBitmap(bitmap));
Source = _BitmapSources[0];
}
}
catch { }
}
在文章开头附加的项目中,可以找到此代码中执行的一些函数。如所见,解决方案的架构并不复杂。可以使用内置的Windows Forms类ImageAnimator
,但作为WPF中大规模解决方案的实践,这个类并不适合。
以下是实现动画帧连续播放的代码示例:
private void OnFrameChanged(object obj)
{
try
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new VoidDelegate(delegate { ChangeSource(); }));
}
catch { }
}
void ChangeSource()
{
try
{
timer.Change(Delays[_nCurrentFrame] * 10, 0);
Source = _BitmapSources[_nCurrentFrame++];
_nCurrentFrame = _nCurrentFrame % _BitmapSources.Count;
}
catch { }
}
类实现了IDisposable
接口,允许在不需要表情符号时释放资源:
public void Dispose()
{
try
{
timer.Change(-1, -1);
timer.Dispose();
_BitmapSources.Clear();
Source = null;
}
catch { }
}