Search code examples
c#.netwinformssystem.drawing

Downscaling a Rectangle to Display as it would look in the Large Original Image


I'm displaying an image in a PictureBox and allowing the user to specify an area in the image by providing the width and height of the rectangle.The location of the rectangle is determined programatically.

The image displayed in the picturebox is scaled down to save processing time.So if the user specifies 200X200 Rectangle this might look large in the preview image but it will be very small in the original image which can be quite large.

How can downscale or translate the Rectangle to show how it would look in the large image.Currently i'm using the following code to upscale a rectangle in the large image.Please advice.

Rectangle ConvertToLargeRect(Rectangle smallRect, Size largeImageSize, Size smallImageSize)
{
    double xScale = (double)largeImageSize.Width / smallImageSize.Width;
    double yScale = (double)largeImageSize.Height / smallImageSize.Height;
    int x = (int)(smallRect.X * xScale + 0.5);
    int y = (int)(smallRect.Y * yScale + 0.5);
    int right = (int)(smallRect.Right * xScale + 0.5);
    int bottom = (int)(smallRect.Bottom * yScale + 0.5);
    return new Rectangle(x, y, right - x, bottom - y);
}

UPDATE: enter image description here enter image description here


Solution

  • I'll use the following methods:

    • TranslatePictureBoxSelectedRectangleToImage
      Translates a selected rectangle on the picture box to coordinates on the image.

    • TranslateImageSelectedRectangleToPictureBox
      Translates a selected rectangle on the image box to coordinates on the picture box.

    • ScaleRectangle
      Scales a rectangle by given scale factor.

    TranslatePictureBoxSelectedRectangleToImage

    public RectangleF TranslatePictureBoxSelectedRectangleToImage(PictureBox p, 
        RectangleF pictureBoxSelectedRectangle)
    {
        var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        var imageRect = (Rectangle)method.Invoke(p, new object[] { p.SizeMode });
        if (p.Image == null)
            return pictureBoxSelectedRectangle;
        var cx = (float)p.Image.Width / (float)imageRect.Width;
        var cy = (float)p.Image.Height / (float)imageRect.Height;
        var r2 = pictureBoxSelectedRectangle;
        r2.Offset(-imageRect.X, -imageRect.Y);
        return new RectangleF(r2.X * cx, r2.Y * cy, r2.Width * cx, r2.Height * cy);
    }
    

    TranslateImageSelectedRectangleToPictureBox

    public RectangleF TranslateImageSelectedRectangleToPictureBox(PictureBox p, 
        RectangleF imageSelectedRectangle)
    {
        var method = typeof(PictureBox).GetMethod("ImageRectangleFromSizeMode",
            System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
        var imageRect = (Rectangle)method.Invoke(p, new object[] { p.SizeMode });
        if (p.Image == null)
            return imageSelectedRectangle;
        var cx = (float)p.Image.Width / (float)imageRect.Width;
        var cy = (float)p.Image.Height / (float)imageRect.Height;
        var r2 = new RectangleF(imageSelectedRectangle.X / cx, imageSelectedRectangle.Y / cy,
            imageSelectedRectangle.Width / cx, imageSelectedRectangle.Height / cy);
        r2.Offset(imageRect.X, imageRect.Y);
        return r2;
    }
    

    ScaleRectangle

    public RectangleF ScaleRectangle(RectangleF r, float c)
    {
        return new RectangleF(r.X * c, r.Y * c, r.Width * c, r.Height * c);
    }
    

    Example

    Using above methods with following assumptions:

    • You have image1 in original size and image2 which is a programmatically resized version of image1 with zoom factor c. (It means c = (float)image2.Width/(float)image1.Width.)
    • You are showing image2 in picture box in zoom mode.

    Question 1 - Having r1 as selected rectangle on picureBox1, what is the rectangle size and location on image1?

    The first method shows how you can convert r1 on picture box, to a rectangle on image2. To convert it to the rectangle on image1, since you know the zoom factor which you used to create image2 from image1, it's enough to apply the same zoom factor on result of first method:

    //First convert rectangle of pictureBox1 to rectangle of image2
    var r2 = TranslatePictureBoxSelectedRectangleToImage(pictureBox1, r1);
    
    //Then convert rectangle of image2 to rectangle of image1
    var result = ScaleRectangle(r2, 1f/c);
    

    Question 2 - Having r1 as selected rectangle on image1, what is the rectangle size and location on pictureBox1?

    The second method shows how you can convert r1 on image2, to a rectangle on pictureBox1. To convert from a rectangle on image1, since you know the zoom factor which you used to create image2 from image1, it's enough to apply the same zoom factor on r1 to get the rectangle on image2, then use the second method:

    //First convert rectangle of the image1 to rectangle of image2
    var r2 = ScaleRectangle(r1, c);
    
    //Then convert rectangle of image2 to rectangle of pictureBox1
    var result = TranslateImageSelectedRectangleToPictureBox(pictureBox1, r2);