如何将鼠标坐标转换为 TransformedBitmap 的像素坐标?
How do I convert from mouse coordinates to pixel coordinates of a TransformedBitmap?
我的问题类似于 ,需要注意的是图像可能是 TransformedBitmap
,其中可以应用翻转和旋转,但仍然 return 像素坐标原始图像。
我的 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) 的坐标,这是图像尺寸。
但是,一旦您应用了转换,例如将 TheImage.Source = bs;
更改为 TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));
,将鼠标悬停在蓝色上方则会给出 (~4, ~0).
我认为可以查看变换的实际矩阵值,并确定在所有各种情况下如何调整点,但似乎应该有一种使用逆变换的更简单的解决方案。
当 Image 元素呈现 TransformedBitmap 时,实际上会忽略 Transform 的中心点,并将位图移动到适当的坐标范围内。
由于此偏移不适用于点的变换,因此您必须对其进行补偿,例如像这样:
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}";
}
我的问题类似于 TransformedBitmap
,其中可以应用翻转和旋转,但仍然 return 像素坐标原始图像。
我的 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) 的坐标,这是图像尺寸。
但是,一旦您应用了转换,例如将 TheImage.Source = bs;
更改为 TheImage.Source = new TransformedBitmap(bs, new RotateTransform(90.0));
,将鼠标悬停在蓝色上方则会给出 (~4, ~0).
我认为可以查看变换的实际矩阵值,并确定在所有各种情况下如何调整点,但似乎应该有一种使用逆变换的更简单的解决方案。
当 Image 元素呈现 TransformedBitmap 时,实际上会忽略 Transform 的中心点,并将位图移动到适当的坐标范围内。
由于此偏移不适用于点的变换,因此您必须对其进行补偿,例如像这样:
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}";
}