WPF 通过 GifBitmapDecoder 调用 WIC 解析 Gif 和进行动画播放的简单方法
本文告诉大家如何在 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 GifBitmapDecoder 解析 gif 格式
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 和进行动画播放的简单方法的更多相关文章
- C#调用脚本语言(三)-- IronJS 与 IronLua 简单方法性能比较
1. 测试环境 1.1. 硬件环境 CPU:intel Core i7-740QM 内存:8GDDR3 Memory 1.2. 系统 系统:Windows 8 Enterprise 开发工具:Vs ...
- 【WPF学习】第四十九章 基本动画
在前一章已经学习过WPF动画的第一条规则——每个动画依赖于一个依赖项属性.然而,还有另一个限制.为了实现属性的动态化(换句话说,使用基于时间的方式改变属性的值),需要有支持相应数据类型的动画类.例如, ...
- 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)
原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...
- poll调用深入解析
poll调用深入解析http://blog.csdn.net/zmxiangde_88/article/details/8099049 poll调用和select调用实现的功能一样,都是网络IO利用的 ...
- javascript ajax 脚本跨域调用全解析
javascript ajax 脚本跨域调用全解析 今天终于有点时间研究了一下javsscript ajax 脚本跨域调用的问题,先在网上随便搜了一下找到一些解决的办法,但是都比较复杂.由是转到jqu ...
- saltstack主机管理项目:动态调用插件解析-模块解析(五)
一.动态调用插件解析 1.目录结构 1.base_module代码解析: def syntax_parser(self,section_name,mod_name,mod_data): print(& ...
- wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name
原文:wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name 写了一个 Storyb ...
- WPF 用代码调用dynamic resource动态更改背景 - CSDN博客
原文:WPF 用代码调用dynamic resource动态更改背景 - CSDN博客 一般dynamic resoource通常在XAML里调用,如下范例: <Button Click=&qu ...
- WPF 精修篇 调用Win32Api
原文:WPF 精修篇 调用Win32Api 栗子是 调用WIn32API 让窗口最前 后台代码 [DllImport("user32.dll")] private static e ...
- WPF控件相对位置解析
WPF控件相对位置的获取方法是比较简单的.对于初学者来说,掌握这一技巧的应用,可以帮助以后对WPF的深入学习,而且在实际使用中,这是一个非常常用的方法. 我们知道WPF有着比较灵活的布局方式,关于某个 ...
随机推荐
- 记录--for in 和 for of的区别详解以及为for in的输出顺序
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 for in 和 for of 相对于大家肯定都不陌生,都是用来遍历属性的没错.那么先看下面的一个例子: 例1 const obj = { ...
- 《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南 - 第6章
本章勘误: 暂无,等待细心的你告诉我哦. 本章注解: 暂无 本章释疑: 暂无,等待你的提问 致谢: MVP 林德熙 MVP 吕毅 sPhinX 相关链接 试读记录
- Eagle+欧奥PicHome创建私有的pinterest网站
Pinterest和花瓣网, 是设计师寻找灵感的天堂!它能够帮你采集.存储和发现灵感.可以说是设计师必用的网站. Eagle是设计师个人的灵感收集工具,它能够方便的采集素材,并快速为图片标签,分类,评 ...
- KingbaseES V8R6集群备份恢复案例之---备份初始化“can not find primary node”故障
案例说明: KingbaseES V8R6集群,备库作为repo-path节点,建立类型为'cluster'模式的备份,在执行sys_backup.sh init时,出现"can not f ...
- kingbaseES V8R3集群运维案例之---集群部署前后ssh端口修改
kingbaseES V8R3集群运维案例之---集群部署前后ssh端口修改 案例说明: kingbaseES V8R3集群部署读写分离的集群是使用ssh的默认端口(22)部署,当改为非默认端口时,在 ...
- archlinux virtualbox使用文件共享 主机arch,客机windows8.1 windows10
参照 https://www.cnblogs.com/cuitang/p/11263008.html 1.安装virtualbox增强功能VBoxGuestAdditions.iso (1)从virt ...
- Typora基础使用教程
Typora基础使用教程(入门级) 安装和激活 安装 typora任意地方搜索下载即可 激活 百度网盘链接链接:https://pan.baidu.com/s/1WKig_3-hkDZTRjS1rgG ...
- USACO 4.1
目录 洛谷 2737 麦香牛块 分析 代码 洛谷 2738 篱笆回路 分析 代码 麦香牛块洛谷传送门,麦香牛块USACO传送门,篱笆回路洛谷传送门,篱笆回路USACO传送门 洛谷 2737 麦香牛块 ...
- #SG函数#HDU 1848 Fibonacci again and again
题目 分析 可取状态只能是斐波那契数,求出SG函数 然后判断三个数sg函数异或和不为0先手必胜 代码 #include <cstdio> #include <cctype> # ...
- 资深技术笔译总结的这7条建议,看完提PR效率倍增
战码先锋,PR征集令(以下简称"战码先锋")第二期正如火如荼地进行中,涉及OpenAtom OpenHarmony(以下简称"OpenHarmony")主干仓. ...