你的位置:首页 > ASP.net教程

[ASP.net教程]WPF 显示GIF动画


简单的通过GifBitmapDecoder解析GIF图片,获取gif帧数和每一帧数据,然后通过时间切换显示,效果如下:

代码如下:

namespace GIfImageApplication{  public class GifImageExceptionRoutedEventArgs : RoutedEventArgs  {    public Exception ErrorException;    public GifImageExceptionRoutedEventArgs(RoutedEvent routedEvent, object obj)      : base(routedEvent, obj)    {    }  }  public class WebReadState  {    public WebRequest webRequest;    public MemoryStream memoryStream;    public Stream readStream;    public byte[] buffer;  }  public class GifImage : System.Windows.Controls.UserControl  {    private GifAnimation gifAnimation = null;    private Image image = null;    public static readonly DependencyProperty ForceGifAnimProperty = DependencyProperty.Register("ForceGifAnim", typeof(bool), typeof(GifImage), new FrameworkPropertyMetadata(false));    public bool ForceGifAnim    {      get      {        return (bool)this.GetValue(ForceGifAnimProperty);      }      set      {        this.SetValue(ForceGifAnimProperty, value);      }    }    public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(GifImage), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnSourceChanged)));    private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {      GifImage obj = (GifImage)d;      string s = (string)e.NewValue;      obj.CreateFromSourceString(s);    }    public string Source    {      get      {        return (string)this.GetValue(SourceProperty);      }      set      {        this.SetValue(SourceProperty, value);      }    }    public static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(GifImage), new FrameworkPropertyMetadata(Stretch.Fill, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnStretchChanged)));    private static void OnStretchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {      GifImage obj = (GifImage)d;      Stretch s = (Stretch)e.NewValue;      if (obj.gifAnimation != null)      {        obj.gifAnimation.Stretch = s;      }      else if (obj.image != null)      {        obj.image.Stretch = s;      }    }    public Stretch Stretch    {      get      {        return (Stretch)this.GetValue(StretchProperty);      }      set      {        this.SetValue(StretchProperty, value);      }    }    public static readonly DependencyProperty StretchDirectionProperty = DependencyProperty.Register("StretchDirection", typeof(StretchDirection), typeof(GifImage), new FrameworkPropertyMetadata(StretchDirection.Both, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnStretchDirectionChanged)));    private static void OnStretchDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {      GifImage obj = (GifImage)d;      StretchDirection s = (StretchDirection)e.NewValue;      if (obj.gifAnimation != null)      {        obj.gifAnimation.StretchDirection = s;      }      else if (obj.image != null)      {        obj.image.StretchDirection = s;      }    }    public StretchDirection StretchDirection    {      get      {        return (StretchDirection)this.GetValue(StretchDirectionProperty);      }      set      {        this.SetValue(StretchDirectionProperty, value);      }    }    public delegate void ExceptionRoutedEventHandler(object sender, GifImageExceptionRoutedEventArgs args);    public static readonly RoutedEvent ImageFailedEvent = EventManager.RegisterRoutedEvent("ImageFailed", RoutingStrategy.Bubble, typeof(ExceptionRoutedEventHandler), typeof(GifImage));    public event ExceptionRoutedEventHandler ImageFailed    {      add      {        AddHandler(ImageFailedEvent, value);      }      remove      {        RemoveHandler(ImageFailedEvent, value);      }    }    void image_ImageFailed(object sender, ExceptionRoutedEventArgs e)    {      RaiseImageFailedEvent(e.ErrorException);    }    void RaiseImageFailedEvent(Exception exp)    {      GifImageExceptionRoutedEventArgs newArgs = new GifImageExceptionRoutedEventArgs(ImageFailedEvent, this);      newArgs.ErrorException = exp;      RaiseEvent(newArgs);    }    private void DeletePreviousImage()    {      if (image != null)      {        this.RemoveLogicalChild(image);        image = null;      }      if (gifAnimation != null)      {        this.RemoveLogicalChild(gifAnimation);        gifAnimation = null;      }    }    private void CreateNonGifAnimationImage()    {      image = new Image();      image.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(image_ImageFailed);      ImageSource src = (ImageSource)(new ImageSourceConverter().ConvertFromString(Source));      image.Source = src;      image.Stretch = Stretch;      image.StretchDirection = StretchDirection;      this.AddChild(image);    }    private void CreateGifAnimation(MemoryStream memoryStream)    {      gifAnimation = new GifAnimation();      gifAnimation.CreateGifAnimation(memoryStream);      gifAnimation.Stretch = Stretch;      gifAnimation.StretchDirection = StretchDirection;      this.AddChild(gifAnimation);    }    private void CreateFromSourceString(string source)    {      DeletePreviousImage();      Uri uri;      try      {        uri = new Uri(source, UriKind.RelativeOrAbsolute);      }      catch (Exception exp)      {        RaiseImageFailedEvent(exp);        return;      }      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          {            CreateNonGifAnimationImage();          }        }      }      else      {        CreateNonGifAnimationImage();      }    }    private delegate void WebRequestFinishedDelegate(MemoryStream memoryStream);    private void WebRequestFinished(MemoryStream memoryStream)    {      CreateGifAnimation(memoryStream);    }    private delegate void WebRequestErrorDelegate(Exception exp);    private void WebRequestError(Exception exp)    {      RaiseImageFailedEvent(exp);    }    private void WebResponseCallback(IAsyncResult asyncResult)    {      WebReadState webReadState = (WebReadState)asyncResult.AsyncState;      WebResponse webResponse;      try      {        webResponse = webReadState.webRequest.EndGetResponse(asyncResult);        webReadState.readStream = webResponse.GetResponseStream();        webReadState.buffer = new byte[100000];        webReadState.readStream.BeginRead(webReadState.buffer, 0, webReadState.buffer.Length, new AsyncCallback(WebReadCallback), webReadState);      }      catch (WebException exp)      {        this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestErrorDelegate(WebRequestError), exp);      }    }    private void WebReadCallback(IAsyncResult asyncResult)    {      WebReadState webReadState = (WebReadState)asyncResult.AsyncState;      int count = webReadState.readStream.EndRead(asyncResult);      if (count > 0)      {        webReadState.memoryStream.Write(webReadState.buffer, 0, count);        try        {          webReadState.readStream.BeginRead(webReadState.buffer, 0, webReadState.buffer.Length, new AsyncCallback(WebReadCallback), webReadState);        }        catch (WebException exp)        {          this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestErrorDelegate(WebRequestError), exp);        }      }      else      {        this.Dispatcher.Invoke(DispatcherPriority.Render, new WebRequestFinishedDelegate(WebRequestFinished), webReadState.memoryStream);      }    }    private void GetGifStreamFromHttp(Uri uri)    {      try      {        WebReadState webReadState = new WebReadState();        webReadState.memoryStream = new MemoryStream();        webReadState.webRequest = WebRequest.Create(uri);        webReadState.webRequest.Timeout = 10000;        webReadState.webRequest.BeginGetResponse(new AsyncCallback(WebResponseCallback), webReadState);      }      catch (SecurityException)      {        CreateNonGifAnimationImage();      }    }    private void ReadGifStreamSynch(Stream s)    {      byte[] gifData;      MemoryStream memoryStream;      using (s)      {        memoryStream = new MemoryStream((int)s.Length);        BinaryReader br = new BinaryReader(s);        gifData = br.ReadBytes((int)s.Length);        memoryStream.Write(gifData, 0, (int)s.Length);        memoryStream.Flush();      }      CreateGifAnimation(memoryStream);    }    private void GetGifStreamFromPack(Uri uri)    {      try      {        StreamResourceInfo streamInfo;        if (!uri.IsAbsoluteUri)        {          streamInfo = Application.GetContentStream(uri);          if (streamInfo == null)          {            streamInfo = Application.GetResourceStream(uri);          }        }        else        {          if (uri.GetLeftPart(UriPartial.Authority).Contains("siteoforigin"))          {            streamInfo = Application.GetRemoteStream(uri);          }          else          {            streamInfo = Application.GetContentStream(uri);            if (streamInfo == null)            {              streamInfo = Application.GetResourceStream(uri);            }          }        }        if (streamInfo == null)        {          throw new FileNotFoundException("Resource not found.", uri.ToString());        }        ReadGifStreamSynch(streamInfo.Stream);      }      catch (Exception exp)      {        RaiseImageFailedEvent(exp);      }    }  }  class GifAnimation : Viewbox  {    private class HedmGifFrame : Image    {      public int delayTime;      public int disposalMethod;      public int left;      public int top;      public int width;      public int height;    }    private Canvas canvas = null;    private List<HedmGifFrame> frameList = null;    private int frameCounter = 0;    private int numberOfFrames = 0;    private int numberOfLoops = -1;    private int currentLoop = 0;    private int logicalWidth = 0;    private int logicalHeight = 0;    private DispatcherTimer frameTimer = null;    private HedmGifFrame currentParseGifFrame;    public GifAnimation()    {      canvas = new Canvas();      this.Child = canvas;    }    private void Reset()    {      if (frameList != null)      {        frameList.Clear();      }      frameList = null;      frameCounter = 0;      numberOfFrames = 0;      numberOfLoops = -1;      currentLoop = 0;      logicalWidth = 0;      logicalHeight = 0;      if (frameTimer != null)      {        frameTimer.Stop();        frameTimer = null;      }    }    #region PARSE    private void ParseGif(byte[] gifData)    {      frameList = new List<HedmGifFrame>();      currentParseGifFrame = new HedmGifFrame();      ParseGifDataStream(gifData, 0);    }    private int ParseBlock(byte[] gifData, int offset)    {      switch (gifData[offset])      {        case 0x21:          if (gifData[offset + 1] == 0xF9)          {            return ParseGraphicControlExtension(gifData, offset);          }          else          {            return ParseExtensionBlock(gifData, offset);          }        case 0x2C:          offset = ParseGraphicBlock(gifData, offset);          frameList.Add(currentParseGifFrame);          currentParseGifFrame = new HedmGifFrame();          return offset;        case 0x3B:          return -1;        default:          throw new Exception("GIF format incorrect: missing graphic block or special-purpose block. ");      }    }    private int ParseGraphicControlExtension(byte[] gifData, int offset)    {      int returnOffset = offset;      int length = gifData[offset + 2];      returnOffset = offset + length + 2 + 1;      byte packedField = gifData[offset + 3];      currentParseGifFrame.disposalMethod = (packedField & 0x1C) >> 2;      int delay = BitConverter.ToUInt16(gifData, offset + 4);      currentParseGifFrame.delayTime = delay;      while (gifData[returnOffset] != 0x00)      {        returnOffset = returnOffset + gifData[returnOffset] + 1;      }      returnOffset++;      return returnOffset;    }    private int ParseLogicalScreen(byte[] gifData, int offset)    {      logicalWidth = BitConverter.ToUInt16(gifData, offset);      logicalHeight = BitConverter.ToUInt16(gifData, offset + 2);      byte packedField = gifData[offset + 4];      bool hasGlobalColorTable = (int)(packedField & 0x80) > 0 ? true : false;      int currentIndex = offset + 7;      if (hasGlobalColorTable)      {        int colorTableLength = packedField & 0x07;        colorTableLength = (int)Math.Pow(2, colorTableLength + 1) * 3;        currentIndex = currentIndex + colorTableLength;      }      return currentIndex;    }    private int ParseGraphicBlock(byte[] gifData, int offset)    {      currentParseGifFrame.left = BitConverter.ToUInt16(gifData, offset + 1);      currentParseGifFrame.top = BitConverter.ToUInt16(gifData, offset + 3);      currentParseGifFrame.width = BitConverter.ToUInt16(gifData, offset + 5);      currentParseGifFrame.height = BitConverter.ToUInt16(gifData, offset + 7);      if (currentParseGifFrame.width > logicalWidth)      {        logicalWidth = currentParseGifFrame.width;      }      if (currentParseGifFrame.height > logicalHeight)      {        logicalHeight = currentParseGifFrame.height;      }      byte packedField = gifData[offset + 9];      bool hasLocalColorTable = (int)(packedField & 0x80) > 0 ? true : false;      int currentIndex = offset + 9;      if (hasLocalColorTable)      {        int colorTableLength = packedField & 0x07;        colorTableLength = (int)Math.Pow(2, colorTableLength + 1) * 3;        currentIndex = currentIndex + colorTableLength;      }      currentIndex++;      currentIndex++;      while (gifData[currentIndex] != 0x00)      {        int length = gifData[currentIndex];        currentIndex = currentIndex + gifData[currentIndex];        currentIndex++;      }      currentIndex = currentIndex + 1;      return currentIndex;    }    private int ParseExtensionBlock(byte[] gifData, int offset)    {      int returnOffset = offset;      int length = gifData[offset + 2];      returnOffset = offset + length + 2 + 1;      if (gifData[offset + 1] == 0xFF && length > 10)      {        string netscape = System.Text.ASCIIEncoding.ASCII.GetString(gifData, offset + 3, 8);        if (netscape == "NETSCAPE")        {          numberOfLoops = BitConverter.ToUInt16(gifData, offset + 16);          if (numberOfLoops > 0)          {            numberOfLoops++;          }        }      }      while (gifData[returnOffset] != 0x00)      {        returnOffset = returnOffset + gifData[returnOffset] + 1;      }      returnOffset++;      return returnOffset;    }    private int ParseHeader(byte[] gifData, int offset)    {      string str = System.Text.ASCIIEncoding.ASCII.GetString(gifData, offset, 3);      if (str != "GIF")      {        throw new Exception("Not a proper GIF file: missing GIF header");      }      return 6;    }    private void ParseGifDataStream(byte[] gifData, int offset)    {      offset = ParseHeader(gifData, offset);      offset = ParseLogicalScreen(gifData, offset);      while (offset != -1)      {        offset = ParseBlock(gifData, offset);      }    }    #endregion    public void CreateGifAnimation(MemoryStream memoryStream)    {      try      {        Reset();        byte[] gifData = memoryStream.GetBuffer(); // Use GetBuffer so that there is no memory copy        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();        }      }      finally      {      }    }    public void NextFrame()    {      NextFrame(null, null);    }    public void NextFrame(object sender, EventArgs e)    {      frameTimer.Stop();      if (numberOfFrames == 0) return;      if (frameList[frameCounter].disposalMethod == 2)      {        frameList[frameCounter].Visibility = Visibility.Hidden;      }      if (frameList[frameCounter].disposalMethod >= 3)      {        frameList[frameCounter].Visibility = Visibility.Hidden;      }      frameCounter++;      if (frameCounter < numberOfFrames)      {        frameList[frameCounter].Visibility = Visibility.Visible;        frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[frameCounter].delayTime * 10);        frameTimer.Start();      }      else      {        if (numberOfLoops != 0)        {          currentLoop++;        }        if (currentLoop < numberOfLoops || numberOfLoops == 0)        {          for (int f = 0; f < frameList.Count; f++)          {            frameList[f].Visibility = Visibility.Hidden;          }          frameCounter = 0;          frameList[frameCounter].Visibility = Visibility.Visible;          frameTimer.Interval = new TimeSpan(0, 0, 0, 0, frameList[frameCounter].delayTime * 10);          frameTimer.Start();        }      }    }  }}

使用:

 <Gif:GifImage Source="loading.gif" ></Gif:GifImage>