介绍下在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 }
  |