How to scale to desired size in pixels using af_imageAspectScaled
Asked Answered
L

1

0

I'm using AlamoFireImage to crop an user profile picture before sending it to the server. Our server has some restrictions and we can't send images larger than 640x640.

I'm using the af_imageAspectScaled UIImage extension function like so:

let croppedImage = image.af_imageAspectScaled(
  toFill: CGSize(
    width: 320,
    height: 320
  )
)

I was expecting this to crop image to a 320px by 320px image. However I found out that the output image is being saved as a 640x640px image with scale 2.0. The following XCTest shows this:

class UIImageTests: XCTestCase {
  func testAfImageAspectScaled() {
    if let image = UIImage(
      named: "ipad_mini2_photo_1.JPG",
      in: Bundle(for: type(of: self)),
      compatibleWith: nil
    ) {
      print (image.scale) // prints 1.0
      print (image.size)  // prints (1280.0, 960.0)

      let croppedImage = image.af_imageAspectScaled(
        toFill: CGSize(
          width: 320,
          height: 320
        )
      )

      print (croppedImage.scale) // prints 2.0
      print (croppedImage.size)  // prints (320.0, 320.0)
    }  
  }
}

I'm running this on the iPhone Xr simulator on Xcode 10.2.

The original image is 1280 by 960 points, with scale 1, which would be equivalent to 1280 by 960 pixels. The cropped image is 320 by 320 points, with scale 2, which would be equivalent to 640 by 640 pixels.

Why is the scale set to 2? Can I change that? How can I generate a 320 by 320 pixels image independent on the scale and device?

Lathy answered 1/5, 2019 at 0:36 Comment(0)
L
1

Well, checking the source code for the af_imageAspectScaled method I found the following code for generating the actual scaled image:

UIGraphicsBeginImageContextWithOptions(size, af_isOpaque, 0.0)
draw(in: CGRect(origin: origin, size: scaledSize))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? self
UIGraphicsEndImageContext()

The parameter with value 0.0 on UIGraphicsBeginImageContextWithOptions tells the method to use the main screen scale factor for defining the image size.

I tried setting this to 1.0 and, when running my testcase, af_imageAspectScaled generated an image with the correct dimensions I wanted.

Here there is a table showing all the iOS devices resolutions. My app was sending appropriate sized images for all devices where the scale factor was 2.0, however several devices have scale factor 3.0. For those the app wasn't working.

Well, unfortunately it seems that if I want to use af_imageAspectScaled I have to divide the final size I want by the device's scale when setting the scaled size like so:

let scale = UIScreen.main.scale

let croppedImage = image.af_imageAspectScaled(
  toFill: CGSize(
    width: 320/scale,
    height: 320/scale
  )
)

I've sent a pull request to AlamofireImage proposing the addition of a parameter scale to the functions af_imageAspectScaled(toFill:), af_imageAspectScaled(toFit:) and af_imageScaled(to:). If they accept it, the above code should become:

// this is not valid with Alamofire 4.0.0 yet! waiting for my pull request to
// be accepted
let croppedImage = image.af_imageAspectScaled(
  toFill: CGSize(
    width: 320,
    height: 320
  ),
  scale: 1.0
)
// croppedImage would be a 320px by 320px image, regardless of the device type.
Lathy answered 1/5, 2019 at 1:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.