本文告诉大家如何在 WPF 里,通过 GifBitmapDecoder 调用 WIC 层来解析 GIF 图片,然后采用动画的方式进行播放

上一篇博客告诉大家,可以通过 GifBitmapDecoder 调用 WIC 层解析 GIF 图片

使用 WIC 层解析 GIF 图片可以调用系统默认解码器,对 GIF 的支持较好,也能支持很多诡异的格式,而且对这些诡异的图片的行为保持和其他应用相同

本文在上一篇博客的基础上,告诉大家如何使用动画播放方式,进行播放 GIF 图片

这是一个简单的方式,优势在于使用动画播放,十分简单。缺点在于只能支持简单的 GIF 图片格式,也就是每一帧都是全画的 GIF 文件,如果只是范围更新的,那么效果很差

本文的实现可以从本文最后拿到所有代码,下面来告诉大家这是如何做的。 先创建一个继承 FrameworkElement 类型的 GifImage 类,将在这个类里面播放 GIF 图片

定义 GifSource 依赖属性,在依赖属性变更时,进行初始化逻辑

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging; class GifImage : FrameworkElement
{
public static readonly DependencyProperty GifSourceProperty = DependencyProperty.Register(
"GifSource", typeof(Uri), typeof(GifImage), new UIPropertyMetadata(default(Uri), GifSourcePropertyChanged)); public Uri GifSource
{
get { return (Uri) GetValue(GifSourceProperty); }
set { SetValue(GifSourceProperty, value); }
} private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as GifImage).Initialize();
} private void Initialize()
{
// 初始化
}
}

在上面的 Initialize 是本文的核心逻辑,将初始化 GIF 的解析

初始化逻辑采用 GifBitmapDecoder 进行解析,代码如下

    private void Initialize()
{
_gifDecoder = new GifBitmapDecoder(GifSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
}
private GifBitmapDecoder _gifDecoder;

可以通过 _gifDecoder.Frames 拿到 GIF 的多个图片,每个图片信息,都可以通过 BitmapMetadata 的 GetQuery 方法获取参数,可以选择的参数有很多,如下

  • /grctlext 控制信息
  • /grctlext/Disposal 处置方法,表示如何处理上一张图片,如替换为背景色等
  • /grctlext/TransparencyFlag 透明色选项
  • /grctlext/Delay 延迟时间,单位是 10 分之一毫秒
  • /grctlext/TransparentColorIndex 透明色索引
  • /imgdesc 图片描述
  • /imgdesc/Left 当前张图片所在的左上坐标和宽高,这里指的是左值
  • /imgdesc/Top 当前张图片所在的左上坐标和宽高,这里指的是上值
  • /imgdesc/Width 当前张图片所在的左上坐标和宽高,这里指的是宽度
  • /imgdesc/Height 当前张图片所在的左上坐标和宽高,这里指的是高度

其他的还有 /grctlext/UserInputFlag /imgdesc/LocalColorTableFlag /imgdesc/InterlaceFlag /imgdesc/SortFlag /imgdesc/LocalColorTableSize 等。详细请看 Native Image Format Metadata Queries - Win32 apps Microsoft Docs

使用 /grctlext/Delay 获取延时时间,根据延时时间创建动画。动画的方式就是修改当前使用第几张图片

    private void Initialize()
{
_gifDecoder = new GifBitmapDecoder(GifSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); var keyFrames = new Int32KeyFrameCollection();
TimeSpan last = TimeSpan.Zero;
for (int i = 0; i < _gifDecoder.Frames.Count; i++)
{
var gifDecoderFrame = _gifDecoder.Frames[i];
var bitmapMetadata = gifDecoderFrame.Metadata as BitmapMetadata;
var delayTime = bitmapMetadata?.GetQuery("/grctlext/Delay") as ushort?;
var delay = delayTime ?? 10;
if (delay == 0)
{
delay = 10;
}
last += TimeSpan.FromMilliseconds(delay * 10);
keyFrames.Add(new DiscreteInt32KeyFrame(i, KeyTime.FromTimeSpan(last)));
} _animation = new Int32AnimationUsingKeyFrames()
{
KeyFrames = keyFrames,
RepeatBehavior = RepeatBehavior.Forever,
};
} private GifBitmapDecoder _gifDecoder;
private Int32AnimationUsingKeyFrames _animation;

添加一个叫播放的函数,调用此函数时,将执行动画

    /// <summary>
/// Starts the animation
/// </summary>
public void StartAnimation()
{
BeginAnimation(FrameIndexProperty, _animation);
} public static readonly DependencyProperty FrameIndexProperty =
DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex))); static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var gifImage = obj as GifImage;
gifImage.ChangingFrameIndex((int) e.NewValue);
} private void ChangingFrameIndex(int index)
{
InvalidateVisual();
}

通过动画修改 FrameIndexProperty 从而通过依赖属性修改进入 InvalidateVisual 方法,让框架重新调用 OnRender 方法

    protected override void OnRender(DrawingContext drawingContext)
{
var gifDecoderFrame = _gifDecoder.Frames[FrameIndex]; drawingContext.DrawImage(gifDecoderFrame,new Rect(new Size(gifDecoderFrame.PixelWidth, gifDecoderFrame.PixelHeight)));
}

如此即可完成播放

此类型的代码如下

class GifImage : FrameworkElement
{
private bool _isInitialized;
private GifBitmapDecoder _gifDecoder;
private Int32AnimationUsingKeyFrames _animation; public int FrameIndex
{
get { return (int) GetValue(FrameIndexProperty); }
set { SetValue(FrameIndexProperty, value); }
} private void Initialize()
{
_gifDecoder = new GifBitmapDecoder(GifSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); var keyFrames = new Int32KeyFrameCollection();
TimeSpan last = TimeSpan.Zero;
for (int i = 0; i < _gifDecoder.Frames.Count; i++)
{
var gifDecoderFrame = _gifDecoder.Frames[i];
var bitmapMetadata = gifDecoderFrame.Metadata as BitmapMetadata;
var delayTime = bitmapMetadata?.GetQuery("/grctlext/Delay") as ushort?;
var delay = delayTime ?? 10;
if (delay == 0)
{
delay = 10;
}
last += TimeSpan.FromMilliseconds(delay * 10);
keyFrames.Add(new DiscreteInt32KeyFrame(i, KeyTime.FromTimeSpan(last)));
} _animation = new Int32AnimationUsingKeyFrames()
{
KeyFrames = keyFrames,
RepeatBehavior = RepeatBehavior.Forever,
}; _isInitialized = true;
} static GifImage()
{
VisibilityProperty.OverrideMetadata(typeof(GifImage),
new FrameworkPropertyMetadata(VisibilityPropertyChanged));
} private static void VisibilityPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if ((Visibility) e.NewValue == Visibility.Visible)
{
((GifImage) sender).StartAnimation();
}
else
{
((GifImage) sender).StopAnimation();
}
} public static readonly DependencyProperty FrameIndexProperty =
DependencyProperty.Register("FrameIndex", typeof(int), typeof(GifImage), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(ChangingFrameIndex))); static void ChangingFrameIndex(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var gifImage = obj as GifImage;
gifImage.ChangingFrameIndex((int) e.NewValue);
} private void ChangingFrameIndex(int index)
{
InvalidateVisual();
} protected override void OnRender(DrawingContext drawingContext)
{
var gifDecoderFrame = _gifDecoder.Frames[FrameIndex]; drawingContext.DrawImage(gifDecoderFrame,new Rect(new Size(gifDecoderFrame.PixelWidth, gifDecoderFrame.PixelHeight)));
} /// <summary>
/// Defines whether the animation starts on it's own
/// </summary>
public bool AutoStart
{
get { return (bool) GetValue(AutoStartProperty); }
set { SetValue(AutoStartProperty, value); }
} public static readonly DependencyProperty AutoStartProperty =
DependencyProperty.Register("AutoStart", typeof(bool), typeof(GifImage), new UIPropertyMetadata(false, AutoStartPropertyChanged)); private static void AutoStartPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if ((bool) e.NewValue)
(sender as GifImage).StartAnimation();
} public static readonly DependencyProperty GifSourceProperty = DependencyProperty.Register(
"GifSource", typeof(Uri), typeof(GifImage), new UIPropertyMetadata(default(Uri), GifSourcePropertyChanged)); public Uri GifSource
{
get { return (Uri) GetValue(GifSourceProperty); }
set { SetValue(GifSourceProperty, value); }
} private static void GifSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as GifImage).Initialize();
} /// <summary>
/// Starts the animation
/// </summary>
public void StartAnimation()
{
if (!_isInitialized)
this.Initialize(); BeginAnimation(FrameIndexProperty, _animation);
} /// <summary>
/// Stops the animation
/// </summary>
public void StopAnimation()
{
BeginAnimation(FrameIndexProperty, null);
}
}

除此之外的其他播放 GIF 方法,请看:

WPF 一个性能比较好的 gif 解析库

WPF 播放 gif

更多请看

gif 格式

wpf GifBitmapDecoder 解析 gif 格式

本文以上的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin e11f2ea15fd5107fac4bd4523580587ce7febd56

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 CairjawworalhulalGeacharkucoha 文件夹

WPF 通过 GifBitmapDecoder 调用 WIC 解析 Gif 和进行动画播放的简单方法的更多相关文章

  1. C#调用脚本语言(三)-- IronJS 与 IronLua 简单方法性能比较

    1.   测试环境 1.1. 硬件环境 CPU:intel Core i7-740QM 内存:8GDDR3 Memory 1.2. 系统 系统:Windows 8 Enterprise 开发工具:Vs ...

  2. 【WPF学习】第四十九章 基本动画

    在前一章已经学习过WPF动画的第一条规则——每个动画依赖于一个依赖项属性.然而,还有另一个限制.为了实现属性的动态化(换句话说,使用基于时间的方式改变属性的值),需要有支持相应数据类型的动画类.例如, ...

  3. 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

    原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  4. poll调用深入解析

    poll调用深入解析http://blog.csdn.net/zmxiangde_88/article/details/8099049 poll调用和select调用实现的功能一样,都是网络IO利用的 ...

  5. javascript ajax 脚本跨域调用全解析

    javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...

  6. saltstack主机管理项目:动态调用插件解析-模块解析(五)

    一.动态调用插件解析 1.目录结构 1.base_module代码解析: def syntax_parser(self,section_name,mod_name,mod_data): print(& ...

  7. wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name

    原文:wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name 写了一个 Storyb ...

  8. WPF 用代码调用dynamic resource动态更改背景 - CSDN博客

    原文:WPF 用代码调用dynamic resource动态更改背景 - CSDN博客 一般dynamic resoource通常在XAML里调用,如下范例: <Button Click=&qu ...

  9. WPF 精修篇 调用Win32Api

    原文:WPF 精修篇 调用Win32Api 栗子是 调用WIn32API 让窗口最前 后台代码 [DllImport("user32.dll")] private static e ...

  10. WPF控件相对位置解析

    WPF控件相对位置的获取方法是比较简单的.对于初学者来说,掌握这一技巧的应用,可以帮助以后对WPF的深入学习,而且在实际使用中,这是一个非常常用的方法. 我们知道WPF有着比较灵活的布局方式,关于某个 ...

随机推荐

  1. 记录--短视频滑动播放在 H5 下的实现

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 短视频已经无数不在了,但是主体还是使用 app 来承载的.本文讲述 H5 如何实现 app 的视频滑动体验. 无声胜有声,一图顶百辩,且看 ...

  2. C++ Concurrency in Action 读书笔记三:并发操作的同步

    Chapter 4 并发操作的同步·Synchronizing concurrent operations

  3. Jmeter的Throughput有误差与分布式测试时的坑

    我是两台压力机,分布式启动jmeter压测180秒,结果throughput显示3075,我用总请求数/总耗时,64万左右/180秒,得到的TPS是3500左右.误差17% 网上说jmeter的thr ...

  4. MySQL备份还原工具

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  5. 以最大速度将数据迁移至AWS S3存储

    数据上云,已经成为企业数据管理发展的必然趋势. 对于企业而言,数据上云"常态化"的趋势,无疑是一个巨大的技术红利.而数据规模爆发式增长的今天,移动和访问云端数据却成为困扰企业的一大 ...

  6. 感悟:FPGA的串行及并行设计思路

    前言 FPGA设计过程中, 会遇到大量的串行转并行或者并行转串行的问题; 这些问题主要体现在FPGA对于速度和面积的均衡上; 一般而言, FPGA使用并行的设计可以提高处理的速度, 消耗更多的资源; ...

  7. 算法学习笔记【1】| LCA(最近公共祖先)

    LCA(最近公共祖先) Part 1:逐步上跳 假设u,v是所求的两点,先把深度大的点逐步上移直到深度相同. 然后两者同时上移,直到第一次遇到相同的点为止. 时间复杂度: O(n)<script ...

  8. 基于IDEA javaweb项目目录结构

    https://www.pianshen.com/article/62631355687/

  9. 【WCH以太网接口系列芯片】CH9121\20的使用和测试

    本篇文章将介绍沁恒微电子的以太网转接芯片CH9121(CH9120和CH9121使用上没有区别,注意配置工具不一样,可以在沁恒微电子官网自行下载测试),该芯片支持网口和串口相互透传,可以通过串口AT指 ...

  10. OpenHarmony 官网文档有哪些上新?下篇:设备开发文档上新

    为了方便社区开发者更易获取 OpenAtom OpenHarmony(以下简称"OpenHarmony")相关文档,暨上篇应用开发文档上新内容,SIG Docs  小组同步准备了设 ...