相信如何为gif文件编码,不少朋友都会,而难点在于怎么让GIF文件中的帧动起来,也就是建立gif动画。数组
先简单介绍一下编码的方法。浏览器
一、调用BitmapEncoder.CreateAsync静态方法实例化编码器,要建立GIF编码器,能够在调用方法时,指定表示GIF编码器的GUID,这个GUID不用特地去记,由于访问BitmapEncoder.GifEncoderId静态属性就能获得。数据结构
二、调用SetPixelData方法设置当前帧的图像数据。注意,编码器对象在建立实例后,默认处于第一帧,所以对于设置第一张图片的数据时,能够直接调用SetPixelData方法。app
三、从第二帧开始,须要先调用GoToNextFrameAsync方法向后移动一帧,而后才调用SetPixelData方法设置数据。设置完最后一帧后就不用再调用GoToNextFrameAsync,由于后面没有内容了,若是调用GoToNextFrameAsync建立新帧而不写入数据,会引起异常。dom
四、关闭相关的流。ide
好比下面示例:函数
BitmapEncoder encoder= await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outStream); …… encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, decoder.PixelWidth, decoder.PixelHeight, decoder.DpiX, decoder.DpiY, data); …… if ( 不是最后一帧 ) { await encoder.GoToNextFrameAsync(); }
若是要让gif产生动画,就得设置延迟时间,即时间间隔。要经过写入图像元数据的方法来实现。动画
表示时间间隔的元数据路径为:ui
BitmapProperties pv = encoder.BitmapProperties; Dictionary<string, BitmapTypedValue> props = new Dictionary<string, BitmapTypedValue>(); …… // Delay表示每一帧的时间间隔,单位为1/100秒 props.Add("/grctlext/Delay", new BitmapTypedValue(30, PropertyType.UInt16)); await pv.SetPropertiesAsync(props); //写入元数据
元数据能够用字典数据结构来操做,Key为字段的路径,Value就是该元数据的值,由BitmapTypedValue类来封装元数据值。使用时经过如下构造函数来实例化。编码
public BitmapTypedValue ( object value, PropertyType type );
value就是元数据的值,类型为object,能够兼容各类值,type参数指定元数据的数据类型,由Windows.Foundation命名空间下的PropertyType枚举来规范。
Delay的值为 1 / 100秒,即0.01秒,若是设置为50,就表示动画每一个帧的间隔为50 * 10 = 500毫秒。
设置delay后保存的gif文件已经有动画效果了,可是,它只播放一次就会停下来。多数状况下,咱们都但愿GIF动画是无限循环播放的,这就要设置其余的元数据值了。
要让gif循环播放,须要指定两个值:
第一个值是 /appext/Application,这个值是必须的,并且是固定的,就是字符串“NETSCAPE2.0”的字节表示形式,注意是字节表示,不要直接设置字符串,该字符串转化为字节数组为11个字节。NetScape有一款浏览器,相信不少人都知道,当年我在Win 98下常常用这个浏览器的,呵呵,一直用到Win Me还在用。
第二个值是 /appext/Data。在C++中,这个值通常包括5个字节,不过咱们在C#中放4个字节也没问题的(其实第五个字节是’\0‘,即NULL,表示结尾)。要实现无限循环播放,只要把下面字节数组写入/appext/Data便可。
3, 1, 0, 0
第一个字节为3,表示紧跟它后面的字节数,由于后面一、0、0是3个字节,因此它的值为3。
第二个字节必须为1,表示启用gif动画。
第三个字节表示循环播放的次数。0表示无限循环,若是但愿动画播放5次就停下来,那就设置为5。一般都为0,由于咱们都喜欢死循环。
第四个字节为有效高字位的迭代统计,我也不知道干嘛用的,反正设置为0就好了。
其实,若是想让动画无限循环,只要记住三、一、0、0四个值就行了,直接背下来也无所谓,反正很好记。
这个示例把5张jpg图片合起来,变成一个GIF文件,而且有动画效果的。为了节省废话,我只帖上建立GIF的核心代码。
StorageFolder photoFolder = KnownFolders.PicturesLibrary; StorageFile newFile = await photoFolder.CreateFileAsync("newfile.gif",CreationCollisionOption.ReplaceExisting); IRandomAccessStream outStream = await newFile.OpenAsync(FileAccessMode.ReadWrite); BitmapEncoder encoder= await BitmapEncoder.CreateAsync(BitmapEncoder.GifEncoderId, outStream); // 元数据 /* * /appext/Application的值是固定的,为“NETSCAPE2.0”,11个字节 * /appext/Data设置循环播放,若是不设置该字段,则只播放一次。 * Data的值是一组字节,因为将第一个字节设置为3,第二个字节设置为1便可以达到循环播放效果, * 其余字符能够为0; * 3 - 表示随后的字节块大小,后面1,0,0三个字节,因此为3; * 1 - 表示Gif使用动画; * 0 - 循环次数,0表示无限循环 */ BitmapProperties pv = encoder.BitmapProperties; Dictionary<string, BitmapTypedValue> props = new Dictionary<string, BitmapTypedValue>(); byte[] buffer = System.Text.Encoding.UTF8.GetBytes("NETSCAPE2.0"); // 此字段必须 props.Add("/appext/Application", new BitmapTypedValue(buffer, PropertyType.UInt8Array)); // 表示循环播放 props.Add("/appext/Data", new BitmapTypedValue(new byte[] { 3, 1, 0, 0 }, PropertyType.UInt8Array)); // Delay表示每一帧的时间间隔,单位为1/100秒 props.Add("/grctlext/Delay", new BitmapTypedValue(30, PropertyType.UInt16)); await pv.SetPropertiesAsync(props); //写入元数据 for (short i = 1; i <= 5; i++) { Uri uri = new Uri("ms-appx:///Assets/" + i.ToString() + ".jpg"); StorageFile inFile = await StorageFile.GetFileFromApplicationUriAsync(uri); IRandomAccessStream inStream = await inFile.OpenReadAsync(); // 解码 BitmapDecoder decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.JpegDecoderId, inStream); // 获取像素数据 PixelDataProvider pxProvider = await decoder.GetPixelDataAsync(); byte[] data = pxProvider.DetachPixelData(); // 编码 encoder.SetPixelData(decoder.BitmapPixelFormat, decoder.BitmapAlphaMode, decoder.PixelWidth, decoder.PixelHeight, decoder.DpiX, decoder.DpiY, data); inStream.Dispose(); if (i < 5) { await encoder.GoToNextFrameAsync(); } } await encoder.FlushAsync(); outStream.Dispose();
下面gif图片就是用上面的示例建立的,一块儿来欣赏一下。
如何? 这些芙蓉花是否是很美?
===============================================
修订:
可能你们已经发现,上面生成的gif动画有点小问题,就是后一帧图片会与上一帧图片重叠,有时候咱们是但愿每一帧图片独立显示。因此在设置元数据时,能够把 /grctlext/Disposal 的值设置为2,表示清除上一帧图片。
props.Add("/grctlext/Disposal", new BitmapTypedValue((byte)2, PropertyType.UInt8));
再看看经过这样修改后生成的图片。
如今,每一帧图片就不会发生重叠了。