Simple and fast real-time graphics for C# computer game (WinForms)

让人想犯罪 __ 提交于 2019-12-23 03:48:06

问题


I'm trying to make an integer array and Bitmap work in harmony as part of a C# winforms project for fast PictureBox editing (avoiding the slow SetPixel command).

I added a Button and a PictureBox, a click event on the aformentioned Button and a closing event on the Form.

The code for the Form is now like so:

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;

    }

}

When run, a black image as expected is present in the beginning.

The image should turn white when the button is clicked, but I'm missing something.

What am I forgetting to do?


回答1:


WinForms controls don't redraw themselves "just because", they have to have a reason to do so.

Some data sources have the ability to tell their container that they have been updated. A Bitmap class doesn't. It has no way of telling the containing PictureBox (or whatever control you use to display it) to tell that it was updated. It could when you call SetPixel() to set single pixels, or UnlockBits() after manipulating a BitmapData instance, but it can't when the bitmap was constructed using a pinned array that you manipulate entirely out of control of the Bitmap class anyway.

So the Bitmap class doesn't have events or other ways to notify its container of updates.

This means you need to tell the containing control that its data source has been updated, so the control can redraw itself.

You can do so as explained in How to refresh PictureBox, namely using pictureBox.Refresh(). This causes the PictureBox control to invalidate itself, and upon the next (immediate) repaint to re-read the now altered bitmap data.

See also MSDN Blogs: Whats the difference between Control.Invalidate, Control.Update and Control.Refresh?.




回答2:


Solution

Add pictureBox1.Refresh() after updating the _Pixels array.

This updates quite quickly, and is capable of rendering smooth video at high resolutions.

Add reference to System.Runtime.InteropServices (available via nuget)

The code for the form is now like so:

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;

    }

}

Testing

Naturally, I wanted to test performance of this method to see if it would be fast enough to render smooth video.

Replace Button click with Timer tick

My test so far is a simple one to begin with. I replaced the button click event with a timer tick event which fires once every millisecond, and I fill the _Pixels array with random colour values.

To ensure that the PictureBox will not try to refresh until any current refreshes have completed, I use a single bool IsRefreshing variable.

Frames per second calculation

I also measure the time elapsed between each refresh, by incrementing a single int HzCount variable, and resetting this variable to zero at the beginning of each PictureBox refresh. Just before I reset the HzCount*, I display the value in a TextBox.

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

72Hz@2560x1440

*Hertz or Hz means frames (or how many times something) elapsed per second. By extension, this means that MegaHertz translates to millions of times a second and GigaHertz translates to thousand-millions (USA billions) of times a second.



来源:https://stackoverflow.com/questions/39387698/simple-and-fast-real-time-graphics-for-c-sharp-computer-game-winforms

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!