介绍下在iOS中的多种图片缩略方式
ref https://nshipster.com/image-resizing/
在UIKit CoreGraphics ImageIO CoreImage vImage
多种缩略方法中 Core Image表现最差。Core Graphics 和 Image I/O最好。
实际上,在苹果官方在 Performance Best Practices section of the Core Image Programming Guide 部分中特别推荐使用Core Graphics或Image I / O功能预先裁剪或缩小图像。
UIKit
UIGraphicsBeginImageContextWithOptions & UIImage -drawInRect:
用于图像大小调整的最高级API可以在UIKit框架中找到。给定一个UIImage,可以使用临时图形上下文来渲染缩放版本。这种方式最简
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func uikit_resize(oriImg:UIImage?,size:CGSize) -> UIImage?{ let hasAlpha = false let scale: CGFloat = 0.0
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale) oriImg!.draw(in: CGRect(origin: .zero, size: size)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resizedImage!
}
|
CoreGraphics
CGBitmapContextCreate & CGContextDrawImage
CoreGraphics / Quartz 2D提供了一套较低级别的API,允许进行更高级的配置。 给定一个CGImage,使用临时位图上下文来渲染缩放后的图像。
使用CoreGraphics图像的质量与UIKit图像相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| func coreGraphics_resize(oriImg:UIImage,size:CGSize)->UIImage?{ guard let cgImage = oriImg.cgImage else { return nil } let bitsPerComponent = cgImage.bitsPerComponent let bytesPerRow = cgImage.bytesPerRow let colorSpace = cgImage.colorSpace let bitmapInfo = cgImage.bitmapInfo let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace!, bitmapInfo: bitmapInfo.rawValue) context?.interpolationQuality = .high context?.draw(cgImage, in: CGRect(origin: .zero, size: size)) guard let resizedImage = context?.makeImage() else{return nil} return UIImage(cgImage: resizedImage) }
|
ImageIO
CGImageSourceCreateThumbnailAtIndex
Image I / O是一个功能强大但鲜为人知的用于处理图像的框架。 独立于Core Graphics,它可以在许多不同格式之间读取和写入,访问照片元数据以及执行常见的图像处理操作。 这个库提供了该平台上最快的图像编码器和解码器,具有先进的缓存机制,甚至可以逐步加载图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func imageIO_resize(url:URL,size:CGSize) -> UIImage? { let options: [CFString: Any] = [ kCGImageSourceCreateThumbnailFromImageIfAbsent: true, kCGImageSourceCreateThumbnailWithTransform: true, kCGImageSourceShouldCacheImmediately: true, kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) ] guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil), let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil } return UIImage(cgImage: image) }
|
CoreImage
里面提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。IOS提供了很多强大的滤镜(Filter),这些Filter提供了各种各样的效果,并且还可以通过滤镜链将各种效果的Filter叠加起来,形成强大的自定义效果,如果你对该效果不满意,还可以子类化滤镜。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| let sharedContext = CIContext(options: [.useSoftwareRenderer : false]) func coreImage_resize(oriImg:UIImage,size:CGSize) -> UIImage? { guard let cgImage = oriImg.cgImage else { return nil } let scale = (Double)(size.width) / (Double)(oriImg.size.width) let image = CIImage(cgImage: cgImage) let filter = CIFilter(name: "CILanczosScaleTransform") filter?.setValue(image, forKey: kCIInputImageKey) filter?.setValue(NSNumber(value:scale), forKey: kCIInputScaleKey) filter?.setValue(1.0, forKey:kCIInputAspectRatioKey) guard let outputCIImage = filter?.outputImage, let outputCGImage = sharedContext.createCGImage(outputCIImage, from: outputCIImage.extent) else { return nil } return UIImage(cgImage: outputCGImage) }
|
vImage
使用CPU的矢量处理器处理大图像。 强大的图像处理功能,包括Core Graphics和Core Video互操作,格式转换和图像处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| func vimage_resize(oriImg:UIImage,size:CGSize) -> UIImage? { guard let cgImage = oriImg.cgImage else { return nil } var format = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: nil, bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue), version: 0, decode: nil, renderingIntent: .defaultIntent) var sourceBuffer = vImage_Buffer() defer { free(sourceBuffer.data) } var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags)) guard error == kvImageNoError else { return nil } let scale = oriImg.scale let destWidth = Int(size.width) let destHeight = Int(size.height) let bytesPerPixel = cgImage.bitsPerPixel / 8 let destBytesPerRow = destWidth * bytesPerPixel let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow) defer { destData.deallocate() } var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow) error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling)) guard error == kvImageNoError else { return nil } var destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue() guard error == kvImageNoError else { return nil } let resizedImage = destCGImage.flatMap { UIImage(cgImage: $0, scale: 0.0, orientation: oriImg.imageOrientation) } destCGImage = nil return resizedImage }
|