【问题标题】:Simple and fast real-time graphics for C# computer game (WinForms)用于 C# 计算机游戏的简单快速的实时图形 (WinForms)
【发布时间】:2017-01-16 04:41:08
【问题描述】:

我正在尝试使整数数组和位图作为 C# winforms 项目的一部分协调工作,以便快速编辑 PictureBox(避免缓慢的 SetPixel 命令)。

我添加了一个Button和一个PictureBox,在上述Button上添加了一个点击事件,在Form上添加了一个关闭事件。

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;

    }

}

运行时,一开始会出现预期的黑色图像。

单击按钮时图像应该变成白色,但我错过了一些东西。

我忘了做什么?

【问题讨论】:

    标签: c# winforms performance bitmap picturebox


    【解决方案1】:

    解决方案

    更新_Pixels数组后添加pictureBox1.Refresh()

    这个更新很快,并且能够以高分辨率呈现流畅的视频。

    添加对 System.Runtime.InteropServices 的引用(可通过 nuget 获得)

    表单的代码现在是这样的:

    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;
    
        }
    
    }
    

    测试

    当然,我想测试这种方法的性能,看看它是否足够快以呈现流畅的视频。

    将按钮点击替换为计时器滴答声

    到目前为止,我的测试是一个简单的开始。我用每毫秒触发一次的timer tick event 替换了button click event,并用随机颜色值填充_Pixels 数组。

    为确保PictureBox 在当前所有刷新完成之前不会尝试刷新,我使用了一个bool IsRefreshing 变量。

    每秒帧数计算

    我还测量每次刷新之间经过的时间,方法是增加单个 int HzCount 变量,并在每次 PictureBox 刷新开始时将此变量重置为零。就在我重置HzCount* 之前,我将值显示在TextBox 中。

    Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz 2.60GHz with 16.0 GB RAM 结果:

    72Hz@2560x1440

    *赫兹或赫兹表示每秒经过的帧数(或多少次)。通过扩展,这意味着兆赫兹转换为每秒数百万次,千兆赫兹转换为每秒数百万次(美国数十亿次)。

    【讨论】:

      【解决方案2】:

      WinForms 控件不会“仅仅因为”重绘自己,它们必须有这样做的理由。

      某些数据源能够告知其容器它们已更新。 Bitmap 类没有。它无法告诉包含的 PictureBox(或您用来显示它的任何控件)告诉它已更新。当您调用 SetPixel() 来设置单个像素,或者在操作 BitmapData 实例之后调用 UnlockBits() 时,它可以,但是当使用完全不受 Bitmap 类控制的固定数组构造位图时,它不能反正。

      因此,Bitmap 类没有事件或其他方式来通知其容器的更新。

      这意味着您需要告诉包含控件其数据源已更新,以便控件可以重绘自身。

      您可以按照How to refresh PictureBox 中的说明进行操作,即使用pictureBox.Refresh()。这会导致 PictureBox 控件自身失效,并在下一次(立即)重绘时重新读取现在更改的位图数据。

      另见MSDN Blogs: Whats the difference between Control.Invalidate, Control.Update and Control.Refresh?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-10
        • 2018-08-19
        • 2012-05-03
        • 1970-01-01
        • 1970-01-01
        • 2022-06-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多