Question Scale Rectangle to Fit in Another

StoneCodeMonkey

Well-known member
Joined
Apr 17, 2009
Messages
56
Programming Experience
5-10
I have been using the same basic code to calculate the scale factor to fit one rectangle within another for some time now. After posting a few replys on how to do this, in various forms, I decided to go back and see if there is a better way. But, I keep coming back to the same methods.

Does anyone know a better method to do this other than the following code while maintaining the aspect ratio?
VB.NET:
    Function ScaleToFit(ByVal DestinationRectangle As Rectangle, ByVal RectangleToScale As Rectangle) As Rectangle
        Dim pOrigin As Point = New Point(DestinationRectangle.Left, DestinationRectangle.Top)
        Dim pSize As Size = New Size(DestinationRectangle.Width, DestinationRectangle.Height)
        Dim wRatio As Single = pSize.Width / RectangleToScale.Width
        Dim hRatio As Single = pSize.Height / RectangleToScale.Height
        Dim ScaleToWidth As Boolean = False
        Dim ScaleToHeight As Boolean = False

        If wRatio < hRatio Then
            'Scale to the width ratio
            ScaleToWidth = True
        Else
            'Scale to the height ratio
            ScaleToHeight = True
        End If

        Dim sizePrint As Size
        If RectangleToScale.Width < pSize.Width AndAlso RectangleToScale.Height < pSize.Height Then
            sizePrint = New Size(RectangleToScale.Width, RectangleToScale.Height)
        Else
            If ScaleToWidth Then
                sizePrint = New Size(RectangleToScale.Width * wRatio, RectangleToScale.Height * wRatio)
            ElseIf ScaleToHeight Then
                sizePrint = New Size(RectangleToScale.Width * hRatio, RectangleToScale.Height * hRatio)
            End If
        End If
        sizePrint.Height -= 1
        sizePrint.Width -= 1


        'Just centers the rectangle within the destination rectangle
        Dim loc As Point
        loc = New Point(pOrigin.X + ((pSize.Width - sizePrint.Width) / 2), pOrigin.Y + ((pSize.Height - sizePrint.Height) / 2))
        Return New Rectangle(loc, sizePrint)

    End Function
 
I have the same calculations, only refactored the code some. The main idea here is that the scaling is done only for the source and target size, the rectangle locations is not relevant for this. Also centering is done within the sizes only. Then finally the new rectangle location can be offset to the target location.
VB.NET:
Function ScaleToFit(ByVal source As Rectangle, ByVal target As Rectangle) As Rectangle
    Dim fitSize As Size = Size.Round(ScaleToFit(source.Size, target.Size))
    Dim centerPt As New Point(Math.Abs(fitSize.Width - target.Width) \ 2, Math.Abs(fitSize.Height - target.Height) \ 2)
    centerPt.Offset(target.Location)
    Return New Rectangle(centerPt, fitSize)
End Function

Function ScaleToFit(ByVal source As Size, ByVal target As Size) As SizeF
    Dim ratio As Single = CSng(Math.Max(source.Width / target.Width, source.Height / target.Height))
    Return New SizeF(source.Width / ratio, source.Height / ratio)
End Function
 
Hi StoneCodeMonkey, I couldn't resist replying to this because I've just been doing something very similar myself (a function for scaling images into a window for a graphics program). I can't see much wrong with your method except that it does seem a bit long-winded, with lots of intermediate variables.

Anyway, my solution is a bit shorter for several reasons.
1. I start off with a rectangle (rect) the same size as the destination rectangle.
2. I use a Rectangle.Inflate statement to produce a small margin around the inner rectangle (I assume that was the intention of reducing the width and height by 1 pixel).
3. I compare the aspect ratios of the two rectangles directly. Then I can decide whether to scale down either the width or the height of the inside rectangle, so as to restore its correct aspect ratio.

Of course, this is just a question of style. The method is the same in principle and it won't make a jot of difference to performance. Perhaps some people will find your code easier to understand. Well here it is:

VB.NET:
    Private Function ScaleToFit(ByVal DestinationRectangle As Rectangle, ByVal RectangleToScale As Rectangle) As Rectangle

        'Start off with a rectangle the same size as the destination rectangle:
        Dim rect As Rectangle = DestinationRectangle
        'Reduce it by 1 pixel all round to leave a slight margin:
        rect.Inflate(-1, -1)

        'find the aspect ratios of the two rectangles:
        Dim AspectRatioScale As Double = RectangleToScale.Width / RectangleToScale.Height
        Dim AspectRatioDest As Double = DestinationRectangle.Width / DestinationRectangle.Height

        'compare the aspect ratios of the two rectangles:
        If AspectRatioScale > AspectRatioDest Then
            'if the inside rectangle has the wider shape, reduce its height
            rect.Height = CInt(rect.Width / AspectRatioScale)
        Else
            'if the inside rectangle has the taller shape, reduce its width:
            rect.Width = CInt(rect.Height * AspectRatioScale)
        End If

        'now centre the rectangle:
        rect.X = DestinationRectangle.X + (DestinationRectangle.Width - rect.Width) \ 2
        rect.Y = DestinationRectangle.Y + (DestinationRectangle.Height - rect.Height) \ 2
        Return rect

    End Function

all the best, Vic
 
VicJ, "maintaining the aspect ratio" means that the Size is scaled equal amount both for width and height, the scale can be both up or down. It is the source size that must be scaled to fit in target size. If you view an image in PictureBox you can see the effect by setting SizeMode Property to Zoom. If you only change one of the dimensions you will get some kind of stretch effect.
 
Hi John,

I think you need to take another look. Perhaps you are being misled by the apparent simplicity of the method.

Of course both sides are changed -- first by "expanding" the source rectangle to fill the destination, then by correcting the new width or height (as appropriate) using the original source aspect ratio. It works.

regards, Vic
 
Last edited:
Yeah sorry VicJ, on second reading I see now what you're doing, and that you produce same results as OP and me.
 
Back
Top