本文告诉大家如何在 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. PagerAdapter深度解析和实践优化

    目录介绍 01.PagerAdapter简单介绍 02.PagerAdapter抽象方法 03.PagerAdapter原理介绍 04.PagerAdapter缓存和销毁 05.自定义PagerAda ...

  2. C#人脸对比服务(基于虹软人脸识别SDKV4.1封装)

    软件截图   项目截图 部分代码 using System; using System.Collections.Generic; using System.Linq; using System.Tex ...

  3. Kingbase ES函数参数模式与Oracle的异同

    文章概要: 本文对主要就KES和Oracle的PLSQL中关于存储过程参数模式异同进行介绍,列举和验证了存在的差异 (如果想直接看差异的结论可直接跳到末尾). 一,存储过程的三种参数模式 重新回顾一下 ...

  4. 32位x86处理器编程架构

    1. IA-32架构的基本执行环境 1.1 寄存器的扩展   为了在汇编语言程序中使用经过扩展(Extend) 的寄存器:   在32位模式下,为了生成32位物理地址,处理器需要使用32位的指令指针寄 ...

  5. 为 AI 而生的编程语言「GitHub 热点速览」

    Mojo 是一种面向 AI 开发者的新型编程语言.它致力于将 Python 的简洁语法和 C 语言的高性能相结合,以填补研究和生产应用之间的差距.Mojo 自去年 5 月发布后,终于又有动作了.最近, ...

  6. #差分约束系统,最长路,线段树优化建边#洛谷 3588 [POI2015] PUS

    题目 给定一个长度为\(n\)的正整数序列 \(a\) ,每个数都在 \(1\) 到 \(10^9\) 范围内, 告诉你其中 \(s\) 个数,并给出 \(m\) 条信息,每条信息包含三个数 \(l, ...

  7. 【直播回顾】参与文档贡献,开启OpenHarmony社区贡献

      5月25日晚上19点,战"码"先锋第二期直播 <参与文档贡献,开启OpenHarmony社区贡献> ,在OpenHarmony社群内成功举行.   本期课程,由华为 ...

  8. 内容分发策略与 SEO 优化指南

    内容分发 内容分发是指通过各种媒介分享.发布或传播内容给受众的过程.这些媒介可以包括不同的渠道,例如社交媒体平台(Facebook.Twitter.LinkedIn.朋友圈.微博.小红书.B 站.抖音 ...

  9. C#利用自动化接口编写OPC客户端,OPC Client,源码直接放网盘

    引用:https://www.cnblogs.com/flh1/p/12409266.html 链接: https://pan.baidu.com/s/1Vs08c7qjShEc9GQ8dvCkdg ...

  10. VSCode如何通过Ctrl+P快速打开node_modules中的文件

    背景 咱们新建一个NodeJS项目,必然会安装许多依赖包,因此经常需要查阅某些依赖包的源码文件.但是,由于node_modules目录包含的文件太多,出于性能考虑,在VSCode中默认情况下是禁止搜索 ...