2

I'm using the following class Ref - Translate Rectangle Position in Zoom Mode Picturebox to translate a rectangle selection within a Bitmap to a Frame Control drawn over a picturebox in the UI.

public class ZoomFactor
{
    public ZoomFactor() { }

    public PointF TranslateZoomPosition(PointF coordinates, SizeF containerSize, SizeF imageSize)
    {
        PointF imageOrigin = TranslateCoordinatesOrigin(coordinates, containerSize, imageSize);
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        return new PointF(imageOrigin.X / scaleFactor, imageOrigin.Y / scaleFactor);
    }

    public RectangleF TranslateZoomSelection(RectangleF selectionRect, SizeF containerSize, SizeF imageSize)
    {
        PointF selectionTrueOrigin = TranslateZoomPosition(selectionRect.Location, containerSize, imageSize);
        float scaleFactor = GetScaleFactor(containerSize, imageSize);

        SizeF selectionTrueSize = new SizeF(selectionRect.Width / scaleFactor, selectionRect.Height / scaleFactor);
        return new RectangleF(selectionTrueOrigin, selectionTrueSize);
    }

    public RectangleF TranslateSelectionToZoomedSel(RectangleF selectionRect, SizeF containerSize, SizeF imageSize)
    {
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        RectangleF zoomedSelectionRect = new
            RectangleF(selectionRect.X * scaleFactor, selectionRect.Y * scaleFactor,
                       selectionRect.Width * scaleFactor, selectionRect.Height * scaleFactor);

        PointF imageScaledOrigin = GetImageScaledOrigin(containerSize, imageSize);
        zoomedSelectionRect.Location = new PointF(zoomedSelectionRect.Location.X + imageScaledOrigin.X,
                                                  zoomedSelectionRect.Location.Y + imageScaledOrigin.Y);
        return zoomedSelectionRect;
    }

    public PointF TranslateCoordinatesOrigin(PointF coordinates, SizeF containerSize, SizeF imageSize)
    {
        PointF imageOrigin = GetImageScaledOrigin(containerSize, imageSize);
        return new PointF(coordinates.X - imageOrigin.X, coordinates.Y - imageOrigin.Y);
    }

    public PointF GetImageScaledOrigin(SizeF containerSize, SizeF imageSize)
    {
        SizeF imageScaleSize = GetImageScaledSize(containerSize, imageSize);
        return new PointF((containerSize.Width - imageScaleSize.Width) / 2,
                          (containerSize.Height - imageScaleSize.Height) / 2);
    }

    public SizeF GetImageScaledSize(SizeF containerSize, SizeF imageSize)
    {
        float scaleFactor = GetScaleFactor(containerSize, imageSize);
        return new SizeF(imageSize.Width * scaleFactor, imageSize.Height * scaleFactor);

    }
    internal float GetScaleFactor(SizeF scaled, SizeF original)
    {
        return (original.Width > original.Height) ? (scaled.Width / original.Width)
                                                  : (scaled.Height / original.Height);
    }
}

// Using the Class
RectangleF BitmapRect = ZoomHelper.TranslateSelectionToZoomedSel(ResizedRect, new SizeF(pbox_preview.Width, pbox_preview.Height), bit.Size);

This works fine for most inputs but when the following input is provided the returned Y coordinate is negative and the scaling is wrong.

ResizedRect --> {X = 37 Y = 2 Width = 227 Height = 308} 

PictureBoxSize ---> SizeF(603,423)

imagesize --->  (311,310)

The returned rectangle is this 
{X = 71.73955 Y = -85.15273 Width = 440.131836 Height = 597.1833}

The Y Coordinate is Negative.

techno
  • 6,100
  • 16
  • 86
  • 192
  • I didn't test Jimi's code which he has shared in [this post](https://stackoverflow.com/a/53802043/3110834), but it looks like it's what you are looking for (the reverse calculation); however, for sake of completeness, I also shared the reverse method, using the same solution as my original method. – Reza Aghaei Jun 24 '21 at 19:11

2 Answers2

2

You know:

  • How to calculate the zoom factor (scale) of the zoomed picture box.
  • How to get the image offset (top-left corner) in the zoomed picture box.

Then having a rectangle on the image (with original size), this is how you can calculate the rectangle size and position on the zoomed picture box:

  1. Zoom out the rectangle using the vertical and horizontal scales
  2. Offset the result using the image offset of the zoomed picture box.

Here are the two methods:

  • GetRectangeOnImage: Having the rectangle on a zoomed picture box, it returns a rectangle on the original image
  • GetRectangeOnPictureBox: Having the rectangle on original image, it returns a rectangle on the zoomed picture box.

Code:

public RectangleF GetRectangeOnImage(PictureBox p, Rectangle rectOnPictureBox)
{
    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 rectOnPictureBox;
    var cx = (float)p.Image.Width / (float)imageRect.Width;
    var cy = (float)p.Image.Height / (float)imageRect.Height;
    rectOnPictureBox.Offset(-imageRect.X, -imageRect.Y);
    return new RectangleF(rectOnPictureBox.X * cx, rectOnPictureBox.Y * cy,
        rectOnPictureBox.Width * cx, rectOnPictureBox.Height * cy);
}
public RectangleF GetRectangeOnPictureBox(PictureBox p, Rectangle rectOnImage)
{
    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 rectOnImage;
    var cx = (float)p.Image.Width / (float)imageRect.Width;
    var cy = (float)p.Image.Height / (float)imageRect.Height;
    var r2 = new RectangleF(rectOnImage.X / cx, rectOnImage.Y / cy,
        rectOnImage.Width / cx, rectOnImage.Height / cy);
    r2.Offset(imageRect.X, imageRect.Y);
    return r2;
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • The method is not tested for edge cases and you may want to enhance it a bit; but in simple tests it worked as expected. – Reza Aghaei Jun 24 '21 at 19:14
1

I did not actually try out your code, so this is just a shot in the blue: Maybe, in your function GetScaleFactor you don't want to compare original.Width > original.Height but original.Width/original.Height > scaled.Width/scaled.Height

LionAM
  • 1,271
  • 10
  • 28
  • Actually this fixed the negative coordinate issue but the height of the translated rectangle is greater than the actual height. – techno Jun 20 '21 at 08:31