Como faço para converter coordenadas do mouse para coordenadas de pixel de uma TransformedBitmap?

0

Pergunta

A minha pergunta é semelhante a Como obter a posição correta do pixel de coordenadas do mouse?, com a ressalva de que a imagem é potencialmente um TransformedBitmap, onde vira e rotações poderia ser aplicado, e ainda retornar as coordenadas de pixel da imagem original.

Meu Window's design parecido com este:

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

e o code-behind parecido com este:

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

Ao passar o mouse no canto inferior direito (que eu coloridas de azul para fácil acompanhamento) de não-transformada imagem, você ver corretamente as coordenadas de (~4, ~3), que são as dimensões da imagem.

enter image description here

No entanto, depois de aplicar uma transformação, por exemplo, mudar TheImage.Source = bs; para TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));, passar o mouse sobre o azul em vez disso dá (~4, ~0).

enter image description here

Eu acho que poderíamos olhar para o real matriz de valores de transformação, e determinar como ajustar o ponto em todos os casos, mas parece que deve haver uma solução mais fácil usando o inverso da transformação.

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

Melhor resposta

1

Quando um elemento de Imagem compõe um TransformedBitmap, o ponto central da Transformação é efetivamente ignorado e o mapa de bits é deslocado para uma adequada coordenar gama.

Desde que essa mudança não é aplicado para o transfomation de um Ponto, você tem que compensar, por exemplo, como este:

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

O código acima não funciona corretamente quando a Transformar o centro é definido como um valor diferente de (0,0).

A fim de fazer o trabalho para Transforma arbitrários ponto central, você tem que limpar os valores de deslocamento na matriz de transformação:

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

Obrigado! Se um incorpora o Image em um Grid que tem um plano de fundo e move o MouseMove evento para o Grid, as coordenadas de pixel não são mais precisos quando fora dos limites da imagem, pois só dá respostas a partir de 0..largura e 0..altura. Alguma idéia de como consertar isso?
Craig W

Há várias maneiras. Mais simples, anexar o manipulador de eventos para o outro aninhados principal elemento que tem as mesmas dimensões da Imagem. Caso contrário, existem métodos como TransformToDescendant ou similar.
Clemens

Não sei o que seguir. Eu posso traduzir o ponto a partir do grid pai para a imagem, não há problema. Mas, por exemplo, quando o usuário passa o cursor para a direita da imagem girada, eu gostaria que a coordenada y para ser negativo para refletir que eles estão acima dos limites da imagem na orientação original. Devo editar o meu questão ou inicie uma nova?
Craig W

Melhor escrever um novo.
Clemens

Em outros idiomas

Esta página está em outros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................