我正在尝试开发一个应用程序,该应用程序加载图像并根据用户使用鼠标定义的矩形对其进行裁剪。 我面临着与这篇post中提到的同样的问题。答案建议考虑图像 DPI 和屏幕 DPI 之间的差异重新计算裁剪矩形的坐标。
xaml代码是:
<Grid Name="GridImage">
<Image Name="LoadedImage" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="HighQuality">
</Image>
<Canvas x:Name="ImageCanvas" ClipToBounds="True"
IsHitTestVisible="True" MouseLeftButtonDown="ImageCanvas_MouseLeftButtonDown" MouseMove="ImageCanvas_MouseMove"
MouseUp="ImageCanvas_MouseUp"
Background="Transparent">
<Rectangle x:Name="SelectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed" />
</Canvas>
</Grid>
Canvas 事件的定义为:
private void ImageCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
isDrawing = true;
startPoint = e.GetPosition(ImageCanvas);
Canvas.SetLeft(SelectionRectangle, startPoint.X);
Canvas.SetTop(SelectionRectangle, startPoint.Y);
SelectionRectangle.Width = 0;
SelectionRectangle.Height = 0;
SelectionRectangle.Visibility = Visibility.Visible;
}
}
private void ImageCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (isDrawing)
{
Point currentPoint = e.GetPosition(ImageCanvas);
double width = Math.Abs(currentPoint.X - startPoint.X);
double height = Math.Abs(currentPoint.Y - startPoint.Y);
width = Math.Min(width, LoadedImage.ActualWidth - Canvas.GetLeft(SelectionRectangle));
height = Math.Min(height, LoadedImage.ActualHeight - Canvas.GetTop(SelectionRectangle));
SelectionRectangle.Width = width;
SelectionRectangle.Height = height;
Canvas.SetLeft(SelectionRectangle, Math.Min(startPoint.X, currentPoint.X));
Canvas.SetTop(SelectionRectangle, Math.Min(startPoint.Y, currentPoint.Y));
}
}
private void ImageCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
Debug.WriteLine("ImageCanvas_MouseUp event triggered");
isDrawing = false;
CropImage();
}
摘自帖子的代码修改如下:
private void Crop()
{
var imagePosition = LoadedImage.TransformToAncestor(GridImage).Transform(new Point(0, 0));
double imageDpiX = 96.0;
double imageDpiY = 96.0;
Rect rect1 = new Rect(Math.Max(Canvas.GetLeft(SelectionRectangle) - imagePosition.X, 0), Math.Max(Canvas.GetTop(SelectionRectangle) - imagePosition.Y, 0), SelectionRectangle.Width, SelectionRectangle.Height);
if (LoadedImage.Source is BitmapSource bitmapSource)
{
imageDpiX = bitmapSource.DpiX;
imageDpiY = bitmapSource.DpiY;
}
//Set the screen DPI
double screenDpiX = 96.0;
double screenDpiY = 96.0;
// Calculate the DPI ratios
double ratioX = screenDpiX / imageDpiX;
double ratioY = screenDpiY / imageDpiY;
Int32Rect rcFrom = new Int32Rect
{
X = (int)(rect1.X * ratioX),
Y = (int)(rect1.Y * ratioY),
Width = (int)(rect1.Width * ratioX),
Height = (int)(rect1.Height * ratioY)
};
try
{
BitmapSource bs = new CroppedBitmap((BitmapSource)LoadedImage.Source, rcFrom);
LoadedImage.Source = bs;
SetImageStretch(LoadedImage);
SetElementVisibility(Visibility.Hidden, Visibility.Visible, SelectionRectangle);
}
}
裁剪矩形下方的图像部分仍然没有反映在裁剪图像中。裁剪后的图像显示为原始图像的放大部分。我错过了什么?
编辑 根据 JonasH 的评论,以下几行已添加到
ImageCanvas_MouseUp
事件中:
Point mousePosition = e.GetPosition(LoadedImage);
double mouseX = mousePosition.X;
double mouseY = mousePosition.Y;
CropImage(mouseX, mouseY);
CropImage 方法中的 rcFrom 计算如下:
var width = x - Canvas.GetLeft(SelectionRectangle);
var height = y - Canvas.GetTop(SelectionRectangle);
Int32Rect rcFrom = new Int32Rect
{
X = (int)x,
Y = (int)y,
Width = (int)width,
Height = (int)height
};
这里x和y对应(mouseX, mouseY)
修改后,不会出现裁剪后的图像。
这是 WPF。因此,我们不必处理 dpi,因为 WPF 渲染引擎以设备无关方式 (DIP) 解释像素。只有当我们进行一些真正的低级渲染时,Dpi 才变得相关。
您可以极大地简化您的代码。裁剪函数所需的所有参数都是裁剪区域的边界矩形,当然还有图像源:
private void ImageCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
isDrawing = false;
Geometry croppedAreaGeometry = this.SelectionRectangle.RenderedGeometry;
Rect croppedAreaBounds = croppedAreaGeometry.Bounds;
// Apply the canvas location to the bounding rectangle
croppedAreaBounds.Offset(Canvas.GetLeft(this.SelectionRectangle), Canvas.GetTop(this.SelectionRectangle));
CropImage(this.LoadedImage, croppedAreaBounds);
SaveImageToPng(this.LoadedImage.Source as BitmapSource, "");
}
private void CropImage(Image image, Rect cropBounds)
{
var int32CropBounds = new Int32Rect((int)cropBounds.Left, (int)cropBounds.Top, (int)cropBounds.X, (int)cropBounds.Y);
var croppedImageSource = new CroppedBitmap(this.Image.Source as BitmapSource, int32CropBounds);
image.Source = croppedImageSource;
}
private void SaveImageToPng(BitmapSource croppedImageSource, string destinationPath)
{
var imageFrame = BitmapFrame.Create(croppedImageSource);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(imageFrame);
using FileStream stream = File.Create(destinationPath);
encoder.Save(stream);
}