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?

Thanks in advance,

[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