在上一篇文章 C#图形编程之GDI绘图:(三) 普通级之双缓冲 中我们介绍了两种双缓冲的机制,通过编码实现了第一种方式,在计算量较少的场景下得到了比较满意的效果。但是当计算了较大时,刷新就出现了卡顿。
本篇文章介绍第二种双缓冲方式。再来回顾下我们的代码:
private void panel1_Paint(object sender, PaintEventArgs e)
{
//黑色底清屏
e.Graphics.Clear(Color.Black);
//第一次输出fps
//实际上这里是不需要的,但由于入门级的绘制太慢,如果不在绘制前输出,就看不到了
e.Graphics.DrawString(string.Format("fps: {0}", (int)fps), Font, Brushes.Yellow, 10, 20);
//绘制小方格,并根据坐标变化实现上移滚动效果
int size = 1;
int xCount = panel1.Width / size + 1;
int yCount = panel1.Height * 2 / size + 1;
for (int i=0; i< xCount; i++)
{
for (int j = 0; j < yCount; j++)
{
int left = i * size * 2;
int top = j * size * 2 - start_top - size * 2;
Rectangle rectItem = new Rectangle(left, top, size, size);
e.Graphics.FillRectangle(Brushes.AliceBlue, rectItem);
}
}
//计算fps
calculateFPS();
//第二次输出fps
e.Graphics.DrawString(string.Format("fps: {0}", (int)fps), Font, Brushes.Yellow, 10, 20);
}
最耗时间的无疑是两个for循环,如果是500*500像素的界面大小,每次刷新都要做25万次的操作,这太恐怖了。所以这里是我们要优化的关键点。事实上,每次重复的绘制工作都是一样的,只是坐标不同,我们完全可以把绘制工作一次做完,然后保存成一张图到内存中。每次刷新我们只要调整这张图的起始坐标不就可以了吗?这样做,即便是计算量更大也无所谓,因为我只要绘制一次就OK,输出就是一张屏,效率非常稳定。这也正是我们在上一篇文章中提及的第二种方式的双缓冲。原理说清楚了,下面开始设计:
(1)调用刷新,如果内存图为空,则执行绘制;
(2)输出时,分上下两个部分。根据坐标变化,上半部分输出的是剩余的部分,下半部分输出的是超出屏幕的部分,这样就可以完美拼接了;
下面直接上代码:
Bitmap bmpMem = null;
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (bmpMem == null)
{
bmpMem = new Bitmap(panel1.Width, panel1.Height);
Graphics g = Graphics.FromImage(bmpMem);
int size = 5;
//绘制小方格,并根据坐标变化实现上移滚动效果
int xCount = panel1.Width / size + 1;
int yCount = panel1.Height * 2 / size + 1;
for (int i = 0; i < xCount; i++)
{
for (int j = 0; j < yCount; j++)
{
int left = i * size * 2;
int top = j * size * 2 - start_top - size * 2;
Rectangle rectItem = new Rectangle(left, top, size, size);
g.FillRectangle(Brushes.AliceBlue, rectItem);
}
}
g.Dispose();
}
//黑色底清屏
e.Graphics.Clear(Color.Black);
//第一次输出fps
//实际上这里是不需要的,但由于入门级的绘制太慢,如果不在绘制前输出,就看不到了
e.Graphics.DrawString(string.Format("fps: {0}", (int)fps), Font, Brushes.Yellow, 10, 20);
if (bmpMem != null)
{
int step = scroll_top % panel1.Height;
Rectangle rectDestTop = new Rectangle(0, 0, panel1.Width, panel1.Height - step);
Rectangle rectSrcTop = new Rectangle(0, step, panel1.Width, panel1.Height - step);
e.Graphics.DrawImage(bmpMem, rectDestTop, rectSrcTop, GraphicsUnit.Pixel);
Rectangle rectDestBottom = new Rectangle(0, panel1.Height - step, panel1.Width, step);
Rectangle rectSrcBottom = new Rectangle(0, 0, panel1.Width, step);
e.Graphics.DrawImage(bmpMem, rectDestBottom, rectSrcBottom, GraphicsUnit.Pixel);
scroll_top++;
}
//计算fps
calculateFPS();
//第二次输出fps
e.Graphics.DrawString(string.Format("fps: {0}", (int)fps), Font, Brushes.Yellow, 10, 20);
}
效果看下图演示:
由于视频转Gif软件的限制,每帧最少要100ms,所以效果不是很明显,可以看到左上的fps达到了63帧左右。无论是放大还是更小的size,都能够保持稳定。细心的读者也许会发现,在两屏衔接的地方稍有瑕疵,因为这个不是本文要介绍的重点,就不展开了,喜欢钻研的读者可以思考下为什么,如何能够改进。
至此,C#图形编程之GDI绘图系列就讲完了。学会了这个,基本可以达到商用级了。
美帝对我们进行了工业软件的封锁,工业软件会涉及到大量的图形化编程。C#做工控软件运行效率比Java高,界面开发比C++容易,笔者做过很多工控方面的项目都是用的C#。希望此文能够给有此方面兴趣的读者带来些许帮助,也祝福我们的国产工业软件早日踏上征途,领先世界。
记录工作和生活中的干货,感兴趣的朋友转发+关注。任何想法可在评论留言。
页面更新:2024-03-11
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号