在WPF中显示动态GIF(转)
?
?
http://www.silverlightchina.net/html/study/WPF/2011/0824/9965.html
?
?
在我們尋求幫助的時候,最不愿意聽到的答復(fù)是:很抱歉,在當前版本的產(chǎn)品中還沒有實現(xiàn)該功能...
在WPF中顯示動態(tài)的GIF圖像時便遇到了這樣的問題,WPF中強大的Image控件卻不支持動態(tài)的GIF(其只能顯示第一幀).當然,我們可以說WPF強大的動畫能力,讓我們完全有理由拋棄傳統(tǒng)的GIF動畫,但如某種情況下如果你覺得使用動態(tài)的GIF更合適的話(比如QQ表情,因為GIF是利于保存和傳輸?shù)?,沒關(guān)系,本篇隨筆將幫助你解決這個問題.
1,曾有過的嘗試:
?
我們在實際開發(fā)過程中也遇到顯示動態(tài)GIF的問題.發(fā)現(xiàn)普通的Image控件不能正常顯示后,我們又發(fā)現(xiàn)網(wǎng)頁瀏覽器卻是可以的,以及windows
XP的"圖片和傳真查看器"也可以,但"Window
Live照片庫"卻不可以.所以我們最初打算使用通過包裝WebBrowseControl來實現(xiàn),即是在WPF中host一個.net2.0中的瀏覽器控件,然后讓該瀏覽器來實現(xiàn)圖片,成功了,但麻煩的事情是鼠標右鍵可以點出網(wǎng)頁的上下文菜單.我們放棄了該方案,除了不愿意花時間來屏蔽上下文菜單和瀏覽器控件的多余功能外,同時我們的覺得瀏覽器控件過于"重量級",有點殺雞用牛刀的感覺.另外,你可能會想到使用WPF中的Frame控件,但也會得到上述結(jié)果.另外,有網(wǎng)友說可以使用MediaElement控件,但大都沒有成功,我也沒有(可能是RP不夠哈,呵呵...)
?
2,GifBitmapDecoder
?
我們發(fā)現(xiàn)WPF中有一個名為GifBitmapDecoder的類,其可以將動態(tài)GIF分解成很多幀并保存在一個列表中,每一幀為一個BitmapFrame類型的對象,其父類為BitmapSource,這也就意味著,我們可以將每一幀賦值給一個Image控件的Source屬性,這樣我們可以得到針對GIF各幀的Image系列:
?
GifBitmapDecoder decoder = new GifBitmapDecoder(?????????????????????????? new Uri("OH.gif", UriKind.Relative),
?????????????????????????? BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
??????????? foreach (BitmapFrame f in decoder.Frames)
??????????? {
??????????????? Image image = new Image();
??????????????? image.Source = f;
??????????????? this.panel1.Children.Add(image);
??????????? }
?
?
?
下圖為將一個GIF圖片的12幀分解出來的所得到的一個系列圖:
?
?
?
?
?
不過先別高興,這還不足以解決我們的問題,因為我們不知道每一幀顯示的時間(幀與幀之間切換的時間間隔),以及一幀顯示結(jié)束后它的處理方法(是顯示下一幀嗎?是顯示背景色嗎?等等...)所以我們還必須一個字節(jié)一個字節(jié)的解析GIF文件以便得到足夠多的信息.
?
3,解析GIF
?
要解析文件就必須知道文件的存儲結(jié)構(gòu),關(guān)于動態(tài)GIF的文件存儲結(jié)構(gòu),可以參考這里:http://blog.zhongmoo.cn/post/45.html
?
比如,得到幀的顯示時間的方法是這樣的:
?
private int ParseGraphicControlExtension(byte[] gifData, int offset)??????? {
??????????? int returnOffset = offset;
??????????? // Extension Block
??????????? int length = gifData[offset + 2];
??????????? returnOffset = offset + length + 2 + 1;
??????????? byte packedField = gifData[offset + 3];
??????????? currentParseGifFrame.disposalMethod = (packedField & 0x1C) >> 2;
??????????? // Get DelayTime
??????????? int delay = BitConverter.ToUInt16(gifData, offset + 4);
??????????? currentParseGifFrame.delayTime = delay;
??????????? while (gifData[returnOffset] != 0x00)
??????????? {
??????????????? returnOffset = returnOffset + gifData[returnOffset] + 1;
??????????? }
??????????? returnOffset++;
??????????? return returnOffset;
??????? }
?
?
?
關(guān)于如何解析就不多介紹了,你只有了解其文件結(jié)構(gòu)然后不斷地移動讀取游標和讀取相應(yīng)的字節(jié)就可以完成了.
?
4,包裝成控件
?
我們想要的最佳效果是,打造一個GifImage控件,就跟Image控件差不多,只要我們指定它的Source屬性,然后其就自動查找GIF文件并讀取或下載,然后解析并顯示.
?
所以,我們將該控件分成了兩個部分,一個部分負責將根據(jù)用戶指定的Source屬性查找并讀取或從網(wǎng)絡(luò)下載GIF到內(nèi)存流,然后另外一部分負責將得到的內(nèi)存流解析并顯示出來.
?
gif文件在哪里?這是一個必須考慮到的問題,控件用戶指定的是一個絕對路徑嗎,還是一個相對路徑,是本地文件還是內(nèi)嵌的資源文件或者是網(wǎng)絡(luò)上的文件.還有該文件一定支持GIF動畫嗎,還是只是一個普通的靜態(tài)圖片,所以負責讀取文件到內(nèi)存流的代碼中應(yīng)該有類似于下面的代碼:
?
if (source.Trim().ToUpper().EndsWith(".GIF") || ForceGifAnim)??????????? {
??????????????? if (!uri.IsAbsoluteUri)
??????????????? {
????????????????????
??????????????????? GetGifStreamFromPack(uri);
??????????????? }
??????????????? else
??????????????? {
??????????????????? string leftPart = uri.GetLeftPart(UriPartial.Scheme);
??????????????????? if (leftPart == "http://" || leftPart == "ftp://" || leftPart == "file://")
??????????????????? {
????????????????????????
??????????????????????? GetGifStreamFromHttp(uri);
??????????????????? }
??????????????????? else if (leftPart == "pack://")
??????????????????? {
?????????????????????????
??????????????????????? GetGifStreamFromPack(uri);
??????????????????? }
??????????????????? else
??????????????????? {
??????????????????????? //創(chuàng)建無動畫的普通Image
??????????????????????? CreateNonGifAnimationImage();
??????????????????? }
??????????????? }
??????????? }
??????????? else
??????????? {
??????????????? //創(chuàng)建無動畫的普通Image
??????????????? CreateNonGifAnimationImage();
??????????? }
??????? }
?
?
?
當讀取文件成功后,一切都好辦了,通過解析內(nèi)存流中的數(shù)據(jù),我們可以得到足夠多的信息,比如幀的列表,每幀顯示的時間以及該幀顯示完成后如何處理,那么我們就可以用一個計時器(DispatcherTimer)來處理這一切而形成一個動畫了.
?
/**//// <summary>??????? /// 從內(nèi)存流中創(chuàng)建圖片
??????? /// </summary>
??????? public void CreateGifAnimation(MemoryStream memoryStream)
??????? {
??????????? Reset();
??????????? byte[] gifData = memoryStream.GetBuffer();??
??????????? GifBitmapDecoder decoder = new GifBitmapDecoder(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
??????????? numberOfFrames = decoder.Frames.Count;
??????????? try
??????????? {
??????????????? ParseGif(gifData);
??????????? }
??????????? catch
??????????? {
??????????????? throw new FileFormatException("Unable to parse Gif file format.");
??????????? }
??????????? for (int i = 0; i < decoder.Frames.Count; i++)
??????????? {
??????????????? frameList[i].Source = decoder.Frames[i];
??????????????? frameList[i].Visibility = Visibility.Hidden;
??????????????? canvas.Children.Add(frameList[i]);
??????????????? Canvas.SetLeft(frameList[i], frameList[i].left);
??????????????? Canvas.SetTop(frameList[i], frameList[i].top);
??????????????? Canvas.SetZIndex(frameList[i], i);
??????????? }
??????????? canvas.Height = logicalHeight;
??????????? canvas.Width = logicalWidth;
??????????? frameList[0].Visibility = Visibility.Visible;
??????????? for (int i = 0; i < frameList.Count; i++)
??????????? {
??????????????? Console.WriteLine(frameList[i].disposalMethod.ToString() + " " + frameList[i].width.ToString() + " " + frameList[i].delayTime.ToString());
??????????? }
??????????? if (frameList.Count > 1)
??????????? {
??????????????? if (numberOfLoops == -1)
??????????????? {
??????????????????? numberOfLoops = 1;
??????????????? }
??????????????? frameTimer = new System.Windows.Threading.DispatcherTimer();
??????????????? frameTimer.Tick += NextFrame;
??????????????? frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[0].delayTime * 10);
??????????????? frameTimer.Start();
??????????? }
??????? }
?
?
?
OK,我們可以像使用Image控件一樣來使用我們的GifImage控件了:
?
?
?
?
?
?
?
| 源碼下載 |
?
?
本文來自周銀輝的博客,原文地址:http://www.cnblogs.com/zhouyinhui/archive/2007/12/23/1011555.html
總結(jié)
以上是生活随笔為你收集整理的在WPF中显示动态GIF(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 选择or不选择苹果的理由
- 下一篇: WPF案例:如何设计历史记录查看UI