According to this session, iOS Memory Deep Dive, we had better use ImageIO
to downscale images.
The bad of using UIImage
downscale images.
- Will decompress original image into memory
- Internal coordinate space transforms are expensive
Use ImageIO
About Image in memory
- Memory use is related to the dimensions of the images, not the file size.
UIGraphicsBeginImageContextWithOptions
always uses SRGB
rendering-format, which use 4 bytes per pixel.
- A image have
load -> decode -> render
3 phases.
UIImage
is expensive for sizing and to resizing
For the following image, if you use UIGraphicsBeginImageContextWithOptions
we only need 590KB to load a image, while we need
2048 pixels x 1536 pixels x 4 bytes per pixel
= 10MB when decoding
while UIGraphicsImageRenderer
, introduced in iOS 10, will automatically pick the best graphic format in iOS12. It means, you may save 75% of memory by replacing UIGraphicsBeginImageContextWithOptions
with UIGraphicsImageRenderer
if you don't need SRGB.
This is my article about iOS images in memory
func resize(url: NSURL, maxPixelSize: Int) -> CGImage? {
let imgSource = CGImageSourceCreateWithURL(url, nil)
guard let imageSource = imgSource else {
return nil
}
var scaledImage: CGImage?
let options: [NSString: Any] = [
// The maximum width and height in pixels of a thumbnail.
kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
kCGImageSourceCreateThumbnailFromImageAlways: true,
// Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
kCGImageSourceCreateThumbnailWithTransform: true
]
scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary)
return scaledImage
}
let filePath = Bundle.main.path(forResource:"large_leaves_70mp", ofType: "jpg")
let url = NSURL(fileURLWithPath: filePath ?? "")
let image = resize(url: url, maxPixelSize: 600)
or
// Downsampling large images for display at smaller size
func downsample(imageAt imageURL: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
let imageSource = CGImageSourceCreateWithURL(imageURL as CFURL, imageSourceOptions)!
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
let downsampleOptions =
[kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
// Should include kCGImageSourceCreateThumbnailWithTransform: true in the options dictionary. Otherwise, the image result will appear rotated when an image is taken from camera in the portrait orientation.
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels] as CFDictionary
let downsampledImage =
CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions)!
return UIImage(cgImage: downsampledImage)
}