【学习笔记】Windows GDI绘图(十二)双缓冲管理(用GIF动画测试)

2024-06-07 18:36

本文主要是介绍【学习笔记】Windows GDI绘图(十二)双缓冲管理(用GIF动画测试),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 引言
  • 默认双缓冲
    • SetStyle
  • 手动管理双缓冲图形
    • BufferedGraphicsManager缓冲图形管理器
    • BufferedGraphicsContext 缓冲图形上下文
    • BufferedGraphics 图形缓冲区
    • 验证双缓冲的效果(Gif动画显示非正常速度)
    • 结束语
      • 性能对比

引言

图形编程中一个常见的问题就是闪烁,当需要绘制多个复杂的图形时可能导致渲染的图像出现闪烁或其他不可接受的形式。

.Net中提供了双缓冲(Double buffering)来解决这一问题。

双缓冲使用内存缓冲区来解决多个绘制操作相关的闪烁问题。启用双缓冲后,所有的绘制操作都会首先渲染到内存的缓冲区,到所有绘制操作完成后,再直接复制到与其关联的绘图表面上。由于在屏幕上仅执行一次图形操作,因此消除了闪烁等问题

默认双缓冲

在.Net中要启用控件的默认双缓冲功能,只需将设置DoubleBuffered为True或调用SetStyle方法。

SetStyle

原型:

protected void SetStyle (System.Windows.Forms.ControlStyles flag, bool value);

ControlStyles枚举

说明
AllPaintingInWmPaint如果为 true,则控件忽略窗口消息 WM_ERASEBKGND 以减少闪烁。
仅当将 UserPaint 位设置为 true 时,才应用此样式。
DoubleBuffer如果为 true,则在缓冲区中进行绘制,并且完成后将结果输出到屏幕。 双缓冲可以防止因重绘控件而引起的闪烁。
如果将 DoubleBuffer 设置为 true,则还应将 UserPaint 和 AllPaintingInWmPaint 设置为 true。
UserPaint如果为 true,则会由控件而不是由操作系统来绘制控件自身。
如果 false,则不会引发 Paint 事件。 此样式仅适用于从 Control 派生的类。
OptimizedDoubleBuffer如果为 true,则控件将首先绘制到缓冲区而不是直接绘制到屏幕,这可以减少闪烁。
如果将此属性设置为 true,则还应将 AllPaintingInWmPaint 设置为 true
ResizeRedraw如果为 true,则控件会在调整大小时进行重绘。

除了设置DoubleBuffered = True,还可以

   this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint,true);this.UpdateStyles();

手动管理双缓冲图形

手动管理双缓冲图形,需要用到BufferedGraphics和

BufferedGraphicsManager缓冲图形管理器

原型:

public static class BufferedGraphicsManager

作用:提供对应用程序域的主缓冲图形上下文对象的访问。

BufferedGraphicsManager类允许你实现自定义双缓冲。一般是获取其属性Current为 BufferedGraphicsContext类型。

BufferedGraphicsContext 缓冲图形上下文

原型:

public sealed class BufferedGraphicsContext : IDisposable

作用:提供创建可用于双缓冲图形缓冲区的方法。

1、通过 BufferedGraphicsManager.Current获取到一个BufferedGraphicsContext 对象
2、设置MaximumBuffer为最大缓冲区大小(一般是显示图像的宽+1和高+1,缓冲区大小的内存为被长期占用,而图像大小缓冲区大小时会被临时使用,直到该对象被释放)。
3、调用Allocate方法,分配一个指定大小的缓冲区图形(BufferedGraphics)

context = BufferedGraphicsManager.Current;
context.MaximumBuffer = new Size(gifImage.Width + 1, gifImage.Height + 1);
bufferdGraphics = context.Allocate(e.Graphics, new Rectangle(0, 0, gifImage.Width, gifImage.Height));

BufferedGraphics 图形缓冲区

原型:

public sealed class BufferedGraphics : IDisposable

作用:提供用于双缓冲的图形缓冲区。

1、其属性Graphics可用于绘制图形
2、再调用Render方法,绘制到指定的Graphics上。

验证双缓冲的效果(Gif动画显示非正常速度)

通过鼠标点击窗体控制切换不同的效果,分别是

  • 禁用双缓冲模式
  • 启用默认双缓冲模式
  • 自定义管理双缓冲模式
    先看看不同方式下的效果
    双缓冲
    1、不启用双缓冲时,屏幕严重闪烁
    2、启用双缓冲时,默认和自定义效果(不知使用方法上有问题)差不多。
    3、注意,这个是用来测试绘制效果的,实际的Gif播放不是这样使用。
    public partial class FrmDoubleBufferd : Form{public FrmDoubleBufferd(){InitializeComponent();}private void FrmDoubleBufferd_Load(object sender, EventArgs e){Init();}/// <summary>/// 双缓冲类型/// 0-禁用双缓冲/// 1-默认双缓冲/// 2-手管理双缓冲/// </summary>private int BufferdType = -1;private void FrmDoubleBufferd_Click(object sender, EventArgs e){BufferdType++;if (BufferdType > 2) BufferdType = 0;if (BufferdType == 0){this.DoubleBuffered = false;this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, false);}else{this.DoubleBuffered = true;this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);}CurrFrameIndex = 0;TotalCount = 0;elaspedTime = new ConcurrentBag<(long, long)>();dt = DateTime.Now;NextGifImage();}private int CurrFrameIndex = 0;private int FrameCount = 0;FrameDimension Dimension;private Image GifImage = null;private Image AIWoman = null;Rectangle srcRect;private BufferedGraphicsContext context;private BufferedGraphics bufferdGraphics;private void Init(){GifImage = Image.FromFile("Heartbeat.gif");AIWoman = Image.FromFile("AIWoman.png");srcRect = new Rectangle(0, 0, AIWoman.Width, AIWoman.Height);if (ImageAnimator.CanAnimate(GifImage)){Dimension = new FrameDimension(GifImage.FrameDimensionsList[0]);FrameCount = GifImage.GetFrameCount(Dimension);}context = BufferedGraphicsManager.Current;context.MaximumBuffer = new Size(GifImage.Width + 1, GifImage.Height + 1);bufferdGraphics = context.Allocate(this.CreateGraphics(), new Rectangle(0, 0, GifImage.Width, GifImage.Height));NextGifImage();}Random random = new Random((int)DateTime.Now.Ticks);private void FrmDoubleBufferd_Paint(object sender, PaintEventArgs e){var dstRect = new Rectangle(random.Next(100, 600), random.Next(10, 600), 400, 400);e.Graphics.DrawImage(GifImage, 0, 0, GifImage.Width, GifImage.Height);e.Graphics.DrawImage(AIWoman, dstRect, srcRect, GraphicsUnit.Pixel);if (BufferdType == 0){e.Graphics.DrawString($"禁用双缓冲模式", Font, Brushes.Red, new PointF(20, 20));DrawElaspedTime(e.Graphics);}else if (BufferdType == 1){e.Graphics.DrawString($"默认双缓冲模式", Font, Brushes.Red, new PointF(20, 20));DrawElaspedTime(e.Graphics);}else{return;}//绘制结束后,显示下一帧NextGifImage();}ConcurrentBag<(long, long)> elaspedTime = new ConcurrentBag<(long, long)>();private void DrawElaspedTime(Graphics g){int height = 40;foreach (var time in elaspedTime){g.DrawString($"{time.Item1},耗时:{time.Item2}", Font, Brushes.Red, new PointF(20, height));height += 20;}}private long TotalCount= 0;DateTime dt = DateTime.Now;bool redraw= false;//切换Gif显示帧private void NextGifImage(){do{TotalCount++;CurrFrameIndex++;if (CurrFrameIndex >= FrameCount){CurrFrameIndex = 0;}if (CurrFrameIndex % 50 == 0){var elasped = (CurrFrameIndex, (long)(DateTime.Now - dt).TotalMilliseconds);elaspedTime.Add(elasped);dt = DateTime.Now;}//更新为下一帧GifImage.SelectActiveFrame(Dimension, CurrFrameIndex);ImageAnimator.UpdateFrames(GifImage);if (BufferdType == 2){//自定义管理双缓冲时,不在Paint事件中更新var dstRect = new Rectangle(random.Next(100, 600), random.Next(10, 400), 400, 400);bufferdGraphics.Graphics.Clear(Color.White);bufferdGraphics.Graphics.DrawImage(GifImage, 0, 0, GifImage.Width, GifImage.Height);bufferdGraphics.Graphics.DrawImage(AIWoman, dstRect, srcRect, GraphicsUnit.Pixel);bufferdGraphics.Graphics.DrawString($"自定义管理双缓冲模式", Font, Brushes.Red, new PointF(20, 20));DrawElaspedTime(bufferdGraphics.Graphics);bufferdGraphics.Render();Application.DoEvents();redraw = true;}else{//触发重绘this.Invalidate();redraw = false;}}while(redraw);            }private void FrmDoubleBufferd_FormClosing(object sender, FormClosingEventArgs e){bufferdGraphics.Dispose();GifImage.Dispose();AIWoman.Dispose();}}

结束语

性能对比

  1. 自定义管理双缓冲
    在这里插入图片描述
  2. 默认双缓冲
    在这里插入图片描述
    左上角的数字意义是,每绘制50帧(绘制Gif的同时还绘制了美女)的耗时(单位ms)。

原本想测试下,自定义管理双缓冲会不会比默认启动的双缓冲性能要高,不知是使用的方法不问题,还是其他什么原因,没感觉到有更高的性能(反而觉得慢了些),如果您有更好的例子可以说明,麻烦留言,万分感谢。

再次强调,实际的Gif动画播放不是像本文那样实现的,可通过ImageAnimator类的相关方法实现,本文之所以这样写,本是想着每绘制完一帧后,开始处理下一帧,看哪种方法更快。

在未完全吃透自定义管理双缓冲情况,建议还是用默认的双缓冲就可以了。

这篇关于【学习笔记】Windows GDI绘图(十二)双缓冲管理(用GIF动画测试)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1039971

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

AI绘图怎么变现?想做点副业的小白必看!

在科技飞速发展的今天,AI绘图作为一种新兴技术,不仅改变了艺术创作的方式,也为创作者提供了多种变现途径。本文将详细探讨几种常见的AI绘图变现方式,帮助创作者更好地利用这一技术实现经济收益。 更多实操教程和AI绘画工具,可以扫描下方,免费获取 定制服务:个性化的创意商机 个性化定制 AI绘图技术能够根据用户需求生成个性化的头像、壁纸、插画等作品。例如,姓氏头像在电商平台上非常受欢迎,

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在