相信如何為gif文件編碼,很多朋友都會,而難點在於怎么讓GIF文件中的幀動起來,也就是創建gif動畫。
Gif文件編碼方法
先簡單介紹一下編碼的方法。
1、調用BitmapEncoder.CreateAsync靜態方法實例化編碼器,要創建GIF編碼器,可以在調用方法時,指定表示GIF編碼器的GUID,這個GUID不用特意去記,因為訪問BitmapEncoder.GifEncoderId靜態屬性就能得到。
2、調用SetPixelData方法設置當前幀的圖像數據。注意,編碼器對象在創建實例后,默認處於第一幀,因此對於設置第一張圖片的數據時,可以直接調用SetPixelData方法。
3、從第二幀開始,需要先調用GoToNextFrameAsync方法向后移動一幀,然后才調用SetPixelData方法設置數據。設置完最后一幀后就不用再調用GoToNextFrameAsync,因為后面沒有內容了,如果調用GoToNextFrameAsync創建新幀而不寫入數據,會引發異常。
4、關閉相關的流。
比如下面示例:
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產生動畫,就得設置延遲時間,即時間間隔。要通過寫入圖像元數據的方法來實現。
表示時間間隔的元數據路徑為:
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,表示緊跟它后面的字節數,因為后面1、0、0是3個字節,所以它的值為3。
第二個字節必須為1,表示啟用gif動畫。
第三個字節表示循環播放的次數。0表示無限循環,如果希望動畫播放5次就停下來,那就設置為5。通常都為0,因為我們都喜歡死循環。
第四個字節為有效高字位的迭代統計,我也不知道干嗎用的,反正設置為0就行了。
其實,如果想讓動畫無限循環,只要記住3、1、0、0四個值就好了,直接背下來也無所謂,反正很好記。
生成GIF動畫示例
這個示例把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));
再看看通過這樣修改后生成的圖片。
現在,每一幀圖片就不會發生重疊了。
上面示例的源碼下載:http://files.cnblogs.com/tcjiaan/BuildGifApp.zip