0

I'm coding an application that can take screenshots, it needs to take them ultra fast (multiple a second) then process them. Here is the code I'm using to do it - it works but is extremely slow.

using System.Drawing;
using System.Drawing.Imaging;

public static Bitmap CaptureScreen()
{
    Bitmap BMP = new Bitmap(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
                            System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
                            System.Drawing.Imaging.PixelFormat.Format32bppArgb);
    System.Drawing.Graphics GFX = System.Drawing.Graphics.FromImage(BMP);
    GFX.CopyFromScreen(System.Windows.Forms.Screen.PrimaryScreen.Bounds.X,
                        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Y,
                        0, 0,
                        System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size,
                        System.Drawing.CopyPixelOperation.SourceCopy);

    return BMP;
}

Usage - :

Bitmap im1 = new Bitmap(CaptureScreen());

The above code works fine but takes at least 5 seconds to process. So could someone please provide a method like the one above except faster, I also want to use the foreground window to capture, not the whole screen.

EDIT Here is the comparison code!

private void timer2_Tick(object sender, EventArgs e)
{

    pictureBox1.Image = CaptureScreen();
    pictureBox2.Image = CaptureScreenOld();
    Bitmap im1 = (Bitmap)pictureBox1.Image;
    Bitmap im2 = (Bitmap)pictureBox2.Image;
    for (int y = 0; y < pictureBox1.Height; y++)
    {
        for (int x = 0; x < pictureBox1.Width; x++)
        {
            // Get the color of the current pixel in each bitmap
            Color color1 = im1.GetPixel(x, y);
            Color color2 = im2.GetPixel(x, y);

            // Check if they're the same
            if (color1 != color2)
            {
                // If not, generate a color...
                Color myRed = Color.FromArgb(90, 0, 0);
                // .. and set the pixel in one of the bitmaps
                im2.SetPixel(x, y, myRed);
                pictureBox2.Image = im2;
            }
        }
    }
}
Picrofo Software
  • 5,475
  • 3
  • 23
  • 37
  • Related: http://stackoverflow.com/questions/1163761/c-sharp-capture-screenshot-of-active-window – FThompson Dec 18 '12 at 03:20
  • 3
    What do you need to do with the images? And why are you re-copying the entire image into another `Bitmap`? `Bitmap im1 = CaptureScreen();` should save a lot of time. And *five* seconds? It takes less time for me to run a box blur on a screen capture... just how big is this screen? – Ry- Dec 18 '12 at 03:20
  • 1
    The same code runs on my system (1680 x 1050 resolution) in about 120ms. – Andrew Cooper Dec 18 '12 at 03:31
  • 1600x900. And it compares a first screenshot to a second screenshot, any differences on the second screenshot that isnt on the first screenshot is marked with red. I have that working. – user1911675 Dec 18 '12 at 03:40
  • @user1911675: Your comparison code is probably what's taking the time, then; could you show that code, please? – Ry- Dec 18 '12 at 03:55

1 Answers1

0

Okay, using GetPixel a bunch of times to compare pixels is really slow. Lock your bits instead, into an array:

private static void CompareBitmaps(Bitmap a, Bitmap b) {
    Stopwatch sw = new Stopwatch();

    sw.Start();

    var lockRect = new Rectangle(0, 0, a.Width, a.Height);
    BitmapData ad = a.LockBits(lockRect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
    BitmapData bd = b.LockBits(lockRect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

    Int32[] apixels = new Int32[ad.Width * ad.Height];
    Int32[] bpixels = new Int32[bd.Width * bd.Height];

    Marshal.Copy(ad.Scan0, apixels, 0, apixels.Length);
    Marshal.Copy(bd.Scan0, bpixels, 0, bpixels.Length);

    for(int i = 0; i < apixels.Length; i++) {
        if(apixels[i] != bpixels[i]) {
            unchecked {
                bpixels[i] = (int)0xff5a0000;
            }
        }
    }

    Marshal.Copy(bpixels, 0, bd.Scan0, bpixels.Length);

    a.UnlockBits(ad);
    b.UnlockBits(bd);

    sw.Stop();

    Console.WriteLine("CompareBitmaps took {0}.", sw.Elapsed);
}

I get times of about 240 milliseconds for this; is it enough?

Ry-
  • 218,210
  • 55
  • 464
  • 476