Как мне получить координаты указанного цвета внутри эллипса? - PullRequest
2 голосов
/ 17 июня 2020

Небольшой отказ от ответственности: Я впервые возился с графикой в ​​формах, поэтому я не очень знаком с концепциями здесь

Хорошо, поэтому я пытался создать приложение который отслеживает положение курсора на всем экране и нарисовал aws эллипс вокруг него. Код, который я заимствовал, был взят из этого вопроса (я изменил положение X и Y эллипса, чтобы автоматически настраиваться вокруг курсора независимо от его размера), все работает отлично до этого момента. Вот код на данный момент:

        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();
        }

Итак, теперь я хочу, чтобы приложение проверяло, присутствует ли заранее назначенный цвет в области эллипса, и если да, то получает положение ближайшего пикселя к курсор этого цвета. Я искал везде и не нашел способа сделать это.

Я понимаю, что лог c за ним должен был бы получить все пиксели внутри эллипса, проверить, существует ли цвет, и найти один пиксель с этим цветом, ближайший к курсору, но я не был в состоянии реализовать это.

Любая помощь будет очень признательна.

1 Ответ

2 голосов
/ 17 июня 2020

Это упрощенный метод (он не требует PInvoking, отслеживания / подключения мыши или других операций низкого уровня ).
Он может работать достаточно хорошо, если вам не нужно слишком много контроля о том, что происходит за вашим окном, вы не хотите записывать анимированные изображения, просто сделайте то, что указано в описании вопроса: захватите цвета внешних Windows / элементов рабочего стола, которые в настоящее время находятся под указателем мыши.

Здесь используется трюк: формы BackColor и TransparencyKey устанавливаются в цвет blue-i sh (Color.Navy). Это позволяет иметь прозрачную, но solid Форму.
На практике, MouseMove события возникают, даже если Форма полностью прозрачна и может быть нажата, хотя .

Другой квази-трюк - это двойная буферизация формы с использованием стандартного свойства DoubleBuffer , а не OptimizedDoubleBuffer, которое можно включить, вызвав метод SetStyle().

Свойство ResizeRedraw установлено в значение true, поэтому форма Redr aws сама себя при изменении размера.

При этой настройке, чтобы получить цвет под положением курсора, вам просто нужно сделать снимок текущего экрана в один пиксель, используя растровое изображение размером (1, 1) (нам нужен только этот один пиксель) и используйте (не очень быстрый, но функциональный) метод GetPixel() для чтения цвета из растрового изображения.

При нажатии правой кнопки мыши цвет под курсором сохраняется в List<Color> (который доступен с помощью общедоступного / доступного только для чтения свойства SavedColors), а затем отрисовывается в PictureBox, используемом как канва для этого Палитра .

Чтобы построить этот пример:

  • Создайте новую форму
  • Добавьте PictureBox (здесь с именем picColor) и закрепите его В правом верхнем углу. Этот элемент управления используется для отображения текущего цвета под курсором при перемещении указателя мыши.
  • Добавьте второй PictureBox (здесь с именем picPalette) под предыдущим и закрепите его сверху-справа-снизу. Это используется для рисования текущей палитры сохраненных цветов.
    В конструкторе используйте панель событий, чтобы подписаться на событие Paint с помощью метода обработчика, который вы можете найти в этом коде (т. Е. Не добавлять другой).

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;
                }
            }
        }
    }
}

Вот как это работает:

Transparent Form GetPixel

...