iOS中圖片相關(guān)的內(nèi)容非常多,iOS中的圖片相關(guān)的API也非常多,從UIKit中的UIImage,到CoreGraphic中CGImage,CoreImage中的CIImage.
通常情況下使用UIImage來縮放圖片大小,在UIImage有contentMode
屬性,會(huì)有多個(gè)枚舉屬性.ScaleAspectFit
,.ScaleAspectFill
,通過賴進(jìn)行
使圖片縮放改變大小
在進(jìn)行圖片的縮放的大小時(shí),首先需要了解到縮放的目標(biāo)大小.
直接通過縮放因子改變大小
最簡單的方式就是使用常量factor,去乘以圖像的原來大小.
let size = CGSize(width: image.size.width/2, height: image.size.height/2)
或者直接使用CGAffineTransform
改變transform
let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformScale(0.5,0.5))
不改變Aspect Ratio的縮放
如果要將一個(gè)圖片放到一個(gè)rect中,而不改變它原始的aspect ratio.可以使用AVFoundation
的AVMakeRectWithAspectRatioInsideRect
方法.
import AVFoundation
let rect = AVMakeRectWithAspectRatioInsideRect(image.size, imageView.bounds)
改變圖片大小的方法匯總
有多種方法可以改變圖片的大小,每種方法的性能不同.
UIGraphicsBeginImageContextWithOptions
和UIImage -drawInRect:
通常最高階的API是UIKit,使用UIImage然后使用Graphic Context實(shí)時(shí)繪出一個(gè)大小不同的圖片.可以使用UIGraphicsBeginImageContextWithOptions
和UIGraphicsGetImageFromCurrentImageContext
獲取縮略圖.
let image = UIImage(contentsOfFile: self.URL.absoluteString!)
let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIGraphicsBeginImageContextWithOptions()
會(huì)創(chuàng)建一個(gè)渲染context,然后使用原來的圖片draw.
- 第一個(gè)參數(shù)
size
就是縮小以后的圖片的大小. - 第二個(gè)參數(shù)
isOpaque
,用來描述圖片的alpha通道是否渲染.設(shè)置為false
表示完全不透明 - 第三個(gè)參數(shù)
scale
,就是dispaly scale factor.如果設(shè)置成0.0
那么就是main screen使用的(retina是2.0,iphone6p是3.0)
CGBitmapContextCreate
和CGContextDrawImage
使用的Core Graphic/Quartz 2D使用的是低階API,可以使用CGImage,使用CGBitmapContextCreate()
和CGBitmapContextCreateImage()
獲取縮略圖.
let cgImage = UIImage(contentsOfFile: self.URL.absoluteString!).CGImage
let width = CGImageGetWidth(cgImage) / 2
let height = CGImageGetHeight(cgImage) / 2
let bitsPerComponent = CGImageGetBitsPerComponent(cgImage)
let bytesPerRow = CGImageGetBytesPerRow(cgImage)
let colorSpace = CGImageGetColorSpace(cgImage)
let bitmapInfo = CGImageGetBitmapInfo(cgImage)
let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo.rawValue)
CGContextSetInterpolationQuality(context, kCGInterpolationHigh)
CGContextDrawImage(context, CGRect(origin: CGPointZero, size: CGSize(width: CGFloat(width), height: CGFloat(height))), cgImage)
let scaledImage = CGBitmapContextCreateImage(context).flatMap { UIImage(CGImage: $0) }
CGImageSourceCreateThumbnailAtIndex
也可以使用Image I/O的framework也可以用來縮放圖片大小.
import ImageIO
if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) {
let options: [NSString: NSObject] = [
kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
kCGImageSourceCreateThumbnailFromImageAlways: true
]
let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) }
}
使用CoreImage進(jìn)行Lanczos重新采樣
CoreImage中內(nèi)置Lanczos Resampling,具體的函數(shù)是CILanczosScaleTransform
filter.
let image = CIImage(contentsOfURL: self.URL)
let filter = CIFilter(name: "CILanczosScaleTransform")!
filter.setValue(image, forKey: "inputImage")
filter.setValue(0.5, forKey: "inputScale")
filter.setValue(1.0, forKey: "inputAspectRatio")
let outputImage = filter.valueForKey("outputImage") as! CIImage
let context = CIContext(options: [kCIContextUseSoftwareRenderer: false])
let scaledImage = UIImage(CGImage: self.context.createCGImage(outputImage, fromRect: outputImage.extent()))
在Accelerate中的vImage
使用Accelerat framework包括vImage
的圖像處理函數(shù).
let cgImage = UIImage(contentsOfFile: self.URL.absoluteString!).CGImage
// create a source buffer
var format = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: nil,
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.First.rawValue),
version: 0, decode: nil, renderingIntent: CGColorRenderingIntent.RenderingIntentDefault)
var sourceBuffer = vImage_Buffer()
defer {
sourceBuffer.data.dealloc(Int(sourceBuffer.height) * Int(sourceBuffer.height) * 4)
}
var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else { return nil }
// create a destination buffer
let scale = UIScreen.mainScreen().scale
let destWidth = Int(image.size.width * 0.5 * scale)
let destHeight = Int(image.size.height * 0.5 * scale)
let bytesPerPixel = CGImageGetBitsPerPixel(image.CGImage) / 8
let destBytesPerRow = destWidth * bytesPerPixel
let destData = UnsafeMutablePointer<UInt8>.alloc(destHeight * destBytesPerRow)
defer {
destData.dealloc(destHeight * destBytesPerRow)
}
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
let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return nil }
// create a UIImage
let scaledImage = destCGImage.flatMap { UIImage(CGImage: $0, scale: 0.0, orientation: image.imageOrientation) }
各種方式的選擇
- UIKit,CoreGraphics以及Image I/O的性能優(yōu)秀.如果僅僅是縮放的話最好使用CoreImage.
- 日常的圖像縮放以后不進(jìn)行其他操作的話,使用
UIGraphicsBeginImageContextWithOptions
是最好的選擇. - 如果對圖像質(zhì)量有更高的要求,最好使用
CGBitmapContextCreate
和CGContextSetInterpolationQuality
. - 如果縮放的目的是顯示縮略圖,那么最好使用
CGImageSourceCreateThumbnailAtIndex
- 縮放不要用
vImage