Cropping Image is not cropping correctly

Viewed 145

I have followed a YouTube Tutorial on how to create a cropping image application.

Goal:

My Goal is simply load a screenshot image into PictureBox1.

Then mark an area with a Red Rectangle to crop and display into PictureBox2.

Here is the code:

public partial class Form18 : Form
{
    Rectangle Rect;

    Point LocationXY;
    Point LocationX1Y1;

    bool IsMouseDown = false;




    public Form18()
    {
        InitializeComponent();
    }


    private void Form18_Load(object sender, EventArgs e)
    {

    }

    private void button1_Click(object sender, EventArgs e)
    {
        //Clipboard image
        pictureBox1.Image = Clipboard.GetImage();
    }


    private void picturebox1_MouseDown(object sender, MouseEventArgs e)
    {
        IsMouseDown = true;
        LocationXY = e.Location;
    }

    private void picturebox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (IsMouseDown == true)
        {
            LocationX1Y1 = e.Location;

            Refresh();
        }
    }

    private void picturebox1_MouseUp(object sender, MouseEventArgs e)
    {
        if (IsMouseDown == true)
        {
            LocationX1Y1 = e.Location;

            IsMouseDown = false;

            if (Rect != null)
            {
                Bitmap bit = new Bitmap(pictureBox1.Image, pictureBox1.Width, pictureBox1.Height);

                Bitmap cropImg = new Bitmap(Rect.Width, Rect.Height);

                Graphics g = Graphics.FromImage(cropImg);
                g.DrawImage(bit, 0, 0, Rect, GraphicsUnit.Pixel);
                pictureBox2.Image = cropImg;
            }
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (Rect != null)
        {
            e.Graphics.DrawRectangle(Pens.Red, GetRect());
        }
    }
    private Rectangle GetRect()
    {
        Rect = new Rectangle();

        Rect.X = Math.Min(LocationXY.X, LocationX1Y1.X);

        Rect.Y = Math.Min(LocationXY.Y, LocationX1Y1.Y);

        Rect.Width = Math.Abs(LocationXY.X - LocationX1Y1.X);

        Rect.Height = Math.Abs(LocationXY.Y - LocationX1Y1.Y);

        return Rect;
    }

}

Output:

And here is the output output:

On the left hand side - you can see the screenshot input and on the right hand side - you can see the cropped image.

I do not understand - the rectangle marked in Red does not crop the image correctly? - it shows different area of the screenshot image.

Where is the going wrong?

Screenshot

2 Answers

Instead of flooding the memory with disposable Graphics and Bitmap objects in the MouseUp event of the PictureBox1, handle the Paint event of PictureBox2 and use the supported Graphics object to draw, and just use the e.Graphics.DrawImage overload to pass the source and destination rectangles.

//...
private Point LocationXY;
private Point LocationX1Y1;

private void button1_Click(object sender, EventArgs e)
{
    if (Clipboard.ContainsImage())
    {
        pictureBox1.Image?.Dispose();
        pictureBox1.Image = Clipboard.GetImage();
    }
}

private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    if(e.Button == MouseButtons.Left)
        LocationXY = e.Location;
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        LocationX1Y1 = e.Location;
        pictureBox1.Invalidate();
    }
}

private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
    if(e.Button == MouseButtons.Left)
    {
        LocationX1Y1 = e.Location;

        pictureBox1.Invalidate();
        pictureBox2.Invalidate();
    }
}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    if (MouseButtons == MouseButtons.Left)
        e.Graphics.DrawRectangle(Pens.Red, GetRect());
}

private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
    var src = GetRect();

    if (src == Rectangle.Empty) return;

    var des = new Rectangle(0, 0, src.Width, src.Height);

    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;

    e.Graphics.DrawImage(pictureBox1.Image,
        des, src, GraphicsUnit.Pixel);
}

private Rectangle GetRect()
{
    return new Rectangle(
        Math.Min(LocationXY.X, LocationX1Y1.X),
        Math.Min(LocationXY.Y, LocationX1Y1.Y),
        Math.Abs(LocationXY.X - LocationX1Y1.X),
        Math.Abs(LocationXY.Y - LocationX1Y1.Y)
        );
}
//...

Use the following to get the cropped image:

//...
private Bitmap GetCroppedImage()
{
    var src = GetRect();

    if (src == Rectangle.Empty) return null;

    var des = new Rectangle(0, 0, src.Width, src.Height);
    var b = new Bitmap(src.Width, src.Height);

    using (var g = Graphics.FromImage(b))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.CompositingQuality = CompositingQuality.HighQuality;

        g.DrawImage(pictureBox1.Image, des, src, GraphicsUnit.Pixel);
    }
    return b;
}
//...

Then you can save it like:

private void Save()
{
    using (var d = new SaveFileDialog())
    {
        d.Filter = "PNG|*.png|JPEG|*.jpeg;*.jpg|BMP|*.bmp";

        if (d.ShowDialog() != DialogResult.OK) return;

        using (var b = GetCroppedImage())
        {
            if (b == null) return;

            ImageFormat f;

            switch (d.FilterIndex)
            {
                case 2:
                    f = ImageFormat.Jpeg;
                    break;
                case 3:
                    f = ImageFormat.Bmp;
                    break;
                default:
                    f = ImageFormat.Png;
                    break;
            }

            b.Save(d.FileName, f);
        }
    }
}

SOQ60819266

Setting the SizeMode to Normal may not crop the image with the following code

Bitmap bit = new Bitmap(pictureBox1.Image, pictureBox1.Width, pictureBox1.Height);
Bitmap cropImg = new Bitmap(Rect.Width, Rect.Height);

The cropping will be relative to the Picturebox width and height.

Change the new Bitmap line to the following

Bitmap bit = new Bitmap(pictureBox1.Image, pictureBox1.Image.Width, pictureBox1.Image.Height);
Bitmap cropImg = new Bitmap(Rect.Width, Rect.Height);

OR Set the SizeMode of the picturebox to StretchImage

this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
Related