Search code examples
c#.netwinformsimagecrop

Crop image using a fixed-size draggable picturebox


I'm working on a winforms project that involves cropping an image. My goal is to do this by using a fixed-size draggable picturebox control, allowing the user to select the area they want to preserve.

My problem is when I crop the image; it "works", but the crop area offsets a little. Here's the result I get:

Original image Cropped image

To clarify, I'm not talking about the zooming, that's per design. Notice the orange box is mostly focusing on the eye of the storm, but the cropped image is not.

This is my code for the crop operation:

private void tsbRecortar_Click(object sender, EventArgs e)
{
    Rectangle recorte = new Rectangle(pbxSeleccion.Location.X, pbxSeleccion.Location.Y, pbxSeleccion.Width, pbxSeleccion.Height);

    foto = recortarImagen(foto, recorte);
    pbxImagen.Image = foto;
}

private Image recortarImagen(Image imagen, Rectangle recuadro)
{
    try
    {
        Bitmap bitmap = new Bitmap(imagen);
        Bitmap cropedBitmap = bitmap.Clone(recuadro, bitmap.PixelFormat);

        return (Image)(cropedBitmap);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error");

        return null;
    }
}

pbxSeleccion is the draggable orange rectangle; its parent is pbxImage (I re-parent it on form's load).

As you can see, I'm using the coordinates of pbxSeleccion to define the starting point of the crop area, but is not working as expected... sometimes, I even get an "Out of Memory" exception.

I think this has to do with how the image loads in the parent picturebox, something about how the margin is handled "under the hood", but nothing I tried fixes it... just changes the magnitude of the offset.

Searching the web and SO has helped me a lot, but for this particular issue, I can't seem to find an answer... please, feel free to point out improvements to my code, I haven't been coding for long and I'm new to C# and .NET

Any help is highly appreciated. Cheers!


Solution

  • Suppose your original image is displayed in a PictureBox. You passed in the wrong location of the orange cropping window. Here is the corrected code for you:

    private void tsbRecortar_Click(object sender, EventArgs e){
      Point p = yourPictureBox.PointToClient(pbxSelection.PointToScreen(Point.Empty));
      Rectangle recorte = new Rectangle(p.X, p.Y, pbxSeleccion.Width, pbxSeleccion.Height);
    
      foto = recortarImagen(foto, recorte);
      pbxImagen.Image = foto;
    }
    

    I use PointToClient and PointToScreen here because I think it's the best way to do. You can then change the container of your pictureBox safely without having to modify the code. If you use the code like the following, it's not dynamically enough when you want to place your pictureBox in another container:

    Rectangle recorte = new Rectangle(pbxSeleccion.X + yourPictureBox.Left,
                                      pbxSeleccion.Y + yourPictureBox.Top, 
                                      pbxSeleccion.Width, pbxSeleccion.Height);
    

    NOTE: you can also use RectangleToClient and RectangleToScreen like this:

    private void tsbRecortar_Click(object sender, EventArgs e){
       Rectangle recorte = yourPictureBox.RectangleToClient(pbxSeleccion.RectangleToScreen(pbxSeleccion.ClientRectangle));
       foto = recortarImagen(foto, recorte);
       pbxImagen.Image = foto;
    }