<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C# WPF 內置解碼器實現 GIF 動圖控件

      相對于 WinForm PictureBox 控件原生支持動態 GIF,WPF Image 控件卻不支持,讓人摸不著頭腦

      常用方法

      提到 WPF 播放動圖,常見的方法有三種

      MediaElement

      使用 MediaElement 控件,缺點是依賴 Media Player,且不支持透明

      <MediaElement Source="animation.gif" LoadedBehavior="Play" Stretch="Uniform"/>
      

      WinForm PictureBox

      借助 WindowsFormsIntegration 嵌入 WinForm PictureBox,缺點是不支持透明

      <WindowsFormsHost>
          <wf:PictureBox x:Name="winFormsPictureBox"/>
      </WindowsFormsHost>
      

      WpfAnimatedGif

      引用 NuGet 包 WpfAnimatedGif,支持透明

      <Image gif:ImageBehavior.AnimatedSource="Images/animation.gif"/>
      

      作者還有另一個性能更好、跨平臺的 XamlAnimatedGif,用法相同

      原生解碼方法

      WPF 雖然原生 Image 不支持 GIF 動圖,但是提供了 GifBitmapDecoder 解碼器,可以獲取元數據,包括循環信息、邏輯尺寸、所有幀信息等

      判斷是否循環和循環次數

      int loop = 1;
      bool isAnimated = true;
      var decoder = new GifBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
      var data = decoder.Metadata;
      if (data.GetQuery("/appext/Application") is byte[] array1)
      {
          string appName = Encoding.ASCII.GetString(array1);
          if ((appName == "NETSCAPE2.0" || appName == "ANIMEXTS1.0")
              && data.GetQuery("/appext/Data") is byte[] array2)
          {
              loop = array2[2] | array2[3] << 8;// 獲取循環次數, 0 表示無限循環
              isAnimated = array2[1] == 1;
          }
      }
      

      獲取畫布邏輯尺寸

      var width = Convert.ToUInt16(data.GetQuery("/logscrdesc/Width"));
      var height = Convert.ToUInt16(data.GetQuery("/logscrdesc/Height"));
      

      獲取每一幀信息

      /// <summary>當前幀播放完成后的處理方法</summary>
      enum DisposalMethod
      {
          /// <summary>被全尺寸不透明的下一幀覆蓋替換</summary>
          None,
          /// <summary>不丟棄, 繼續顯示下一幀未覆蓋的任何像素</summary>
          DoNotDispose,
          /// <summary>重置到背景色</summary>
          RestoreBackground,
          /// <summary>恢復到上一個未釋放的幀的狀態</summary>
          RestorePrevious,
      }
      
      sealed class FrameInfo
      {
          public Image Frame { get; }
          public int DelayTime { get; }
          public DisposalMethod DisposalMethod { get; }
      
          public FrameInfo(BitmapFrame frame)
          {
              Frame = new Image { Source = frame };
              var data = (BitmapMetadata)frame.Metadata;
              DelayTime = Convert.ToUInt16(data.GetQuery("/grctlext/Delay"));
              DisposalMethod = (DisposalMethod)Convert.ToByte(data.GetQuery("/grctlext/Disposal"));
              ushort left = Convert.ToUInt16(data.GetQuery("/imgdesc/Left"));
              ushort top = Convert.ToUInt16(data.GetQuery("/imgdesc/Top"));
              ushort width = Convert.ToUInt16(data.GetQuery("/imgdesc/Width"));
              ushort height = Convert.ToUInt16(data.GetQuery("/imgdesc/Height"));
              Canvas.SetLeft(Frame, left);
              Canvas.SetTop(Frame, top);
              Canvas.SetRight(Frame, left + width);
              Canvas.SetBottom(Frame, top + height);
          }
      }
      

      自定義控件完整代碼

      將所有幀畫面按其大小位置和順序放置在 Canvas 中,結合所有幀的播放處理方法和持續時間,使用關鍵幀動畫,即可實現無需依賴第三方的自定義控件,且性能和 XamlAnimatedGif 相差無幾

      using System;
      using System.IO;
      using System.Text;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Media;
      using System.Windows.Media.Animation;
      using System.Windows.Media.Imaging;
      
      public sealed class GifImage : ContentControl
      {
          /// <summary>當前幀播放完成后的處理方法</summary>
          enum DisposalMethod
          {
              /// <summary>被全尺寸不透明的下一幀覆蓋替換</summary>
              None,
              /// <summary>不丟棄, 繼續顯示下一幀未覆蓋的任何像素</summary>
              DoNotDispose,
              /// <summary>重置到背景色</summary>
              RestoreBackground,
              /// <summary>恢復到上一個未釋放的幀的狀態</summary>
              RestorePrevious,
          }
      
          sealed class FrameInfo
          {
              public Image Frame { get; }
              public int DelayTime { get; }
              public DisposalMethod DisposalMethod { get; }
      
              public FrameInfo(BitmapFrame frame)
              {
                  Frame = new Image { Source = frame };
                  var data = (BitmapMetadata)frame.Metadata;
                  DelayTime = Convert.ToUInt16(data.GetQuery("/grctlext/Delay"));
                  DisposalMethod = (DisposalMethod)Convert.ToByte(data.GetQuery("/grctlext/Disposal"));
                  ushort left = Convert.ToUInt16(data.GetQuery("/imgdesc/Left"));
                  ushort top = Convert.ToUInt16(data.GetQuery("/imgdesc/Top"));
                  ushort width = Convert.ToUInt16(data.GetQuery("/imgdesc/Width"));
                  ushort height = Convert.ToUInt16(data.GetQuery("/imgdesc/Height"));
                  Canvas.SetLeft(Frame, left);
                  Canvas.SetTop(Frame, top);
                  Canvas.SetRight(Frame, left + width);
                  Canvas.SetBottom(Frame, top + height);
              }
          }
      
          public static readonly DependencyProperty UriSourceProperty =
              DependencyProperty.Register(nameof(UriSource), typeof(Uri), typeof(GifImage), new PropertyMetadata(null, OnSourceChanged));
      
          public static readonly DependencyProperty StreamSourceProperty =
              DependencyProperty.Register(nameof(StreamSource), typeof(Stream), typeof(GifImage), new PropertyMetadata(null, OnSourceChanged));
      
          public static readonly DependencyProperty FrameIndexProperty =
              DependencyProperty.Register(nameof(FrameIndex), typeof(int), typeof(GifImage), new PropertyMetadata(0, OnFrameIndexChanged));
      
          public static readonly DependencyProperty StretchProperty =
              DependencyProperty.Register(nameof(Stretch), typeof(Stretch), typeof(GifImage), new PropertyMetadata(Stretch.None, OnStrechChanged));
      
          public static readonly DependencyProperty StretchDirectionProperty =
              DependencyProperty.Register(nameof(StretchDirection), typeof(StretchDirection), typeof(GifImage), new PropertyMetadata(StretchDirection.Both, OnStrechDirectionChanged));
      
          public static readonly DependencyProperty IsLoadingProperty =
              DependencyProperty.Register(nameof(IsLoading), typeof(bool), typeof(GifImage), new PropertyMetadata(false));
      
          public Uri UriSource
          {
              get => (Uri)GetValue(UriSourceProperty);
              set => SetValue(UriSourceProperty, value);
          }
      
          public Stream StreamSource
          {
              get => (Stream)GetValue(StreamSourceProperty);
              set => SetValue(StreamSourceProperty, value);
          }
      
          public int FrameIndex
          {
              get => (int)GetValue(FrameIndexProperty);
              private set => SetValue(FrameIndexProperty, value);
          }
      
          public Stretch Stretch
          {
              get => (Stretch)GetValue(StretchProperty);
              set => SetValue(StretchProperty, value);
          }
      
          public StretchDirection StretchDirection
          {
              get => (StretchDirection)GetValue(StretchDirectionProperty);
              set => SetValue(StretchDirectionProperty, value);
          }
      
          public bool IsLoading
          {
              get => (bool)GetValue(IsLoadingProperty);
              set => SetValue(IsLoadingProperty, value);
          }
      
          private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              ((GifImage)d)?.OnSourceChanged();
          }
      
          private static void OnFrameIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              ((GifImage)d)?.OnFrameIndexChanged();
          }
      
          private static void OnStrechChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              if (d is GifImage image && image.Content is Viewbox viewbox)
              {
                  viewbox.Stretch = image.Stretch;
              }
          }
      
          private static void OnStrechDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
          {
              if (d is GifImage image && image.Content is Viewbox viewbox)
              {
                  viewbox.StretchDirection = image.StretchDirection;
              }
          }
      
          Stream stream;
          Canvas canvas;
          FrameInfo[] frameInfos;
          Int32AnimationUsingKeyFrames animation;
      
          public GifImage()
          {
              IsVisibleChanged += OnIsVisibleChanged;
              Unloaded += OnUnloaded;
          }
      
          private void OnUnloaded(object sender, RoutedEventArgs e)
          {
              Release();
          }
      
          private void OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
          {
              if (IsVisible)
              {
                  StartAnimation();
              }
              else
              {
                  StopAnimation();
              }
          }
      
          private void StartAnimation()
          {
              BeginAnimation(FrameIndexProperty, animation);
          }
      
          private void StopAnimation()
          {
              BeginAnimation(FrameIndexProperty, null);
          }
      
          private void Release()
          {
              StopAnimation();
              canvas?.Children.Clear();
              stream?.Dispose();
              animation = null;
              frameInfos = null;
          }
      
          private async void OnSourceChanged()
          {
              Release();
              IsLoading = true;
              FrameIndex = 0;
              if (UriSource != null)
              {
                  stream = await ResourceHelper.GetStream(UriSource);
              }
              else
              {
                  stream = StreamSource;
              }
              if (stream != null)
              {
                  int loop = 1;
                  bool isAnimated = true;
                  var decoder = new GifBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                  var data = decoder.Metadata;
                  if (data.GetQuery("/appext/Application") is byte[] array1)
                  {
                      string appName = Encoding.ASCII.GetString(array1);
                      if ((appName == "NETSCAPE2.0" || appName == "ANIMEXTS1.0")
                          && data.GetQuery("/appext/Data") is byte[] array2)
                      {
                          loop = array2[2] | array2[3] << 8;// 獲取循環次數, 0表示無限循環
                          isAnimated = array2[1] == 1;
                      }
                  }
                  if (!(Content is Viewbox viewbox))
                  {
                      Content = viewbox = new Viewbox
                      {
                          Stretch = Stretch,
                          StretchDirection = StretchDirection,
                      };
                  }
                  if (canvas == null || canvas.Parent != Content)
                  {
                      canvas = new Canvas();
                      viewbox.Child = canvas;
                  }
                  canvas.Width = Convert.ToUInt16(data.GetQuery("/logscrdesc/Width"));
                  canvas.Height = Convert.ToUInt16(data.GetQuery("/logscrdesc/Height"));
                  int count = decoder.Frames.Count;
                  frameInfos = new FrameInfo[count];
                  for (int i = 0; i < count; i++)
                  {
                      var info = new FrameInfo(decoder.Frames[i]);
                      Image frame = info.Frame;
                      frameInfos[i] = info;
                      canvas.Children.Add(frame);
                      Panel.SetZIndex(frame, i);
                      canvas.Width = Math.Max(canvas.Width, Canvas.GetRight(frame));
                      canvas.Height = Math.Max(canvas.Height, Canvas.GetBottom(frame));
                  }
                  OnFrameIndexChanged();
                  if (isAnimated)
                  {
                      var keyFrames = new Int32KeyFrameCollection();
                      var last = TimeSpan.Zero;
                      for (int i = 0; i < frameInfos.Length; i++)
                      {
                          last += TimeSpan.FromMilliseconds(frameInfos[i].DelayTime * 10);
                          keyFrames.Add(new DiscreteInt32KeyFrame(i, last));
                      }
                      animation = new Int32AnimationUsingKeyFrames
                      {
                          KeyFrames = keyFrames,
                          RepeatBehavior = loop == 0 ? RepeatBehavior.Forever : new RepeatBehavior(loop)
                      };
                      StartAnimation();
                  }
              }
              IsLoading = false;
          }
      
          private void OnFrameIndexChanged()
          {
              if (frameInfos != null)
              {
                  int index = FrameIndex;
                  frameInfos[index].Frame.Visibility = Visibility.Visible;
                  if (index > 0)
                  {
                      var previousInfo = frameInfos[index - 1];
                      switch (previousInfo.DisposalMethod)
                      {
                          case DisposalMethod.RestoreBackground:
                              // 隱藏之前的所有幀
                              for (int i = 0; i < index - 1; i++)
                              {
                                  frameInfos[i].Frame.Visibility = Visibility.Hidden;
                              }
                              break;
                          case DisposalMethod.RestorePrevious:
                              // 隱藏上一幀
                              previousInfo.Frame.Visibility = Visibility.Hidden;
                              break;
                      }
                  }
                  else
                  {
                      // 重新循環, 只顯示第一幀
                      for (int i = 1; i < frameInfos.Length; i++)
                      {
                          frameInfos[i].Frame.Visibility = Visibility.Hidden;
                      }
                  }
              }
          }
      }
      

      使用到的從 URL 獲取圖像流的方法

      using System;
      using System.IO;
      using System.IO.Packaging;
      using System.Net;
      using System.Threading.Tasks;
      using System.Windows;
      
      public static class ResourceHelper
      {
          public static Task<Stream> GetStream(Uri uri)
          {
              if (!uri.IsAbsoluteUri)
              {
                  throw new ArgumentException("uri must be absolute");
              }
              if (uri.Scheme == Uri.UriSchemeHttps
                  || uri.Scheme == Uri.UriSchemeHttp
                  || uri.Scheme == Uri.UriSchemeFtp)
              {
                  return Task.Run<Stream>(() =>
                  {
                      using (var client = new WebClient())
                      {
                          byte[] data = client.DownloadData(uri);
                          return new MemoryStream(data);
                      }
                  });
              }
              else if (uri.Scheme == PackUriHelper.UriSchemePack)
              {
                  var info = uri.Authority == "siteoforigin:,,,"
                      ? Application.GetRemoteStream(uri)
                      : Application.GetResourceStream(uri);
                  if (info != null)
                  {
                      return Task.FromResult(info.Stream);
                  }
              }
              else if (uri.Scheme == Uri.UriSchemeFile)
              {
                  return Task.FromResult<Stream>(File.OpenRead(uri.LocalPath));
              }
              throw new FileNotFoundException(uri.OriginalString);
          }
      }
      

      調用示例

      <gif:GifImage UriSource="C:\animation.gif"/>
      

      ImageAnimator

      WinForm 中播放 GIF 用到了 ImageAnimator,利用它也可以在 WPF 中實現 GIF 動圖控件,但其是基于 GDI 的方法,更推薦性能更好、支持硬解的解碼器方法

      // 將多幀圖像顯示為動畫,并觸發事件
      ImageAnimator.Animate(Image, EventHandler)
      
      // 暫停動畫
      ImageAnimator.StopAnimate(Image, EventHandler)
      
      // 判斷圖像是否支持動畫
      ImageAnimator.CanAnimate(Image)
      
      // 在圖像中前進幀,下次渲染圖像時繪制新幀
      ImageAnimator.UpdateFrames(Image)
      

      透明 GIF

      GIF 本身只有 256 色,沒有 Alpha 通道,但其仍支持透明,是通過其特殊的自定義顏色表調色盤實現的

      上圖是一張單幀透明 GIF,使用 Windows 自帶畫圖打開,會錯誤顯示為橙色背景

      放入 WinForm PictureBox 中,Win7 和較舊的 Win10 也會錯誤顯示為橙色背景

      但最新的 Win11 和 Win10 上會顯示為透明背景,猜測是近期 Win11 在截圖工具中推出了錄制 GIF 功能時順手更新了 .NET System.Drawing GIF 解析方法,Win10 也收到了這次補丁更新

      不過使用 WPF 解碼器方法能過獲得正確的背景

      相關資料

      Table of Contents

      Native Image Format Metadata Queries - Win32 apps

      WICGifGraphicControlExtensionProperties (wincodec.h) - Win32 apps | Microsoft Learn

      WICGifImageDescriptorProperties (wincodec.h) - Win32 apps | Microsoft Learn

      [WPF疑難]在WPF中顯示動態GIF - 周銀輝 - 博客園

      wpf GifBitmapDecoder 解析 gif 格式

      濃縮的才是精華:淺析GIF格式圖片的存儲和壓縮 - 騰訊云開發者 - 博客園

      posted @ 2025-08-11 09:25  藍點lilac  閱讀(704)  評論(4)    收藏  舉報
      主站蜘蛛池模板: 99热精品毛片全部国产无缓冲| 成在人线av无码免费看网站直播 | 国产老头多毛Gay老年男| 久久国产自拍一区二区三区| 国产av午夜精品福利| 亚洲精品一区国产| 久久三级国内外久久三级| 少妇激情一区二区三区视频| 亚洲欧美日韩成人综合一区| 一本av高清一区二区三区| 久久青青草原亚洲AV无码麻豆| 中文字幕国产精品专区| 国产女同疯狂作爱系列| 亚洲精品一区二区三区蜜| 国产精品自在自线视频| 婷婷久久香蕉五月综合加勒比| 热久久这里只有精品99| 亚洲深深色噜噜狠狠网站| 亚洲精品天堂在线观看| 久久夜色精品国产噜噜亚洲sv| 亚洲av色综合久久综合| 久久综合久中文字幕青草| 亚洲性猛交xxxx| 酒店大战丝袜高跟鞋人妻| 国产高清一区二区不卡| 亚洲国产区男人本色vr| 亚洲嫩模喷白浆在线观看| 国产av综合色高清自拍| 国产亚洲精品AA片在线播放天 | 一级片免费网站| 四虎永久免费精品视频| 米奇亚洲国产精品思久久| 亚洲国产日韩在线视频| 色爱av综合网国产精品| 国产精品无码素人福利不卡| 国产乱码精品一区二区三| 少妇人妻av无码专区| 久热久热久热久热久热久热| 精品久久久中文字幕一区| 成人年无码av片在线观看| 欧日韩无套内射变态|