How do I get the coordinates of a specified color inside an Ellipse?

怎甘沉沦 提交于 2020-06-27 16:44:29

问题


Small disclaimer: This is my first time messing with Graphics in Forms, therefore I am not so familiar with the concepts here

Alright, so I have been trying to make an application that tracks the cursor's position in the entire screen and draws an Ellipse around it. The code I borrowed was from this question (I changed the X and Y position of the Ellipse in order to auto-adjust itself around the cursor regardless of its size) everything works perfectly up to this point. Here is the code so far:

        public static float width;
        public static float height;

        public Main(float w, float h)
        {
            InitializeComponent();
            this.DoubleBuffered = true;
            width = w;
            height = h;
            BackColor = Color.White;
            FormBorderStyle = FormBorderStyle.None;
            Bounds = Screen.PrimaryScreen.Bounds;
            TopMost = true;
            TransparencyKey = BackColor;
            this.ShowInTaskbar = false;
            timer1.Tick += timer1_Tick;
        }

        Timer timer1 = new Timer() { Interval = 1, Enabled = true };

        protected override void OnPaint(PaintEventArgs e)
        {
            DrawTest(e.Graphics);
            base.OnPaint(e);
        }

        private void DrawTest(Graphics g)
        {
            var p = PointToClient(Cursor.Position);
            g.DrawEllipse(Pens.DeepSkyBlue, p.X - (width / 2), p.Y - (height / 2), width, height);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Invalidate();
        }

So now I want the application to check whether a preassigned color is present within the area of the Ellipse, and if so, get the position of the nearest pixel to the cursor that has this color. I have searched everywhere and haven't found any method of doing it.

I get that the logic behind it would be to get all the pixels within the Ellipse, check if the color exists and find the one pixel with this color nearest to the cursor but I haven't been able to implement it.

Any help would be very appreciated.


回答1:


This is a simplified method (it doesn't require PInvoking, mouse tracking/hooking or other low level operations).
It can work well enough if you don't need too much control on what happens behind your Window, you don't want to record animated Images, just do what's in the question's description: capture colors of external Windows / Desktop elements currently under the mouse pointer.

A trick is used here: the Form's BackColor and its TransparencyKey are set to a blue-ish color (Color.Navy). This allows to have a transparent but solid Form.
In practice, MouseMove events are raised even if the Form is completely transparent an can be clicked-though.

Another quasi-trick, is to double-buffer the Form, using the standard DoubleBuffer property, not the OptimizedDoubleBuffer which can be enabled calling the SetStyle() method.

The ResizeRedraw property is set to true, so the Form redraws itself if/when resized.

With this setup, to get the Color under the Cursor position, you just have to take an one-Pixel snapshot of the current Screen, using a Bitmap sized as (1, 1) (we need just that one Pixel) and use the (not very fast but functional) GetPixel() method to read the Color from the Bitmap.

When the Mouse right button is clicked, the Color under the Cursor is saved in a List<Color> (which is accessible using the public/read-only SavedColors property) and then drawn in a PictureBox used as canvas for this Palette.

To build this example:

  • Create a new Form
  • Add a PictureBox (here, named picColor) and anchor it Top-Right. This Control is used to show the current Color under the Cursor when the Mouse pointer moves.
  • Add a second PictureBox (here, named picPalette) under the previous one and anchor it Top-Right-Bottom. This is used to draw the current palette of saved colors.
    In the Designer, use the Events Panel to subscribe to the Paint event using the handler method you can find in this code (i.e., don't add another).

using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public partial class frmColorPicker : Form
{
    Color m_CurrentColor = Color.Empty;
    List<Color> m_SavedColors = new List<Color>();

    public frmColorPicker()
    {
        InitializeComponent();
        this.ResizeRedraw = true;
        this.DoubleBuffered = true;

        this.TopMost = true;
        this.BackColor = Color.Navy;
        this.TransparencyKey = Color.Navy;
    }

    public Color CursorEllipseColor { get; set; } = Color.Orange;

    public List<Color> SavedColors => m_SavedColors;

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        GetColorUnderCursor();
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        var rect = GetCursorEllipse();
        using (var pen = new Pen(CursorEllipseColor, 2)) {
            e.Graphics.DrawEllipse(pen, rect);
        }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
        base.OnMouseDown(e);
        if (e.Button == MouseButtons.Right) {
            m_SavedColors.Add(m_CurrentColor);
            picPalette.Invalidate();
        }
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        base.OnMouseMove(e);
        this.Invalidate();
    }

    private Rectangle GetCursorEllipse()
    {
        var cursorEllipse = new Rectangle(PointToClient(Cursor.Position), Cursor.Size);
        cursorEllipse.Offset(-cursorEllipse.Width / 2, -cursorEllipse.Height / 2);
        return cursorEllipse;
    }

    private void GetColorUnderCursor()
    {
        using (var bmp = new Bitmap(1, 1))
        using (var g = Graphics.FromImage(bmp)) {
            g.CopyFromScreen(Cursor.Position, Point.Empty, new Size(1, 1));
            m_CurrentColor = bmp.GetPixel(0, 0);
            picColor.BackColor = m_CurrentColor;
        }
    }

    private void picPalette_Paint(object sender, PaintEventArgs e)
    {
        int rectsCount = 0;
        int rectsLines = 0;
        int rectsPerLine = picPalette.Width / 20;

        foreach (var color in m_SavedColors) {
            using (var brush = new SolidBrush(color)) {
                var rect = new Rectangle(new Point(rectsCount * 20, rectsLines * 20), new Size(20, 20));
                e.Graphics.FillRectangle(brush, rect);
                e.Graphics.DrawRectangle(Pens.DarkGray, rect);
                rectsCount += 1;
                if (rectsCount == rectsPerLine) {
                    rectsCount = 0;
                    rectsLines += 1;
                }
            }
        }
    }
}

This is how it works:



来源:https://stackoverflow.com/questions/62428888/how-do-i-get-the-coordinates-of-a-specified-color-inside-an-ellipse

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!