C#电脑游戏(WinForms)简单快速的实时图形
我正在努力使整数数组和Bitmap协调工作,作为快速PictureBox编辑的C#winforms项目的一部分(避免慢速SetPixel命令)。
我添加了一个Button和一个PictureBox,一个关于上述Button的点击事件和一个关闭事件在Form上。
表单的代码现在是这样的:
public partial class Form1 : Form { uint[] _Pixels { get; set; } Bitmap _Bitmap { get; set; } GCHandle _Handle { get; set; } IntPtr _Addr { get; set; } public Form1() { InitializeComponent(); int imageWidth = 100; //1920; int imageHeight = 100; // 1080; PixelFormat fmt = PixelFormat.Format32bppRgb; int pixelFormatSize = Image.GetPixelFormatSize(fmt); int stride = imageWidth * pixelFormatSize; int padding = 32 - (stride % 32); if (padding < 32) { stride += padding; } _Pixels = new uint[(stride / 32) * imageHeight + 1]; _Handle = GCHandle.Alloc(_Pixels, GCHandleType.Pinned); _Addr = Marshal.UnsafeAddrOfPinnedArrayElement(_Pixels, 0); _Bitmap = new Bitmap(imageWidth, imageHeight, stride / 8, fmt, _Addr); pictureBox1.Image = _Bitmap; } private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < _Pixels.Length; i++) { _Pixels[i] = ((uint)(255 | (255 << 8) | (255 << 16) | 0xff000000)); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _Addr = IntPtr.Zero; if (_Handle.IsAllocated) { _Handle.Free(); } _Bitmap.Dispose(); _Bitmap = null; _Pixels = null; } }
运行时,开头会出现预期的黑色图像。
单击按钮时图像应变为白色,但我遗漏了一些东西。
我忘了做什么?
解
更新_Pixels
数组后添加pictureBox1.Refresh()
。
这种更新非常快,并且能够以高分辨率呈现流畅的video。
表单的代码现在是这样的:
public partial class Form1 : Form { uint[] _Pixels { get; set; } Bitmap _Bitmap { get; set; } GCHandle _Handle { get; set; } IntPtr _Addr { get; set; } public Form1() { InitializeComponent(); int imageWidth = 100; //1920; int imageHeight = 100; // 1080; PixelFormat fmt = PixelFormat.Format32bppRgb; int pixelFormatSize = Image.GetPixelFormatSize(fmt); int stride = imageWidth * pixelFormatSize; int padding = 32 - (stride % 32); if (padding < 32) { stride += padding; } _Pixels = new uint[(stride / 32) * imageHeight + 1]; _Handle = GCHandle.Alloc(_Pixels, GCHandleType.Pinned); _Addr = Marshal.UnsafeAddrOfPinnedArrayElement(_Pixels, 0); _Bitmap = new Bitmap(imageWidth, imageHeight, stride / 8, fmt, _Addr); pictureBox1.Image = _Bitmap; } private void button1_Click(object sender, EventArgs e) { for (int i = 0; i < _Pixels.Length; i++) { _Pixels[i] = ((uint)(255 | (255 << 8) | (255 << 16) | 0xff000000)); } pictureBox1.Refresh(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _Addr = IntPtr.Zero; if (_Handle.IsAllocated) { _Handle.Free(); } _Bitmap.Dispose(); _Bitmap = null; _Pixels = null; } }
测试
当然,我想测试这种方法的性能,看它是否足够快以呈现流畅的video。
用计时器勾选替换按钮单击
到目前为止,我的测试很简单。 我用一个timer tick event
替换了button click event
,该timer tick event
每毫秒触发一次,并且我用随机颜色值填充_Pixels
数组。
为了确保PictureBox
不会尝试刷新,直到任何当前刷新完成,我使用一个bool IsRefreshing
变量。
每秒帧数计算
我还测量每次刷新之间经过的时间,通过递增单个int HzCount
变量,并在每个PictureBox
刷新开始时将此变量重置为零。 就在我重置HzCount
*之前,我在TextBox
显示该值。
Intel(R)Core(TM)i7-5600U CPU @ 2.60GHz 2.60GHz,具有16.0 GB RAM结果:
72Hz的2560×1440 @
*赫兹或Hz表示每秒经过的帧数(或多少次)。 通过扩展,这意味着MegaHertz每秒翻译数百万次,而GigaHertz翻译为每秒数千万(美国数十亿)次。
WinForms控件不会“仅仅因为”重绘自己,他们必须有理由这样做。
某些数据源能够告诉其容器已更新。 Bitmap类没有。 它无法告诉包含PictureBox(或用于显示它的任何控件)来告诉它已更新。 它可以在操作BitmapData
实例后调用SetPixel()
设置单个像素或UnlockBits()
,但是当使用固定数组构建位图时,无论如何操作完全不受Bitmap类的控制。
因此,Bitmap类没有事件或其他方式来通知其容器更新。
这意味着您需要告知包含控件其数据源已更新,因此控件可以重绘自身。
您可以按照如何刷新PictureBox中的说明进行操作,即使用pictureBox.Refresh()
。 这会导致PictureBox控件使自身无效,并在下一次(立即)重绘时重新读取现在更改的位图数据。
另请参阅MSDN博客:Control.Invalidate,Control.Update和Control.Refresh之间的区别是什么? 。