-->

Crop a diagonal area from an image in WPF

2020-03-30 05:48发布

问题:

I want to crop from an image using user-drawn rectangles on a canvas. The rectangles can be moved, re-sized, and rotated.

When the user selects "Get Cropped Image", the area inside the rectangle should be saved in a second image location on the page, which I can do perfectly well, so long as the rectangle is not rotated. (Straight-forward use of CroppedBitmap.) However, when the rectangle is at an angle I do not know how to perform the crop.

This is what I want to do (forgive my poor MS Paint skills):

My questions are:

1) How do I correctly track or calculate the points of the rectangle?

and,

2) Once I have the points, how do I crop the rotated rectangle?

EDIT: Thanks to user Rotem, I believe that I have the answer to the second question. Using code modified from the following answers: Answer 1, Answer 2, I am seeing good results. Unfortunately, I am still unable to track the correct location points for the rectangle, so I cannot fully test this as of yet.

public static Bitmap CropRotatedRect(Bitmap source, System.Drawing.Rectangle rect, float angle, bool HighQuality)
 {
    Bitmap result = new Bitmap((int)rect.Width, (int)rect.Height);
    using (Graphics g = Graphics.FromImage(result))
     {
        g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
        using (Matrix mat = new Matrix())
         {
            mat.Translate(-rect.Location.X, -rect.Location.Y);
            mat.RotateAt(-(angle), rect.Location);
            g.Transform = mat;
            g.DrawImage(source, new System.Drawing.Point(0, 0));
         }
     }
    return result;
 }

EDIT: The answer to the first point is much easier than I had originally thought. You can always get the top-left corner of the rectangle by calling—

double top = Canvas.GetTop(rect);
double left = Canvas.GetLeft(rect);

You can then calculate the rest of the points using the width and the height—

Point topLeft = new Point(left, top);
Point topRight = new Point(left + rect.Width, top);
Point bottomLeft = new Point(left, top + rect.Height);
Point bottomRight = new Point(left + rect.Width, top + rect.Height);
Point centerPoint = new Point(left + (rect.Width / 2), top + (rect.Height / 2));

If your rectangle is rotated, then you have to translate these points to determine where they truly lie on the canvas—

public Point TranslatePoint(Point center, Point p, double angle)
 { 
    // get the point relative to (0, 0) by subtracting the center of the rotated shape.
    Point relToOrig = new Point(p.X - center.X, p.Y - center.Y);
    double angleInRadians = angle * Math.PI / 180;
    double sinOfA = Math.Sin(angleInRadians);
    double cosOfA = Math.Cos(angleInRadians);
    Point translatedPoint = new Point(relToOrig.X * cosOfA - relToOrig.Y * sinOfA,
                                      relToOrig.X * sinOfA + relToOrig.Y * cosOfA);
    return new Point(translatedPoint.X + center.X, translatedPoint.Y + center.Y);
 }

Once you are able to translate the top-left corner, you can use Rotem's cropping method. You can also calculate the position of the rest of the rectangle, so you are able to determine if the rectangle is within the bounds of the image, if it is touching an edge, or any other thing that you might want to do in regards to the position.

回答1:

I discovered the answer to my own question(s), and made the appropriate edits along the way. Please see above for the answer.