Rounding
首先,确保在缩放之前对尺寸进行四舍五入。drawInRect:
在这种情况下可以模糊原本可用的图像。要舍入到最接近的整数值:
size.width = truncf(size.width);
size.height = truncf(size.height);
对于某些任务,您可能需要向下舍入 (floorf) 或向上舍入 (ceilf)。
CILanczosScaleTransform 不可用
然后,忽略我之前推荐的 CILanczosScaleTransform。虽然 Core Image 的部分功能在 iOS 5.0 中可用,但 Lanczos 缩放功能却不可用。如果它确实可用,请利用它。对于使用 Mac OS 的人来说,它是可用的,可以使用它。
虚拟图像缩放
然而,有一个高质量的缩放算法可用vImage。下图显示了使用它的方法 (vImageScaledImage) 与不同上下文插值选项的比较。另请注意这些选项在不同缩放级别的行为有何不同。
On this diagram, it preserved the most line detail:
![Scaling comparison on diagram](https://i.stack.imgur.com/cJils.png)
On this photograph, compare the leaves at lower left:
![Scaling comparison on tree photograph](https://i.stack.imgur.com/K9nBb.png)
On this photograph, compare the textures in lower right:
![Scaling comparison on rock photograph](https://i.stack.imgur.com/yz7oN.png)
Do not use it on pixel art; it creates odd scaling artifacts:
![Scaling comparison on pixel art, showing scaling artifacts](https://i.stack.imgur.com/blNTU.png)
Although it on some images it has interesting rounding effects:
![Scaling comparison on Space Invader](https://i.stack.imgur.com/3PZci.png)
表现
毫不奇怪,kCGImageInterpolationHigh 是最慢的标准图像插值选项。此处实现的 vImageScaledImage 仍然较慢。将分形图像缩小到原始大小的一半,花费了 UIImageInterpolationHigh 的 110% 的时间。缩小到四分之一,需要 340% 的时间。
如果你在模拟器中运行它,你可能会有不同的想法;在那里,它可以比 kCGImageInterpolationHigh 快得多。据推测,vImage 多核优化使其在桌面上具有相对优势。
Code
// Method: vImageScaledImage:(UIImage*) sourceImage withSize:(CGSize) destSize
// Returns even better scaling than drawing to a context with kCGInterpolationHigh.
// This employs the vImage routines in Accelerate.framework.
// For more information about vImage, see https://developer.apple.com/library/mac/#documentation/performance/Conceptual/vImage/Introduction/Introduction.html#//apple_ref/doc/uid/TP30001001-CH201-TPXREF101
// Large quantities of memory are manually allocated and (hopefully) freed here. Test your application for leaks before and after using this method.
- (UIImage*) vImageScaledImage:(UIImage*) sourceImage withSize:(CGSize) destSize;
{
UIImage *destImage = nil;
if (sourceImage)
{
// First, convert the UIImage to an array of bytes, in the format expected by vImage.
// Thanks: http://stackoverflow.com/a/1262893/1318452
CGImageRef sourceRef = [sourceImage CGImage];
NSUInteger sourceWidth = CGImageGetWidth(sourceRef);
NSUInteger sourceHeight = CGImageGetHeight(sourceRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *sourceData = (unsigned char*) calloc(sourceHeight * sourceWidth * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger sourceBytesPerRow = bytesPerPixel * sourceWidth;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(sourceData, sourceWidth, sourceHeight,
bitsPerComponent, sourceBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, sourceWidth, sourceHeight), sourceRef);
CGContextRelease(context);
// We now have the source data. Construct a pixel array
NSUInteger destWidth = (NSUInteger) destSize.width;
NSUInteger destHeight = (NSUInteger) destSize.height;
NSUInteger destBytesPerRow = bytesPerPixel * destWidth;
unsigned char *destData = (unsigned char*) calloc(destHeight * destWidth * 4, sizeof(unsigned char));
// Now create vImage structures for the two pixel arrays.
// Thanks: https://github.com/dhoerl/PhotoScrollerNetwork
vImage_Buffer src = {
.data = sourceData,
.height = sourceHeight,
.width = sourceWidth,
.rowBytes = sourceBytesPerRow
};
vImage_Buffer dest = {
.data = destData,
.height = destHeight,
.width = destWidth,
.rowBytes = destBytesPerRow
};
// Carry out the scaling.
vImage_Error err = vImageScale_ARGB8888 (
&src,
&dest,
NULL,
kvImageHighQualityResampling
);
// The source bytes are no longer needed.
free(sourceData);
// Convert the destination bytes to a UIImage.
CGContextRef destContext = CGBitmapContextCreate(destData, destWidth, destHeight,
bitsPerComponent, destBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big);
CGImageRef destRef = CGBitmapContextCreateImage(destContext);
// Store the result.
destImage = [UIImage imageWithCGImage:destRef];
// Free up the remaining memory.
CGImageRelease(destRef);
CGColorSpaceRelease(colorSpace);
CGContextRelease(destContext);
// The destination bytes are no longer needed.
free(destData);
if (err != kvImageNoError)
{
NSString *errorReason = [NSString stringWithFormat:@"vImageScale returned error code %d", err];
NSDictionary *errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
sourceImage, @"sourceImage",
[NSValue valueWithCGSize:destSize], @"destSize",
nil];
NSException *exception = [NSException exceptionWithName:@"HighQualityImageScalingFailureException" reason:errorReason userInfo:errorInfo];
@throw exception;
}
}
return destImage;
}