iOS图片-图片缩略方法

介绍下在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 // Automatically use scale factor of main screen

/**
创建一个图片类型的上下文。调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片
size:表示所要创建的图片的尺寸
opaque:表示这个图层是否完全透明,如果图形完全不用透明最好设置为YES以优化位图的存储,这样可以让图层在渲染的时候效率更高
scale:指定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好
*/
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
oriImg!.draw(in: CGRect(origin: .zero, size: size))

let resizedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resizedImage!

//in iOS 10 UIGraphicsImageRenderer
// let render = UIGraphicsImageRenderer(size: size)
// return render.image(actions: { (context) in
// oriImg?.draw(in: CGRect(origin: .zero, size: size))
// })
}

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 }

// create a destination buffer
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)

// scale the image
error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
guard error == kvImageNoError else { return nil }

// create a CGImage from vImage_Buffer
var destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return nil }

// create a UIImage
let resizedImage = destCGImage.flatMap {
UIImage(cgImage: $0, scale: 0.0, orientation: oriImg.imageOrientation)
}

destCGImage = nil
return resizedImage
}