如何直接在窗体缓冲区上绘制(使用指针)

时间:2019-04-19 13:28:33

标签: c# winforms

我有一些可视数据缓冲区,该缓冲区的变化很快。而且我需要以至少60fps的速度绘制它。

在我发现的所有其他类似问题中-建议使用Bitmap作为临时缓冲区,如下所示:


        public unsafe TimeSpan CopyToFormBuffer(float* buff)
        {
            var b = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
            var bd = b.LockBits(this.ClientRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            //this is more of a GPU work, but for simplicity i put it on CPU:
            var hy = bd.Scan0;
            for (int y = 0; y < this.ClientSize.Height; ++y, hy += bd.Stride)
            {
                var hx = hy;
                for (int x = 0; x < this.ClientSize.Width; ++x, hx += 4, buff += 4)
                {
                    var p = (byte*)hx;
                    p[3] = Convert.ToByte(buff[0] * 255);
                    p[2] = Convert.ToByte(buff[1] * 255);
                    p[1] = Convert.ToByte(buff[2] * 255);
                    p[0] = Convert.ToByte(buff[3] * 255);
                }
            }

            b.UnlockBits(bd);
            var sw = Stopwatch.StartNew();
            this.CreateGraphics().DrawImage(b, 0, 0);
            sw.Stop();
            return sw.Elapsed;
        }
        private unsafe void Form1_Load(object sender, EventArgs e)
        {

            var buff = (float*)Marshal.AllocHGlobal(this.ClientSize.Width * this.ClientSize.Height * 4 * sizeof(float));

            for (var i = 100; i > 0; i--)
                MessageBox.Show(CopyToFormBuffer(buff).ToString());

        }

但是这样,行:

            this.CreateGraphics().DrawImage(b, 0, 0);

进行额外的复制操作,这太慢了(在全屏模式下,我的电脑上80-90ms)。

因此,有没有办法像我对Bitmap的{​​{1}}那样直接绘制表单缓冲区?

注意,即使在示例数据中,视觉数据缓冲区只是4хFloat栅格颜色的数组,对于更复杂的格式,我也需要相同的颜色,因此无论如何都需要进行某种颜色转换。

1 个答案:

答案 0 :(得分:0)

这里是您使用Forms Paint()事件尝试完成的实现。这比每帧分配一个屏幕缓冲区要快得多。您可以在表单中的任何位置修改“ buff”像素缓冲区,它将在表单中重新绘制。您只需要调用this.Invalidate()即可强制调用Paint。每当您更改buff时,请在表单本身上调用Invalidate。

 public partial class Form1 : Form
    {
        private unsafe float* buff;

        public unsafe Form1()
        {
            InitializeComponent();
            buff = (float*)Marshal.AllocHGlobal(this.ClientSize.Width * this.ClientSize.Height * 4 * sizeof(float));
        }

        private unsafe void Form1_Paint(object sender, PaintEventArgs e)
        {
            var sw = Stopwatch.StartNew();

            var b = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
            var bd = b.LockBits(this.ClientRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

            //this is more of a GPU work, but for simplicity i put it on CPU:
            var hy = bd.Scan0;
            for (int y = 0; y < this.ClientSize.Height; ++y, hy += bd.Stride)
            {
                var hx = hy;
                for (int x = 0; x < this.ClientSize.Width; ++x, hx += 4, buff += 4)
                {
                    var p = (byte*)hx;
                    p[3] = Convert.ToByte(buff[0] * 255);
                    p[2] = Convert.ToByte(buff[1] * 255);
                    p[1] = Convert.ToByte(buff[2] * 255);
                    p[0] = Convert.ToByte(buff[3] * 255);
                }
            }

            b.UnlockBits(bd);
            e.Graphics.DrawImage(b, 0, 0);
            sw.Stop();

            Debug.WriteLine($"Frame took {sw.Elapsed}ms to draw");
        }
    }