1

I need help getting my program to match the "stored" colour with the current one in the same location then click the mouse if it's the same. The grabbing of the colour works great so far in my code just unsure how to match a colour and a point, etc.

Also a start/stop button for the loop would be nice.

My code so far:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Pixel_detection_test_3
{
    public partial class PixelDetectionForm : Form
    {
        private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
        private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;

        [DllImport("user32.dll")]
        private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);

        private int pixelY;
        private int pixelX;
        private Point pixelYX;
        private static Color currentColour;
        private static Color storedColour;

        public PixelDetectionForm()
        {
            InitializeComponent();
        }

        static Color GetPixel(Point position)
        {
            using (var bitmap = new Bitmap(1, 1))
            {
                using (var graphics = Graphics.FromImage(bitmap))
                {
                    graphics.CopyFromScreen(position, new Point(0, 0), new Size(1, 1));
                }
                return bitmap.GetPixel(0, 0);
            }
        }

        private void PixelDetectionForm_KeyDown(object sender, KeyEventArgs e)
        {
            // Get Cursor Pixel Position
            if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F2)
            {
                pixelY = Cursor.Position.Y;
                pixelX = Cursor.Position.X;
                pixelYX = Cursor.Position;
                textBoxYPos.Text = pixelY.ToString();
                textBoxXPos.Text = pixelX.ToString();
                e.Handled = true;
            }
            // Get Cursor Pixel Colour
            if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F3)
            {
                storedColour = GetPixel(Cursor.Position);
                textBoxColour.Text = storedColour.ToString().Remove(0, 14).TrimEnd(']');
                panelColourDisplay.BackColor = storedColour;
                e.Handled = true;
            }
        }

        // Not working, need help with this
        private async void buttonStart_Click(object sender, EventArgs e)
        {
            while (true)
            {
                GetPixel(pixelYX);

                // Should get position of 'pixelY' and 'pixelX'
                panelColourDisplay2.BackColor = GetPixel(Cursor.Position);

                if (pixelYX == storedColour)
                {
                    MousePress();
                }
                // Need this to prevent not responding
                await Task.Delay(3);
            }
        }

        private void MousePress()
        {
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        }

        private void PixelDetectionForm_Click(object sender, EventArgs e)
        {
            ActiveControl = null;
        }

        private void PixelDetectionForm_Activated(object sender, EventArgs e)
        {
            ActiveControl = null;
        }
    }
}

Thanks

Fadexz
  • 13
  • 4
  • _...then click the mouse if it's the same..._ Click the mouse to do what if I may ask? Maybe someone would suggest a better approach if you could elaborate what you are trying to achieve. –  Feb 09 '20 at 09:46
  • Hey, well I would just like to for example detect a colour on-screen so it's purpose would be to click if it is on the screen. I was going to change out the click for another purpose but that will do as I want to do a similar thing. So to be more clear, I would like to run a loop searching for that colour that is chosen using the mouse position or typed iinto a textbox (which I have done), then click when that exact pixel matches the colour I am looking for. I could let you know it's exact purpose if you want but I don't think it would be meaningful as that's mainly what I want it to do. – Fadexz Feb 09 '20 at 15:07

1 Answers1

0

Well, an alternative to the while..loop is using a Timer to achieve that.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Pixel_detection_test_3
{
    public partial class PixelDetectionForm : Form
    {
        private readonly Timer Tmr;

        private Point lastPoint;
        //Assign this from your input code.
        private Color targetColor;

        public PixelDetectionForm()
        {
            Tmr = new Timer { Interval = 50 };
            Tmr.Tick += (s, e) => FindMatches(Cursor.Position);
        }
        //...

In the timer's Tick event, the FindMatches(..) method is called to check the current Cursor.Position and add the distinct matches into a ListBox. You can replace the last part with what you really need to do when you find a match. Like calling the MousePress() method in your code:

        //...
        private void FindMatches(Point p)
        {
            //To avoid the redundant calls..
            if (p.Equals(lastPoint)) return;

            lastPoint = p;

            using (var b = new Bitmap(1, 1))
            using (var g = Graphics.FromImage(b))
            {
                g.CopyFromScreen(p, Point.Empty, b.Size);

                var c = b.GetPixel(0, 0);

                if (c.ToArgb().Equals(targetColor.ToArgb()) &&
                    !listBox1.Items.Cast<Point>().Contains(p))
                {
                    listBox1.Items.Add(p);
                    listBox1.SelectedItem = p;
                }
            }
        }

        private void PixelDetectionForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Tmr.Dispose();
        }
    }
}

Start and stop the timer in the click events of the Start and Stop buttons.

Here's a demo:

SOQ60128615

Another alternative is to use the Global Mouse and Keyboard Hooks. Check this, this, and this for more details.


Edit 2/11/2020

If you just want to check whether a given color at a given point exists in a given image, then you can do:

private void buttonStart_Click(object sender, EventArgs e)
{
    var targetColor = ...; //target color.
    var targetPoint = ...; //target point.
    var sz = Screen.PrimaryScreen.Bounds.Size;
    using (var b = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppArgb))
    using (var g = Graphics.FromImage(b))
    {
        g.CopyFromScreen(Point.Empty, Point.Empty, b.Size, CopyPixelOperation.SourceCopy);

        var bmpData = b.LockBits(new Rectangle(Point.Empty, sz), ImageLockMode.ReadOnly, b.PixelFormat);
        var pixBuff = new byte[bmpData.Stride * bmpData.Height];

        Marshal.Copy(bmpData.Scan0, pixBuff, 0, pixBuff.Length);

        b.UnlockBits(bmpData);

        for (var y = 0; y < b.Height; y++)
        for(var x = 0; x < b.Width; x++)
        {
            var pos = (y * bmpData.Stride) + (x * 4);
            var blue = pixBuff[pos];
            var green = pixBuff[pos + 1];
            var red = pixBuff[pos + 2];
            var alpha = pixBuff[pos + 3];

            if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()) &&
                new Point(x, y).Equals(targetPoint))
            {
                //execute you code here..
                MessageBox.Show("The given color exists at the given point.");
                return;
            }
        }
    }
    MessageBox.Show("The given color doesn't exist at the given point.");
}

If you want to get a list of all the positions of a given color, then create a new List<Point>() and change the check condition to:

//...
var points = new List<Point>();
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()))
{
    points.Add(new Point(x, y));
}
  • Wow thank you, that is better than I could have done. It doesn't do exactly what I intended but it could still be useful for other things. My intention was for it to be able to grab a colour (which it can do), and or then pick a pixel location. Once it has a pixel and a colour to look for then it would only check that one pixel completely ignoring the mouse location as the computer won't be being touched while checking for a match just waiting for it. So basically just select a pixel and check until it's the same again and click (I will change out the click for some other code using an API). – Fadexz Feb 10 '20 at 14:01
  • I will definitely have a look at those global keyboard hooks. The part i'm having trouble with is once i've picked the pixel position (and placed it in textboxes for X and Y location) how can I get those into a point in place of the cursor.position so I can just search for that one pixel. Also, it's not necessary but being able to check for multiple different pixels and colours for those pixels would be amazing but I would be just happy if it was just one working. Thanks a lot! – Fadexz Feb 10 '20 at 14:11