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

Are you joining the containers revolution? Start leveraging container management using Platform9's ultimate guide to Kubernetes deployment.


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:


Moving towards a private or Hybrid cloud infrastructure model? Get started with our OpenStack Deployment Models guide to learn the proper deployment model for your organization.

Topics:

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}