Proportionally resizing a Picture

  1. 4 years ago

    Garry P

    23 May 2014 Pre-Release Testers Europe (Torquay, UK)

    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,

  2. Michel B

    23 May 2014 Pre-Release Testers, Xojo Pro

    @Garry P 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?

    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

    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.

  3. Kem T

    23 May 2014 Pre-Release Testers, Xojo Pro New York

    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
  4. Garry P

    23 May 2014 Pre-Release Testers Europe (Torquay, UK)

    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:

    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
  5. Sam R

    23 May 2014 Pre-Release Testers, Xojo Pro Hengchun, Pingtung, Taiwan

    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.

      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
  6. Christian S

    24 May 2014 Pre-Release Testers, Xojo Pro Germany

    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.

  7. 8 months ago

    Tim T

    21 Apr 2017 Pre-Release Testers, Xojo Pro Atlanta, GA

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

  8. Sam R

    21 Apr 2017 Pre-Release Testers, Xojo Pro Hengchun, Pingtung, Taiwan

    @Tim T Hey for these routines maxWidth and maxHeight passed in are those in PIXELS???

    pictures are pixel based.

  9. Edited 8 months ago

    @Sam R 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>

    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
  10. Sam R

    22 Apr 2017 Pre-Release Testers, Xojo Pro Hengchun, Pingtung, Taiwan

    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.

    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

    You also need the enum

    Public Enum pictureScaleMode
    toFit
    toFill
    stretchToFill
    End Enum

or Sign Up to reply!