【2012.12.18更新】WPF支持GIF的各种方法

时间:2021-07-24 00:17:21

2012.12.18更新:修复下载链接



已知WPF的Image元素只能显示GIF图片的第一帧,而MediaElement不能加载作为资源或内嵌的资源的GIF图片,所以网上有几种实现方法。

我抄袭网上提供的方法,改头换面后作为自己的GifImage实现。本文的前半部分介绍我的GifImage实现;后半部分做实验,将我的GifImage和网上现存的几种Gif支持方法做性能上的比较。

GifImage

我抄袭了这些地方提供的代码:

GifImage继承自FrameworkElement,添加了Source、Stretch、StretchDirection依赖项属性,用法就跟标准Image元素差不多。从GIF里分解出各帧及其延续时间后,我在OnRender里自行绘制,并启动DispatcherTimer计时,以便按时绘制下一帧。

解析GIF需要GifFormat类的帮助。GifFormat的构造函数需要Stream对象,构造函数认为从该Stream对象中可以读到gif文件,然后按字节解析。

GIF图片是由很多帧构成的,每一帧有延续时间、处置方法、左边、上边等属性,当然还有最重要的图像数据。GifFrame类就代表GIF图片里的帧。

经GifFormat解析后的数据可由LogicalScreenWidth、LogicalScreenHeight和GetFrames方法获得。

每当设置Source属性,如果是gif图片,就会重新创建一个新的GifFormat,然后启动timer。

当然,Source URI的方案是多种多样的,GifImage支持http、ftp、file、pack。

显示GIF的两个重点在MeasureOverride和OnRender方法,它们考虑了Stretch、StretchDirection、Width、Height等属性。

比较

周银辉的GifImageLib提供对GIF图片的支持。他的GifImage的继承链是FrameworkElement <-Control <-ContentControl <-UserControl<-GifImage。他的GifImage内含Canvas,Canvas内含N个Image,每个Image显示GIF图片的一帧。(N等于GIF图片的帧数)设置GifImage.Source来显示GIF图片。

asprodotru的WpfAnimatedControl中的AnimatedImage是用来显示gif图片的。它继承自Image元素,通过按时改变Image.Source以实现动画效果。(GIF里的有些帧需要与前一帧叠加才能显示出正确的影像,我不知道他只设置Image.Source为单一帧怎么保证正确显示的,我还没完全看懂。)设置AnimatedBitmap或调用LoadSmile方法来显示图片。

测试配置

测试用两幅gif图片,如下。

【2012.12.18更新】WPF支持GIF的各种方法

wrong.gif,19.3KB

【2012.12.18更新】WPF支持GIF的各种方法

oh.gif,51.2KB


代码如下

<Window xmlns:my="clr-namespace:Gqqnbig.Windows.Controls;assembly=GifImage"  x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="187" d:DesignWidth="349">
<Viewbox>
<UniformGrid Columns="10" Name="grid"/>
</Viewbox>
</Window>

    public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

for (int i = 0; i < 100; i++)
{
//Gqqnbig.Windows.Controls.GifImage image = new Gqqnbig.Windows.Controls.GifImage();
//image.Source = "pack://Application:,,,/OH.gif";
//grid.Children.Add(image);

//GifImageLib.GifImage image = new GifImageLib.GifImage();
//image.Source = "pack://Application:,,,/OH.gif";
//grid.Children.Add(image);

//WpfAnimatedControl.AnimatedImage image = new WpfAnimatedControl.AnimatedImage();
//var im = System.Drawing.Bitmap.FromFile("OH.gif");
//image.LoadSmile((System.Drawing.Bitmap)im);
//grid.Children.Add(image);


}
}
}

取样方法

两幅gif图片都进行测试。

运行后,每分钟记录一次CPU占用率、private bytes[1]、working set[2]的大小。共记录三次。

测试结果

  我的GifImage GifImageLib AnimatedControl
  CPU Private bytes Working set CPU Private bytes Working set CPU Private bytes Working set
第一次测量 9 95136 80768 11 94780 82096 3 100248 94428
第二次测量 2 94320 80767 2 93568 82000 3 100224 94420
第三次测量 6 93952 80492 3 94576 82036 0 100116 94416
平均 5.67 94469.33 80675.67 5.33 94308.00 82044.00 2.00 100196.00 94421.33
使用wrong.gif



  我的GifImage GifImageLib AnimatedControl
  CPU Private bytes Working set CPU Private bytes Working set CPU Private bytes Working set
第一次测量 6 110792 102612 0 110520 103032 3 244320 237968
第二次测量 3 109188 102100 8 109772 103328 3 245356 238148
第三次测量 4 109132 102088 7 109664 103280 3 247668 238524
平均 4.33 109704.00 102266.67 5.00 109985.33 103213.33 3.00 245781.33 238213.33
使用oh.gif

结论

从CPU占用率来看,我的实现和周银辉的实现不分仲伯;而通过改变Image.Source的AnimatedControl效率最高,应该得益于WPF的内部优化。

从Working set(工作集)来看,我的实现略优于周银辉的GifImageLib;AnimatedControl则大很多,在第二次实验时竟然是另两者的两倍还多。

下载代码

代码包括测试代码、我的Gifimage、GifImageLib、AnimatedControl。测试代码版权没有,GifImageLib版权参考http://www.cnblogs.com/zhouyinhui/archive/2007/12/23/1011555.html,AnimatedControl版权是CPOL

地址:https://www.box.com/files#/files/0/f/66672665/1/f_5203962625


爱让一切都对了

本文(不含程序代码)以3.0协议发布



[1] Working Set看成一个进程可以用到(但不一定会使用)的物理内存。即不引起page fault异常就能够访问的内存。 Working Set包含了可能被其他程序共享的内存, 例如DLL就是一个典型的可能被其他程序共享的资源。所以所有进程的Working Set加起来有可能大于实际的物理内存。

[2] Private Bytes是只被本进程用占用的虚拟地址空间,不包括其他进程共享的内存。Private Bytes既包括不引起page fault异常就能够访问的内存也包括引起page fault异常才能够访问的内存。所以一般Private Bytes大于Working Set。但是如果一个进程和其他进程共享较多内存,也可能造成Working Set大于Private Bytes。(摘自http://blog.csdn.net/fw0124/article/details/6367360