Search code examples
windowswinformsc++-clidrawingpixel

Fast pixel drawing c++ windows forms


I am trying to make a c++ emulator for the original space invaders arcade machine. Everything works just fine but it is slow. After timing several functions in my project i found that the biggest drain is my draw screen functions. It takes +100ms but i need 16ms/frame, e.g. 60Hz. This is my function:

unsigned char *fb = &state->memory[0x2400];
Bitmap ^ bmp = gcnew Bitmap(512, 448);

for (int j = 1; j < 224; j++)
{
    for (int i = 0; i < 256; i+=8)
    {
        unsigned char pix = fb[(j * 256 / 8) + i / 8];
        for (int p = 0; p < 8; p++)
        {
            if (0 != (pix & (1 << p)))
            {
                bmp->SetPixel(i + p, j, Color::White);
            }
            else
            {
                bmp->SetPixel(i + p, j, Color::Black);
            }
        }
    }
}
bmp->RotateFlip(RotateFlipType::Rotate270FlipNone);
this->pictureBox1->Image = bmp;

fb is my framebuffer, a byte array with 8 pixels per byte, 1 for white, 0 for black.

Browsing the internet i found that the "slow part" is the SetPixel method, but i couldn't find a better method to do this.


Solution

  • Thanks to Loathings comment i solved the problem. Instead of redrawing every pixel, i first clear the image to black and then only the white pixels are drawn. Had to use a buffer to prevent flickering

            unsigned char *fb = &state->memory[0x2400];
            Bitmap ^ bmp = gcnew Bitmap(512, 448);
            Image ^buffer = this->pictureBox1->Image;
            Graphics ^g = Graphics::FromImage(buffer);
            for (int j = 1; j < 224; j++)
            {
                for (int i = 0; i < 256; i += 8)
                {
                    unsigned char pix = fb[(j * 256 / 8) + i / 8];
                    for (int p = 0; p < 8; p++)
                    {
                        if (0 != (pix & (1 << p)))
                        {
                            bmp->SetPixel(i + p, j, Color::White);
                        }
    
                    }
                }
            }
            bmp->RotateFlip(RotateFlipType::Rotate270FlipNone);
    
            g->Clear(Color::Black);
            buffer = bmp;
            this->pictureBox1->Image = buffer;
    

    and at initialisation i added next code to prevent nullexception being thrown,

            Bitmap ^def = gcnew Bitmap(20, 20);
            this->pictureBox1->Image = def;
    

    Update: Although this method works for my project, considering the screen will be mostly composed of only black pixels. GetPixel and SetPixel are slow, and shouldn't be used with performance sensitive programs. LockBits will be the way to go. I didn't implement this because i got the performance i needed by just clearing the graphics interface