为什么?(iOS 5.0)

UIImage在缩放时总是变得模糊。如果要保持清晰,我该怎么办?

- (UIImage *)rescaleImageToSize:(CGSize)size { CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); UIGraphicsBeginImageContext(rect.size); [self drawInRect:rect]; // scales image to rect UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return resImage; } 

四舍五入

首先,确保在缩放之前将您的大小舍入。 drawInRect:在这种情况下可以模糊一个可用的图像。 舍入到最接近的整数值:

 size.width = truncf(size.width); size.height = truncf(size.height); 

对于某些任务,您可能需要舍入(floorf)或舍入(ceilf)。

CILanczosScaleTransform不可用

然后,忽略我以前的build议CILanczosScaleTransform。 尽pipeCore Image的部分内容在iOS 5.0中可用,但Lanczos缩放并不是。 如果它变得可用,就利用它。 对于在Mac OS上工作的人,可以使用它。

vImage缩放

但是, vImage中有一个高质量的缩放algorithm。 下面的图片显示了如何使用它的方法(vImageScaledImage)与不同的上下文插值选项进行比较。 还要注意这些选项在不同缩放级别下的行为方式。

在这张图上 ,它保留了最多的线条细节: 在图上缩放比较

在这张照片上 ,比较左下方的叶子: 缩放比较树照片

在这张照片上 ,比较右下方的纹理: 岩石照片缩放比较

不要在像素艺术上使用它; 它会创build奇怪的缩放工件: 像素艺术上的缩放比较,显示缩放伪像

虽然它在一些图像上具有有趣的圆整效果: 空间入侵者的缩放比较

性能

毫不奇怪,kCGImageInterpolationHigh是最慢的标准图像插值选项。 这里实现的vImageScaledImage仍然比较慢。 为了将分形图像缩小到其原始尺寸的一半,需要UIImageInterpolationHigh的110%。 缩小到四分之一,花了340%的时间。

你可能会认为,否则,如果你在模拟器中运行它; 在那里,它可以比kCGImageInterpolationHigh快得多。 据推测,vImage多核优化使其在桌面上具有相对优势。

 // 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; } 

我已经尝试过使用VImage的@Dondragmer答案,但是结果的质量还不够好(我以1/10的比例缩小了图像尺寸)。

这个解决scheme虽然为我工作: UIImage的drawInrect:平滑图像

基本上,这只是说,在视网膜显示器上,你需要创build与视网膜参数的graphics上下文:

 UIGraphicsBeginImageContextWithOptions(size, NO, 2.0f); 

试试看:

 CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationHigh); 

在绘制之前得到高质量的插值。

在放大图像的特殊情况下,想要保留任何尖angular,您需要closures图像插值。 如果你的图像是像素艺术,这是你想要的。 在大多数情况下,这不是。

 UIGraphicsBeginImageContext(rect.size); // Turn off interpolation to keep the scaled image from being blurred. CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationHigh); [self drawInRect:rect]; // scales image to rect UIImage *resImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

如果缩小比例,只需使用kCGInterpolationHigh绘图也不是最好的select。 这很好,但CILanczosScaleTransform稍微清晰。

编辑:从iOS 5.1开始,CILanczosScaleTransform不可用。 看到我的其他答案,您可以使用高品质的缩放例程。