当前位置: 首页 > news >正文

【学习笔记】Windows GDI绘图(十三)动画播放ImageAnimator(可调速)

文章目录

  • 前言
  • 定义
  • 方法
    • CanAnimate 是否可动画显示
    • Animate 动画显示多帧图像
    • UpdateFrames
    • StopAnimate终止动画
    • Image.GetFrameCount 获取动画总帧数
    • Image.GetPropertyItem(0x5100) 获取帧延迟
  • 自定义GIF播放(可调速)

前言

在前一篇文章中用到ImageAnimator获取了GIF动画的一些属性,但没有真正使用ImageAnimator来进行GIF动画播放(还可能误导),所有这篇就深入了解ImageAnimator的使用。
本文使用的动画为另一篇文章 心动(GDI+) 使用 GDI+纯手工绘制生成,有兴趣的可以查看原文,并配原码。

定义

public sealed class ImageAnimator

作用:对具有基于时间帧的图像进行动画显示。

方法

CanAnimate 是否可动画显示

原型:

public static bool CanAnimate (System.Drawing.Image image);

作用:判断指定图像是否包含基于时间帧。

Animate 动画显示多帧图像

原型:

public static void Animate (System.Drawing.Image image, EventHandler onFrameChangedHandler);

作用:将多帧图像显示为动画。

UpdateFrames

原型:

public static void UpdateFrames ();
public static void UpdateFrames (System.Drawing.Image image);

作用:使多帧动画前推进,下次渲染时,使用新的图像。

1、加载一个动画Image.FromFile
2、判断该图像是基于时间帧的图像 CanAnimate
3、指定帧切换事件 Animate
4、推进多帧动画
5、绘制最新图像

private Image gifImg = null;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif()
{gifImg = Image.FromFile("Heartbeat.gif");if (ImageAnimator.CanAnimate(gifImg)){ImageAnimator.Animate(gifImg, OnFrameChanged);}
}
//切换帧事件
private void OnFrameChanged(object o, EventArgs e)
{//强制显示this.paintCtrl_Main.Panel_Main.Invalidate();
}[System.ComponentModel.Description("ImageAnimator的Animate方法")]
public void Demo13_01(PaintEventArgs e)
{if (gifImg == null){LoadGif();}ImageAnimator.UpdateFrames(gifImg);e.Graphics.DrawImage(gifImg, 0, 0, this.paintCtrl_Main.Panel_Main.Width, this.paintCtrl_Main.Panel_Main.Height);
}

由以上代码,使用ImageAnimator加GDI+绘制,可以很容易实现GIF动画的播放。
Gif动画播放

StopAnimate终止动画

原型:

public static void StopAnimate (System.Drawing.Image image, EventHandler onFrameChangedHandler);

作用:停止动画显示。(实际上没有真正停止,还需要其他条件配合)

private Image gifImg = null;
EventHandler OnFrameChangedEventHandler;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif()
{gifImg = Image.FromFile("Heartbeat.gif");if (ImageAnimator.CanAnimate(gifImg)){OnFrameChangedEventHandler = new EventHandler(OnFrameChanged);isStop = false;ImageAnimator.Animate(gifImg, OnFrameChangedEventHandler);this.paintCtrl_Main.Panel_Main.Click += PaintCtrl_Main_Click; ;}
}private void PaintCtrl_Main_Click(object sender, EventArgs e)
{if (isStop){ImageAnimator.Animate(gifImg, OnFrameChangedEventHandler);}else{ImageAnimator.StopAnimate(gifImage, OnFrameChangedEventHandler);}isStop = !isStop;
}private bool isStop = false;
private void OnFrameChangedStop(object o, EventArgs e)
{isStop = true;
}//切换帧事件
private void OnFrameChanged(object o, EventArgs e)
{//强制显示this.paintCtrl_Main.Panel_Main.Invalidate();
}[System.ComponentModel.Description("ImageAnimator的Animate方法")]
public void Demo13_01(PaintEventArgs e)
{if (gifImg == null){LoadGif();}if (!isStop){ImageAnimator.UpdateFrames(gifImg);}e.Graphics.DrawImage(gifImg, 0, 0, this.paintCtrl_Main.Panel_Main.Width,this.paintCtrl_Main.Panel_Main.Height);
}

通过鼠标点击,控制Gif动画开始,还是停止。

Image.GetFrameCount 获取动画总帧数

原型:

public int GetFrameCount (System.Drawing.Imaging.FrameDimension dimension);

作用:返回指定维度的帧数。

Image.GetPropertyItem(0x5100) 获取帧延迟

作用:返回每帧的延迟数

/// <summary>
/// 获取帧总数、帧延迟
/// </summary>
private void GetFrameDelays()
{//总帧数var frameCount = gifImg.GetFrameCount(FrameDimension.Time);var frameDelay = gifImg.GetPropertyItem(0x5100);//FrameDelay 帧延迟//var loopCount= gifImg.GetPropertyItem(0x5110);//LoopCount GIF循环计数byte[] values = frameDelay.Value;// 每个延迟时间占 4 个字节var frameDelays = new int[frameCount];for (int i = 0; i < frameCount; i++){frameDelays[i] = BitConverter.ToInt32(values, i * 4) * 10; // 单位是 1/100 秒,转换为毫秒}
}

自定义GIF播放(可调速)

使用ImageAnimator的Animate的速度由Gif内置的每帧时间延迟控制,具体可见GetFrameDelays函数示例。如果需要实现自定义速度播放,可使用Timer来控制播放速度。
1、使用Image.FromFile加载Gif动画
2、用ImageAnimator.CanAnimate判断是否为多帧动画
3、获取Gif动画的FrameDimension和总帧数
4、定义并启用一个Timer,设置Interval控制播放速度
5、设置Timer.Tick函数,切换当前帧数和强制控件刷新
6、设置控件MouseClick事件,左键timer.Interval减小,右键Interval加大
7、在Paint事件中SelectActiveFrame激活指定帧,然后绘制

[System.ComponentModel.Description("ImageAnimator的GIF动画播放可调速")]
public void Demo13_02(PaintEventArgs e)
{if (gifImg == null){LoadGif2();}if (FrameCount > 0){gifImg.SelectActiveFrame(dimension, currentFrame);e.Graphics.DrawImage(gifImg, 0, 0, gifImg.Width, gifImg.Height);e.Graphics.DrawString($"左键加速/右键减速 {1000 / GifTimer.Interval}帧/秒,当前帧:{currentFrame}", Font, Brushes.Red, new PointF(20, 20));}
}FrameDimension dimension;
/// <summary>
/// 加载多帧图像
/// </summary>
private void LoadGif2()
{gifImg = Image.FromFile("Heartbeat.gif");if (ImageAnimator.CanAnimate(gifImg)){dimension = new FrameDimension(gifImg.FrameDimensionsList[0]);FrameCount = gifImg.GetFrameCount(dimension);GifTimer = new Timer();GifTimer.Interval = 40;GifTimer.Tick += OnTick;GifTimer.Start();this.paintCtrl_Main.Panel_Main.MouseClick += Panel_Main_MouseClick;}
}
private int currentFrame = 0;
/// <summary>
/// 切换当前帧数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTick(object sender, EventArgs e)
{currentFrame = (currentFrame + 1) % FrameCount;this.paintCtrl_Main.Panel_Main.Invalidate();
}private Timer GifTimer;
//加/减速
private void Panel_Main_MouseClick(object sender, MouseEventArgs e)
{if (e.Button == MouseButtons.Right){GifTimer.Interval *= 2;}else{var interval = GifTimer.Interval / 2;if (interval < 1){interval = 1;}GifTimer.Interval = interval;}
}
int FrameCount = 0;

使用Timer控制Gif播放

相关文章:

【学习笔记】Windows GDI绘图(十三)动画播放ImageAnimator(可调速)

文章目录 前言定义方法CanAnimate 是否可动画显示Animate 动画显示多帧图像UpdateFramesStopAnimate终止动画Image.GetFrameCount 获取动画总帧数Image.GetPropertyItem(0x5100) 获取帧延迟 自定义GIF播放(可调速) 前言 在前一篇文章中用到ImageAnimator获取了GIF动画的一些属…...

fps游戏如何快速定位矩阵

fps游戏如何快速定位矩阵 矩阵特点: 1、第一行第一列值的范围在**-1 ---- 1**之间&#xff0c;如果开镜之后值会变大。 2、第一行第三列的值始终为 0。 3、第一行第四列 的值比较大 &#xff0c; >300或者**<-300**。 根据这三个特点&#xff0c;定位矩阵已经足够了…...

【机器学习基础】Python编程06:五个实用练习题的解析与总结

Python是一种广泛使用的高级编程语言,它在机器学习领域中的重要性主要体现在以下几个方面: 简洁易学:Python语法简洁清晰,易于学习,使得初学者能够快速上手机器学习项目。 丰富的库支持:Python拥有大量的机器学习库,如scikit-learn、TensorFlow、Keras和PyTorch等,这些…...

R可视化:生存分析森林图

在R语言中,使用forestplot包来绘制生存分析的森林图是一个专业且直观的方式来展示各种风险因素或治疗对生存结果的影响。森林图(Forest Plot)常用于展示多项研究的效应量和其可信区间,尤其在生存分析中,它可以清晰地显示不同变量或因素对生存时间的影响程度和统计显著性。…...

一个 python+tensorFlow训练1万张图片分类的简单直观例子( 回答由百度 AI 给出 )

问题&#xff1a;给定一个文件夹 train_images&#xff0c;里面有10000张30*30像素的灰度值图片&#xff0c;第1~第10000张图片的名称分别为 00001.png、 00002.png、... 09999.png、10000.png,train_images 下面还有一个 image_category_map.txt文件&#xff0c; 文件的内容…...

DBeaver无法连接Clickhouse,连接失败

DBeaver默认下载的是0.2.6版本的驱动&#xff0c;但是一直连接失败&#xff1a; 报错提示 解决办法 点击上图中的Open Driver Configuration点击库 - 重置为默认状态在弹出的窗口中修改驱动版本号为0.2.4或者其他版本&#xff08;我没有试用过其他版本&#xff09;&#xff0…...

python基础实例

下一个更大的数 定义一个Solution类&#xff0c;用于实现next_great方法 class Solution: def next_great(self, nums1, nums2): # 初始化一个空字典answer&#xff0c;用于存储答案 answer {} # 初始化一个空列表stack&#xff0c;用于存储待比较的数字 stack [] # 遍历nu…...

ADASIS V2 协议-1

ADAS V2协议-1 1 简介2 版本控制3 ADASIS v23.1 ADASIS v2 Horizon &#xff08;地平线&#xff09;3.2 ADASIS v2的构建3.3 ADASIS v2 Horizon Provider &#xff08;ADAS V2地平线提供者&#xff09;3.4 paths and offsets &#xff08;路径和偏移量&#xff09;3.5 Path Pro…...

人工智能安全风险分析及应对策略

文│中国移动通信集团有限公司信息安全管理与运行中心 张峰 江为强 邱勤 郭中元 王光涛 人工智能&#xff08;AI&#xff09;是引领新一轮科技革命和产业变革的关键技术。人工智能赋能网络安全的同时&#xff0c;也会带来前所未有的安全风险。本文在介绍人工智能技术赋能网络安…...

Python驱动下的AI革命:技术赋能与案例解析

在当今这个信息化、数据化的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为推动社会发展的重要力量。而Python&#xff0c;作为一种简单易学、功能强大的编程语言&#xff0c;在AI领域的应用中发挥着至关重要的作用。本文将探讨Python在AI领域的应用、其背后的技…...

JavaScrip轮播图

前言 在网页设计中&#xff0c;轮播图&#xff08;Carousel&#xff09;已经成为一种常见的元素&#xff0c;用于展示一系列的图片或内容卡片。它们不仅能够吸引用户的注意力&#xff0c;还能节省空间&#xff0c;使得用户可以在有限的空间内获得更多的信息。今天&#xff0c;我…...

达梦8 网络中断对系统的影响

测试环境&#xff1a;三节点实时主从 版本&#xff1a;--03134283938-20221019-172201-20018 测试1 系统没有启动确认监视器 关闭节点3网卡 登录节点1检查主库状态 显示向节点2发送归档成功&#xff0c;但无法收到节点3的消息&#xff0c;节点1挂起 日志报错如下&#xf…...

OpenAI发布GPT-4思维破解新策略,Ilya亦有贡献!

OpenAI正在研究如何破解GPT-4的思维&#xff0c;并公开了超级对齐团队的工作&#xff0c;Ilya Sutskever也在作者名单中。 论文地址&#xff1a;https://cdn.openai.com/papers/sparse-autoencoders.pdf 代码&#xff1a;https://github.com/openai/sparse_autoencoder 特征可…...

[消息队列 Kafka] Kafka 架构组件及其特性(二)Producer原理

这边整理下Kafka三大主要组件Producer原理。 目录 一、Producer发送消息源码流程 二、ACK应答机制和ISR机制 1&#xff09;ACK应答机制 2&#xff09;ISR机制 三、消息的幂等性 四、Kafka生产者事务 一、Producer发送消息源码流程 Producer发送消息流程如上图。主要是用…...

faiss ivfpq索引构建

假设已有训练好的向量值&#xff0c;构建索引&#xff08;nlist和随机样本按需选取&#xff09; import numpy as np import faiss import pickle from tqdm import tqdm import time import os import random# 读取嵌入向量并保留对应关系 def read_embeddings(directory, ba…...

ffmpeg视频编码原理和实战-(2)视频帧的创建和编码packet压缩

源文件&#xff1a; #include <iostream> using namespace std; extern "C" { //指定函数是c语言函数&#xff0c;函数名不包含重载标注 //引用ffmpeg头文件 #include <libavcodec/avcodec.h> } //预处理指令导入库 #pragma comment(lib,"avcodec.…...

数据结构:线索二叉树

目录 1.线索二叉树是什么&#xff1f; 2.包含头文件 3.结点设计 4.接口函数定义 5.接口函数实现 线索二叉树是什么&#xff1f; 线索二叉树&#xff08;Threaded Binary Tree&#xff09;是一种对普通二叉树的扩展&#xff0c;它通过在树的某些空指针上添加线索来实现更高效的遍…...

宝塔Linux面板-Docker管理(2024详解)

上一篇文章《宝塔Linux可视化运维面板-详细教程2024》,详细介绍了宝塔Linux面板的详细安装和配置方法。本文详细介绍使用Linux面板管理服务器Docker环境。 目录 1、安装Docker 1.1 在线安装 ​编辑 1.2 手动安装 1.3 运行状态 1.4 镜像加速 2 应用商店 3 总览 4 容器 …...

【Linux】进程(8):Linux真正是如何调度的

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;8&#xff09;&#xff1a;Linux真正是如何调度的&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 之前我们讲过&#xff0c;在大…...

R语言探索与分析14-美国房价及其影响因素分析

一、选题背景 以多元线性回归统计模型为基础&#xff0c;用R语言对美国部分地区房价数据进行建模预测&#xff0c;进而探究提高多元回 归线性模型精度的方法。先对数据进行探索性预处理&#xff0c;随后设置虚拟变量并建模得出预测结果&#xff0c;再使用方差膨胀因子对 多重共…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

第22节 Node.js JXcore 打包

Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本&#xff0c;基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...

STL 2迭代器

文章目录 1.迭代器2.输入迭代器3.输出迭代器1.插入迭代器 4.前向迭代器5.双向迭代器6.随机访问迭代器7.不同容器返回的迭代器类型1.输入 / 输出迭代器2.前向迭代器3.双向迭代器4.随机访问迭代器5.特殊迭代器适配器6.为什么 unordered_set 只提供前向迭代器&#xff1f; 1.迭代器…...

Vuex:Vue.js 应用程序的状态管理模式

什么是Vuex&#xff1f; Vuex 是专门为 Vue.js 应用程序开发的状态管理模式 库。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。 在大型单页应用中&#xff0c;当多个组件共享状态时&#xff0c;简单的单向数据流…...