Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Scaling, Resizing and Orienting Images to a Bounding Square in Swift

DZone's Guide to

Scaling, Resizing and Orienting Images to a Bounding Square in Swift

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.


Earlier this year, I blogged about a small extension I wrote for UIImage for resizing images within a bounding square. Part of my current project, a rewrite of my Nodality application in Swift, requires similar functionality but I wanted to improve the scaling quality and handle images with different orientations (i.e. portrait and landscape).

My resizeToBoundingSquare() function accepts an image and numeric value that defines the bounding square's width and it returns a UIImage. The signature, therefore, is:


 func resizeToBoundingSquare(sourceImage: UIImage, #boundingSquareSideLength : CGFloat) -> UIImage

My first step was to use Lanczos resampling rather than simply relying on drawInRect() at the new size. Lanczos offers smooth resampling which should lead to a better quality image and it's available as one of the built in Core Image filters. 

The code to execute the filter and generate a properly sized CIImage is:

let imgScale = sourceImage.size.width < sourceImage.size.height ? boundingSquareSideLength / sourceImage.size.width : boundingSquareSideLength / sourceImage.size.height
    
    let scaleTransform = CIFilter(name: "CILanczosScaleTransform")
    scaleTransform.setValue(CIImage(image: sourceImage), forKey: "inputImage")
    scaleTransform.setValue(imgScale, forKey: "inputScale")
    scaleTransform.setValue(1.0, forKey: "inputAspectRatio")

    let outputImage = scaleTransform.valueForKey("outputImage") as CIImage

The CIImage is scaled but not square. To create a cropped and centred UIImage, I use the following code:

let context = CIContext(options: nil)
    
    let extent = outputImage.extent()
    let xOffset = Int(extent.width) > NodeConstants.imageWidth ? (extent.width - boundingSquareSideLength) / 2 : 0
    let yOffset = Int(extent.height) > NodeConstants.imageWidth ? (extent.height - boundingSquareSideLength) / 2 : 0
    

    let scaledImage = UIImage(CGImage: context.createCGImage(outputImage, fromRect: CGRect(x: xOffset, y: yOffset, width: boundingSquareSideLength, height: boundingSquareSideLength)))!

Now we have a scaled, square UIImage but there's one final issue. If the original source has been shot in landscape format, the image is rotated. So the final steps are to look at the source image's orientation and rotate accordingly. First, I define an angle to rotate by:

var angle = 0.0;
    
    if (sourceImage.imageOrientation == UIImageOrientation.Right)
    {
        angle = 90.0
    }
    else if (sourceImage.imageOrientation == UIImageOrientation.Left)
    {
        angle = -90.0
    }
    else if (sourceImage.imageOrientation == UIImageOrientation.Down)
    {
        angle = 180
    }
    else if (sourceImage.imageOrientation == UIImageOrientation.Up)
    {
        angle = 0.0

    }

Then I use a CGContext to draw the image. Because the rotation pivot point is at (0,0), I need to translate the context by half the bounding square width before rotating and do the drawAtPoint() at the opposite of that translation:

UIGraphicsBeginImageContext(CGSize(width: boundingSquareSideLength, height: boundingSquareSideLength))
    let cgContext = UIGraphicsGetCurrentContext()
    
    let offset = boundingSquareSideLength / 2.0
    
    CGContextTranslateCTM(cgContext, offset, offset)
    CGContextRotateCTM(cgContext, CGFloat(angle * M_PI / 180))
    

    scaledImage.drawAtPoint(CGPoint(x: -offset, y: -offset))

Finally, I get the UIImage from the context and release it:

let final = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    

    return final


And the result is beautifully scaled images, centred and cropped to a square!

This source code isn't publicly available on GitHub, but this post does contain the full source code of resizeToBoundingSquare().

Finally, if you're wondering how Nodality is looking in Swift, here's a little demo video to whet your appetite:


Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}