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有着比较灵活的布局方式,关于某个 ...
随机推荐
- linux上pip install mysqlclient报错
linux上pip install mysqlclient报错 django连接mysql数据库时 乱糟糟的 一大片红色报错,查了半天资料,失败了无数次,最终终于成功 先用以下代码: sudo apt ...
- JS服务端技术—Node.js知识点
[版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18031964 出自[进步*于辰的博客] 目录 1.NP ...
- vivado的VIO调试工具的使用
vivado中的VIO调试工具的使用 1.实验原理 前面一篇介绍了ILA的独立测试,vivado中还有其他的FPGA测试工具.其中VIO就是个比较常用的工具.相对于ILA更多的关注波形,VIO则专注于 ...
- UE4 c++ -- 简单的UMG
说明 学习一下如何将Widget蓝图与C++连接起来,将处理逻辑写在C++中 基础 在蓝图中,我们显示Widget是通过一个Actor或者PlayerController,甚至关卡蓝图,利用Creat ...
- UE4蓝图对Actor的引用
通过关卡蓝图调用 在关卡中放置一个Actor,在关卡蓝图中右键 create a reference to actor,即可 注意使用该方法创建时,需要现在关卡中选择上该类Actor 当Actor生成 ...
- kafka集群启动命令脚本文件kf.sh
注意代码缩进 添加执行权限 chmod +x kf.sh 1 #! /bin/bash 2 case $1 in 3 "start"){ 4 for i in hadoop102 ...
- 2020.02.05【NOIP提高组】模拟A 组
[toc] CF293B Distinct Paths=JZOJ 4012 CF261E Maxim and Calculator=JZOJ 4010 JZOJ 2292 PPMM 题目 满足队列出入 ...
- Web服务器启用HTTPS的配置方法
本文于2016年3月完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. nginx的配置方法 可以参考Jerry Qu的本博客 Nginx 配 ...
- 重磅官宣,OpenHarmony技术峰会来了
技术构筑万物智联 创新使能行业发展 2月25日 第一届开放原子开源基金会OpenHarmony技术峰会即将启幕 众多行业大咖齐聚深圳 开启一场"技术硬核"探索盛宴 亮点拉满,我 ...
- 文档贡献与写作必读-OpenHarmony开发者文档风格指南
在您使用OpenHarmony文档或参与OpenHarmony文档/生态内容贡献时,是否遇到过如下问题: ● 应该使用第一人称还是第二人称来写作? ● Markdown文件应该如何命名? ● 代码块及 ...