Let's say I have a 1280x1024 BitmapSource in a WPF Image control. There is a 100x100 "active" area of this image that I want to be able to zoom in on with a button click. I want to zoom as much as possible while maintaining the aspect ratio and keeping all "active" pixels visible. This is what I have:
XAML:
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Zoom" Click="Button_Click" />
<Border DockPanel.Dock="Top" ClipToBounds="True">
<Image Name="TheImage" />
</Border>
</DockPanel>
Code:
private const int WIDTH = 1280;
private const int HEIGHT = 1024;
private const int MIN_X = 100;
private const int MAX_X = 200;
private const int MIN_Y = 100;
private const int MAX_Y = 200;
public MainWindow()
{
InitializeComponent();
byte[] image = new byte[WIDTH * HEIGHT];
for (int y = MIN_Y; y <= MAX_Y; y++)
for (int x = MIN_X; x <= MAX_X; x++)
image[y * WIDTH + x] = byte.MaxValue;
TheImage.Source = BitmapSource.Create(WIDTH, HEIGHT, 96.0, 96.0, PixelFormats.Gray8, null, image, WIDTH);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
double factor = 1.0 / Math.Max((MAX_X - MIN_X) / (double)WIDTH, (MAX_Y - MIN_Y) / (double)HEIGHT);
Matrix matrix = Matrix.Identity;
matrix.ScaleAt(factor, factor, (MIN_X + MAX_X) / 2.0 / WIDTH * TheImage.ActualWidth, (MIN_Y + MAX_Y) / 2.0 / HEIGHT * TheImage.ActualHeight);
TheImage.RenderTransform = new MatrixTransform(matrix);
}
Here is what it looks like before zoom:
It looks like the amount of zoom is correct, but I think the issue is that the center of the scaling should shift due to the scaling, but I'm not sure how to account for that upfront.
Figured it out. The simplest approach seems to be to first do a translation such that the center of the active area is in the center of the image, then do the scaling centered on that:
private void Button_Click(object sender, RoutedEventArgs e)
{
double factor = 1.0 / Math.Max((MAX_X - MIN_X) / (double)WIDTH, (MAX_Y - MIN_Y) / (double)HEIGHT);
Matrix matrix = Matrix.Identity;
matrix.Translate(0.5 * TheImage.ActualWidth - (MIN_X + MAX_X) / 2.0 / WIDTH * TheImage.ActualWidth, 0.5 * TheImage.ActualHeight - (MIN_Y + MAX_Y) / 2.0 / HEIGHT * TheImage.ActualHeight);
matrix.ScaleAt(factor, factor, 0.5 * TheImage.ActualWidth, 0.5 * TheImage.ActualHeight);
TheImage.RenderTransform = new MatrixTransform(matrix);
}