本文告诉大家如何在 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. .NET分布式Orleans - 5 - 持久化

    在分布式系统中,数据的持久化是至关重要的一环. Orleans 7 引入了强大的持久化功能,使得在分布式环境下管理数据变得更加轻松和可靠. 本文将介绍什么是 Orleans 7 的持久化,如何设置它以 ...

  2. C# 日志监控软件 基于 FileSystemWatcher

    效果: 代码如下: using System; using System.Collections.Generic; using System.ComponentModel; using System. ...

  3. MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

    本文基于 Linux 内核 5.4 版本进行讨论 自上篇文章<从 Linux 内核角度探秘 JDK MappedByteBuffer> 发布之后,很多读者朋友私信我说,文章的信息量太大了, ...

  4. Android实现简单登录界面

    LoginActivity 1 package com.example.myapplication; 2 3 import android.os.Bundle; 4 import android.vi ...

  5. mybatis添加maven依赖

    1 <dependencies> 2 <!--导入Mybatis依赖包--> 3 <dependency> 4 <groupId>org.mybatis ...

  6. Linux中JMeter的使用

    Linux中JMeter的使用 Linux版本JMeter安装 # 1.下载.安装JMeter 如果有安装包直接上传即可 wget -c https://archive.apache.org/dist ...

  7. 【直播回顾】OpenHarmony知识赋能五期第四课——子系统音频解读

    5月12日晚上19点,知识赋能第五期第四节课<OpenHarmony标准系统多媒体子系统之音频解读>,在OpenHarmony开发者成长计划社群内成功举行. 本期课程,由深开鸿资深技术专家 ...

  8. 如何在现实场景中随心放置AR虚拟对象?

    随着AR的发展和电子设备的普及,人们在生活中使用AR技术的门槛降低,比如对于不方便测量的物体使用AR测量,方便又准确:遇到陌生的路段使用AR导航,清楚又便捷:网购时拿不准的物品使用AR购物,体验更逼真 ...

  9. Maven 必备技能:MAC 系统下 JDK和Maven 安装及环境变量配置详细讲解

    开发中难免因系统问题或者版本变更反复折腾JDK和Maven环境变量,干脆写个笔记备忘个,也方便小伙伴们节省时间. =================JDK安装与环境变量配置====== 1.官网下载j ...

  10. 史上最全的中高级JAVA工程师-面试题汇总

    史上最全的中高级JAVA工程师-面试题汇总 置顶 2019-10-15 18:58:32 Jeff.Smile 阅读数 34460更多 分类专栏: # 随笔 版权声明:本文为博主原创文章,遵循CC 4 ...