WPF 图像裁剪应用程序中的差异

问题描述 投票:0回答:1

我正在尝试开发一个应用程序,该应用程序加载图像并根据用户使用鼠标定义的矩形对其进行裁剪。 我面临着与这篇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)

修改后,不会出现裁剪后的图像。

c# wpf
1个回答
0
投票

这是 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);
}
© www.soinside.com 2019 - 2024. All rights reserved.