0

I have a function which isn't so complicated, but for some reason, each time I call it, it freezes the screen for like quarter of a second. This function is on a timer and is deployed every second so it can get pretty annoying to the user.

The function basically gets a screen shot of the screen, checks if the screen that it's looking for by comparing 10 pixels and returns true if that's the screen or false otherwise :

ClassName.CheckScreen(CaptureScreen(),ClassName.Pxarr1); //Capture screen isn't the problem. It gave me no freezes in an endless loop.

Thats the class :

class ClassName
{
    public static Pixel[] Pxarr1 = new[]
    {
        new Pixel(Color.FromArgb(255, 204, 204, 170), new Point(15, 145)),
        new Pixel(Color.FromArgb(255, 221, 204, 187), new Point(20, 460)),
        new Pixel(Color.FromArgb(255, 221, 204, 187), new Point(20, 545)),
        new Pixel(Color.FromArgb(255, 204, 187, 170), new Point(15, 150)),
        new Pixel(Color.FromArgb(255, 221, 204, 187), new Point(22, 190)),
        new Pixel(Color.FromArgb(255, 204, 204, 187), new Point(25, 540)),
        new Pixel(Color.FromArgb(255, 204, 187, 153), new Point(22, 61)),
        new Pixel(Color.FromArgb(255, 221, 204, 170), new Point(23, 563)),
        new Pixel(Color.FromArgb(255, 204, 187, 153), new Point(23, 47)),
        new Pixel(Color.FromArgb(255, 204, 204, 187), new Point(23, 463)),

    };

    public static Pixel[] Pxarr2 = new[]
    {
        new Pixel(Color.FromArgb(255, 221, 255, 119), new Point(80, 120)),
        new Pixel(Color.FromArgb(255, 51, 119, 221), new Point(180, 525)),
        new Pixel(Color.FromArgb(255, 204, 170, 85), new Point(630, 455)),
        new Pixel(Color.FromArgb(255, 85, 153, 17), new Point(707, 177)),
        new Pixel(Color.FromArgb(255, 255, 153, 34), new Point(520, 440)),
        new Pixel(Color.FromArgb(255, 255, 238, 51), new Point(150, 325)),
        new Pixel(Color.FromArgb(255, 0, 85, 255), new Point(455, 70)),
        new Pixel(Color.FromArgb(255, 255, 221, 51), new Point(685, 285)),
        new Pixel(Color.FromArgb(255, 17, 17, 17), new Point(547, 369)),
        new Pixel(Color.FromArgb(255, 170, 170, 136), new Point(500, 545)),

    };

    public static Pixel[] Pxarr3 = new[]
    {
        new Pixel(Color.FromArgb(255, 238, 238, 238), new Point(353, 223)),
        new Pixel(Color.FromArgb(255, 28, 33, 49), new Point(428, 198)),
        new Pixel(Color.FromArgb(255, 85, 85, 85), new Point(462, 314)),
        new Pixel(Color.FromArgb(255, 221, 221, 238), new Point(450, 450)),
        new Pixel(Color.FromArgb(255, 102, 102, 102), new Point(384, 349)),
        new Pixel(Color.FromArgb(255, 204, 204, 204), new Point(406, 248)),
        new Pixel(Color.FromArgb(255, 221, 221, 221), new Point(464, 453)),
        new Pixel(Color.FromArgb(255, 255, 204, 17), new Point(413, 198)),
        new Pixel(Color.FromArgb(255, 204, 204, 204), new Point(343, 447)),
        new Pixel(Color.FromArgb(255, 255, 255, 255), new Point(403, 457)),

    };

    public static Pixel[] Pxarr4 = new[]
    {
        new Pixel(Color.FromArgb(255, 204, 170, 136), new Point(120, 227)),
        new Pixel(Color.FromArgb(255, 221, 187, 153), new Point(502, 170)),
        new Pixel(Color.FromArgb(255, 119, 85, 34), new Point(692, 243)),
        new Pixel(Color.FromArgb(255, 238, 221, 187), new Point(211, 169)),
        new Pixel(Color.FromArgb(255, 187, 170, 136), new Point(272, 238)),
        new Pixel(Color.FromArgb(255, 170, 153, 119), new Point(696, 64)),
        new Pixel(Color.FromArgb(255, 136, 85, 17), new Point(306, 242)),
        new Pixel(Color.FromArgb(255, 187, 170, 119), new Point(115, 236)),
        new Pixel(Color.FromArgb(255, 204, 187, 153), new Point(310, 183)),
        new Pixel(Color.FromArgb(255, 153, 102, 34), new Point(647, 245)),

    };

    public static Pixel[] Pxarr5 = new[]
    {
        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(376, 150)),
        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(376, 350)),

        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(506, 150)),
        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(506, 350)),

        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(246, 150)),
        new Pixel(Color.FromArgb(255, 255, 238, 221), new Point(246, 350))
    };

    public static bool CheckScreen(Bitmap img, Pixel[] samples)
    {
        int verifiedpixels = 0;
        for (int i = 0; i < samples.Length; i++)
        {
            Color c = img.GetPixel(samples[i].Location.X, samples[i].Location.Y);
            if (c == samples[i].Color)
                    verifiedpixels++;
        }
        return verifiedpixels >= (samples.Length * 0.6);
    }

}

Note : A 100 loop for statement was twice as slow with CaptureScreen() than with an image from a file. (4 seconds vs 2 seconds)

But still, I don't understand why this function casuses any freezing, even with an image from a file.

Jalal Said
  • 15,906
  • 7
  • 45
  • 68
user779444
  • 1,365
  • 4
  • 21
  • 38

2 Answers2

2

Since you are using a timer, it should not freeze the UI, unless you are using System.Windows.Forms.Timer which will execute its call back on the UI thread. Instead use System.Timers.Timer or System.Threading.Timer. Example for System.Timers.Timer:

private System.Timers.Timer _checkPixelsTimer = new System.Timers.Timer();

public Form1()
{
    InitializeComponents();

    _checkPixelsTimer.Intervla = 1000;//one second
    _checkPixelsTimer.AutoReset = true;
    _checkPixelsTimer.Elapced += OnCheckPixelsTimerElapced;
    _checkPixelsTimer.Start();
}

private void OnCheckPixelsTimerElapced(object sender, System.Timer.ElapcedEventArgs e)
{
    if (ClassName.CheckScreen(CaptureScreen(),ClassName.Pxarr1))
    {
        MethodInvoker method = new MethodInvoker(() => { /*code that relays on the UI thread */ });
        if (this.InvokeRequired)
        {
            this.Invoke(method); 
        }
        else
        {
            method();
        }
    }
}

Edit: update the code to preview how to call a function at the UI thread.

Jalal Said
  • 15,906
  • 7
  • 45
  • 68
  • I'm not convinced that this is the posters problem. – Mitch Wheat Jul 24 '11 at 01:26
  • @Mitch: You should!, He is using another thread, the UI should not **freeze** even if the function is taking a lot of time and process. for instance: test this: `new Thread(() => { while(true) Thread.SpinWait(500000); }){ IsBackground = true }.Start();` it will eat up the cpu but you may notice that the UI will not freeze... – Jalal Said Jul 24 '11 at 01:31
  • yes, I understand how threads/UI thread work. Perhaps the poster should post his full code.... – Mitch Wheat Jul 24 '11 at 01:39
  • You are correct about the threads. I have this function working on another thread and its unnoticeable. But I can't use another thread because I need a real-time answer. if(CheckScreen() == true) {/*Do that*/}. Unfortunately, /*Do That*/ involves controls from the main thread. – user779444 Jul 24 '11 at 01:59
  • The solution is **Simple**: at the other thread `if (CheckScreen()) { this.Invoke(new MethodInvoker(() => { /*Do that*/ }));}` – Jalal Said Jul 24 '11 at 02:01
  • Thanks! Combined threading and the timer : private void OnCheckPixelsTimerElapced(object sender, EventArgs e) { Thread thread = new Thread(FunctionS); thread.Start(); } private void FunctionS() { CheckScreen(CaptureScreen(),Pxarr1) } – user779444 Jul 24 '11 at 02:39
  • @user: you are welcome, but note that if you use `System.Timers.Timer` you don't have to use a new thread to invoke inside it because the elapsed event handler in fact is already executing on a different thread. – Jalal Said Jul 24 '11 at 02:43
1

GetPixel() and SetPixel() are notoriously slow.

Use an alternative method such as the LockBits()/UnlockBits() technique. See Fast work with Bitmaps in C#

Community
  • 1
  • 1
Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541