Как преобразовать координаты мыши в пиксельные координаты преобразованного растрового изображения?

0

Вопрос

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

Мой Windowдизайн выглядит так:

    <DockPanel>
        <Label DockPanel.Dock="Bottom" Name="TheLabel" />
        <Image DockPanel.Dock="Top" Name="TheImage" Stretch="Uniform" RenderOptions.BitmapScalingMode="NearestNeighbor" MouseMove="TheImage_MouseMove" />
    </DockPanel>

и кодовая подсказка выглядит так:

        public MainWindow()
        {
            InitializeComponent();

            const int WIDTH = 4;
            const int HEIGHT = 3;
            byte[] pixels = new byte[WIDTH * HEIGHT * 3];
            pixels[0] = Colors.Red.B;
            pixels[1] = Colors.Red.G;
            pixels[2] = Colors.Red.R;
            pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 0] = Colors.Blue.B;
            pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 1] = Colors.Blue.G;
            pixels[(WIDTH * (HEIGHT - 1) + (WIDTH - 1)) * 3 + 2] = Colors.Blue.R;
            BitmapSource bs = BitmapSource.Create(WIDTH, HEIGHT, 96.0, 96.0, PixelFormats.Bgr24, null, pixels, WIDTH * 3);
            TheImage.Source = bs;
        }

        private void TheImage_MouseMove(object sender, MouseEventArgs e)
        {
            Point p = e.GetPosition(TheImage);
            if (TheImage.Source is TransformedBitmap tb)
                TheLabel.Content = tb.Transform.Inverse.Transform(new Point(p.X * tb.PixelWidth / TheImage.ActualWidth, p.Y * tb.PixelHeight / TheImage.ActualHeight)).ToString();
            else if (TheImage.Source is BitmapSource bs)
                TheLabel.Content = new Point(p.X * bs.PixelWidth / TheImage.ActualWidth, p.Y * bs.PixelHeight / TheImage.ActualHeight).ToString();
        }

При наведении курсора в правом нижнем углу (который я покрасил синим для удобства отслеживания) нетрансформированного изображения вы правильно видите координаты (~4, ~3), которые являются размерами изображения.

enter image description here

Однако, как только вы примените преобразование, например, измените TheImage.Source = bs; Для TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));, зависание над синим вместо этого дает (~4, ~0).

enter image description here

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

.net bitmapsource c# image
2021-11-23 06:19:28
1

Лучший ответ

1

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

Поскольку этот сдвиг не применяется к перемещению точки, вы должны компенсировать его, например, так:

var p = e.GetPosition(TheImage);

if (TheImage.Source is BitmapSource bs)
{
    p = new Point(
        p.X * bs.PixelWidth / TheImage.ActualWidth,
        p.Y * bs.PixelHeight / TheImage.ActualHeight);

    if (TheImage.Source is TransformedBitmap tb)
    {
        var w = tb.Source.PixelWidth;
        var h = tb.Source.PixelHeight;

        p = tb.Transform.Inverse.Transform(p);

        p = new Point((p.X + w) % w, (p.Y + h) % h);
    }

    TheLabel.Content = $"x: {(int)p.X}, y: {(int)p.Y}";
}

Однако приведенный выше код будет работать неправильно, если для центра преобразования задано значение, отличное от (0,0).

Чтобы заставить его работать для преобразований с произвольной центральной точкой, необходимо очистить значения смещения в матрице преобразования:

var p = e.GetPosition(TheImage);

if (TheImage.Source is BitmapSource bs)
{
    p = new Point(
        p.X * bs.PixelWidth / TheImage.ActualWidth,
        p.Y * bs.PixelHeight / TheImage.ActualHeight);

    if (TheImage.Source is TransformedBitmap tb)
    {
        var w = tb.Source.PixelWidth;
        var h = tb.Source.PixelHeight;
        var t = tb.Transform.Value;
        t.Invert();
        t.OffsetX = 0d;
        t.OffsetY = 0d;

        p = t.Transform(p);

        p = new Point((p.X + w) % w, (p.Y + h) % h);
    }

    TheLabel.Content = $"x: {(int)p.X}, y: {(int)p.Y}";
}
2021-11-23 10:54:45

Спасибо! Если кто-то встраивает Image в Grid который имеет фон и перемещает MouseMove событие для Grid, координаты пикселей больше не являются точными, когда они находятся за пределами изображения, так как они дают ответы только от 0..ширина и 0..высота. Есть идеи, как это исправить?
Craig W

Есть несколько способов. Проще всего прикрепить обработчик событий к другому вложенному родительскому элементу, который имеет те же размеры, что и Изображение. В противном случае существуют такие методы, как TransformToDescendant или аналогичные.
Clemens

Не уверен, что я понимаю. Я могу без проблем перевести точку с родительской сетки на изображение. Но, например, когда пользователь наводит курсор справа от повернутого изображения, я бы хотел, чтобы координата y была отрицательной, чтобы отразить, что они находятся выше границ изображения в исходной ориентации. Должен ли я отредактировать свой вопрос или начать новый?
Craig W

Лучше напиши что-нибудь новое.
Clemens

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................