Proportionally resizing a Picture

I am trying to figure out how to proportionally resize a Picture object if it is greater than a certain width or height.

For instance, given a Picture object, p, how would I scale this if its width is > maxWidth or its height > maxHeight such that is fits these dimensions but maintains its aspect ration (i.e. is not distorted)?

Does anybody have a working code example to achieve this?

[quote=90702:@Garry Pettet]I am trying to figure out how to proportionally resize a Picture object if it is greater than a certain width or height.

For instance, given a Picture object, p, how would I scale this if its width is > maxWidth or its height > maxHeight such that is fits these dimensions but maintains its aspect ration (i.e. is not distorted)?

Does anybody have a working code example to achieve this?[/quote]

[code]Sub Paint(g As Graphics, areas() As REALbasic.Rect)
if MyImage = Nil then return
Dim ratio as double
dim largeur as integer
if MyImage.Width > MyImage.Height then
ratio = me.width / MyImage.width
largeur = me.width
else
ratio = me.Height / MyImage.height
largeur = me.Height * (MyImage.Width / MyImage.Height)
end if

dim destheight as integer = MyImage.height * ratio
dim large as integer = MyImage.Width
dim haut as integer = MyImage.Height
g.DrawPicture(MyImage,(Me.width-largeur)/2,0,largeur,destheight,0,0,large,haut)

End Sub
[/code]

Whatever the proportions of the picture, it will be sized to the smaller dimension. For instance a portrait image in a landscape canvas will occupy the total height and be reduced accordingly to preserve its aspect ratio.

1 Like

Hereâ€™s the code I came up with:

``````Function ResizeToFit(Extends p As Picture, maxWidth As Integer, maxHeight As Integer) As Picture
dim ratio as double = p.Width / p.Height

dim w1 as Integer = maxWidth
dim h1 as Integer = w1 / ratio

dim h2 as Integer = maxHeight
dim w2 as Integer = h2 * ratio

dim useWidth, useHeight as Integer

if h1 > maxHeight then
useWidth = w2
useHeight = h2

elseif w2 > maxWidth then
useWidth = w1
useHeight = h1

elseif ( maxHeight - h1 ) < ( maxWidth - w2 ) then
useWidth = w1
useHeight = h1

else
useWidth = w2
useHeight = h2

end if

dim newPict as new Picture( useWidth, useHeight, p.Depth )
newPict.Graphics.DrawPicture( p, 0, 0, newPict.Width, newPict.Height, 0, 0, p.Width, p.Height )

return newPict

End Function``````

Both these methods work. Thanks for the inspiration.

Iâ€™ve distilled them into as few lines as code and am using it as class extension:

[code]Function ResizeToFit(Extends p as Picture, maxWidth as Integer, maxHeight as Integer) As Picture
â€™ Calculate the scale ratio

dim ratio as Double = min( maxHeight/p.height, maxWidth/p.width)

â€™ Calculate new size
dim w as integer = p.width * ratio
dim h as integer = p.height * ratio

â€™ Create a new picture to return
dim newPic as new Picture(w, h, 32)

â€™ Draw picture in the new size
newPic.graphics.DrawPicture(p, 0, 0, w, h, 0, 0, p.width, p.height)

return newPic
End Function[/code]

Nice,
For many years I was using code very similar to what Garry posted, just no where near as elegant!

If I may, I can squeeze out a few more lines from your code.

[code] Function ResizeToFit(Extends p as Picture, maxWidth as Integer, maxHeight as Integer) As Picture
â€™ Calculate the scale ratio

``````dim ratio as Double = min( maxHeight/p.height, maxWidth/p.width)

' Create a new picture to return
dim newPic as new Picture( p.width * ratio, p.height * ratio )

' Draw picture in the new size
newPic.graphics.DrawPicture( p, 0, 0, newPic.width, newPic.height, 0, 0, p.width, p.height)

return newPic
``````

End Function[/code]

1 Like

itâ€™s normally a good idea to put calculation results into local variables. that way you can look at them in debugger.

e.g. if picture constructor raises exception, you may want to see what values you have for width and height.

PS: Your resize doesnâ€™t handle mask. But may work for alpha channel images.

Hey for these routines maxWidth and maxHeight passed in are those in PIXELS???
Or inches? Or does it matter?

pictures are pixel based.

[quote=91015:@Sam Rowlands]Nice,
For many years I was using code very similar to what Garry posted, just no where near as elegant!

If I may, I can squeeze out a few more lines from your code.

``` <snip> dim ratio as Double = min( maxHeight/p.height, maxWidth/p.width) </snip> ```[/quote]

I think â€śminâ€ť here can be replaced with â€śmaxâ€ť. Min will â€śfit to sizeâ€ť the entire image and max will â€śfill to sizeâ€ť, giving a bit of cropping. I use pretty much the above code, adding a FillOrFit parameter, â€ś0â€ť to fit â€ś1â€ť to fill

```If FillOrFit = 0 Then dim ratio as Double = min( maxHeight/p.height, maxWidth/p.width) Else dim ratio as Double = max( maxHeight/p.height, maxWidth/p.width) End```

If it helpsâ€¦ Hereâ€™s what I currently use to resize images, it allows for fitting, filling (with cropping) and stretching. Also importantly you can prevent small images from resized up, which I needed for something.

[code]Public Function resizeToFit(extends p as picture, maxWidth as integer, maxHeight as integer, scaleMode as pictureScaleMode = pictureScaleMode.toFit, onlyShrink as boolean = false) as picture
Dim nSize as xojo.core.size

select case scaleMode
case picturescaleMode.toFit, picturescaleMode.toFill // â€“ Scale to fit & Scale to fill
dim scale as Double = if( scaleMode = picturescaleMode.toFit, _
min( maxHeight / p.height, maxWidth / p.width ), _
max( maxHeight / p.height, maxWidth / p.width ) )

``````if onlyShrink then scale = min( scale, 1.0 )

nSize = new xojo.core.size( floor( p.width * scale ), floor( p.height * scale ) )
``````

case picturescaleMode.stretchToFill // â€” Stretch to fill.
nSize = new xojo.Core.size( maxWidth, maxHeight )

end select

// â€” Now create the target image and draw our image into the center.
Dim rvalue as new picture( min( nSize.width, maxWidth ), min( nSize.height, maxHeight ) )
rvalue.graphics.drawPicture( p, ( rvalue.width - nSize.width ) * 0.5, ( rvalue.height - nSize.height ) * 0.5, _
nSize.width, nSize.height, 0, 0, p.width, p.height )

return rvalue
End Function
[/code]

You also need the enum

```Public Enum pictureScaleMode toFit toFill stretchToFill End Enum```