3

I'd like to be able to determine the backgroundcolor of a winform at a certain point on the screen, so I can take some action depending on that particular color. Sadly the System.Drawing library doesn't seem to specify a method that enables me to do so.

How should I then determine the color at a certain point?

Martin Liversage
  • 96,855
  • 20
  • 193
  • 238
fuaaark
  • 521
  • 8
  • 21
  • Possible duplicate of http://stackoverflow.com/questions/1483928/how-to-read-the-color-of-a-screen-pixel – Marco Apr 12 '12 at 09:17

3 Answers3

5

Use that :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace ColorUnderCursor
{
    class CursorColor
    {
        [DllImport("gdi32")]
        public static extern uint GetPixel(IntPtr hDC, int XPos, int YPos);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool GetCursorPos(out POINT pt);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);

        /// <summary> 
        /// Gets the System.Drawing.Color from under the mouse cursor. 
        /// </summary> 
        /// <returns>The color value.</returns> 
        public static Color Get()
        {
            IntPtr dc = GetWindowDC(IntPtr.Zero);

            POINT p;
            GetCursorPos(out p);

            long color = GetPixel(dc, p.X, p.Y);
            Color cc = Color.FromArgb((int)color);
            return Color.FromArgb(cc.B, cc.G, cc.R);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
        public POINT(int x, int y)
        {
            X = x;
            Y = y;
        } 
    }
}
Amen Ayach
  • 4,048
  • 1
  • 20
  • 23
4

Assuming you want to stay away from the Win32 API, you can use Graphics.CopyFromScreen() to draw the content of your screen to a Bitmap object. From there, you just need to use Bitmap.GetPixel() to retrieve a Color object with the right color.

Here's some code you can use to retrieve the complete desktop (works with multiple monitors):

public Image GetScreenshot()
{
    int screenWidth = Convert.ToInt32(System.Windows.SystemParameters.VirtualScreenWidth);
    int screenHeight = Convert.ToInt32(SystemParameters.VirtualScreenHeight);
    int screenLeft = Convert.ToInt32(SystemParameters.VirtualScreenLeft);
    int screenTop = Convert.ToInt32(SystemParameters.VirtualScreenTop);

    Image imgScreen = new Bitmap(screenWidth, screenHeight);
    using (Bitmap bmp = new Bitmap(screenWidth, screenHeight, PixelFormat.Format32bppArgb))
    using (Graphics g = Graphics.FromImage(bmp))
    using (Graphics gr = Graphics.FromImage(imgScreen))
    {
        g.CopyFromScreen(screenLeft, screenTop, 0, 0, new Size(screenWidth, screenHeight));
        gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
        gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
        gr.DrawImage(bmp, new Rectangle(0, 0, screenWidth, screenHeight));
    }

    return imgScreen;
}
Lâm Tran Duy
  • 795
  • 3
  • 7
2

This is a slightly different take on the other answers already provided as you've only stated that you want to determine the "color" but have not explicitly stated that you wanted the RGB values.

This distinction is necessary because there may be variations to a colour that are imperceptible to the human eye. For example let's assume that you are interested in detecting the colour "blue". The values (5, 5, 240) and (10, 10, 255) are only very subtly different from (0, 0, 255). In fact, the difference is so subtle that you'd need to compare the colour swatches side by side to tell them apart and even then it depends on the quality of your monitor. They're subtly different on my desktop monitor but are indistinguishable on my laptop. To cut the long story short, RGB values are a bad way of determining colour.

There are numerous methods to aid in calculating the difference between colours, the most common being the Delta-E method. However, this may be over kill if you just want a quick and dirty method. Convert RGB space into HSV and use the difference in hue to determine the colour. Modifying Amen's example, you get the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

class CursorHue
    {
        [DllImport("gdi32")]
        public static extern uint GetPixel(IntPtr hDC, int XPos, int YPos);

        public static float GetHue(Point p)
        {           
            long color = GetPixel(dc, p.X, p.Y);
            Color cc = Color.FromArgb((int)color);
            return cc.GetHue();
        }
    }

Pure blue has a hue value of 240. The previous examples (5, 5, 240) and (10, 10, 255) both have a hue of 240. This is good news as it means that hue is a measure that is fairly tolerant to RGB differences, and the difference is also quick to calculate (i.e. just take the absolute difference in the hue values). At this point, we should also introduce another parameter which governs acceptable tolerance to colour difference. A 15 degree hue tolerance will make the system fairly robust.

Here's sample code that compares two colours to determine whether they are acceptably similar

public static bool AreSimilar(Color c1, Color c2, float tolerance = 15f)
{
  return Math.Abs(c1.GetHue() - c2.GetHue() <= tolerance;
}

For a more in-depth explanation of why this works, see this Wikipedia article on HSV colour spaces.

CadentOrange
  • 2,948
  • 1
  • 28
  • 51