抗锯齿、UIImage 和性能

Anti Alias, UIImage and Performance

我有一个 UIImage 加载到 UIImageViewUIImageUIImageView 大,并且已按比例缩小以适合。显然按比例缩小 UIImage 显示锯齿状边缘。

在性能方面消除此图像锯齿的最佳方法是什么?

我看过this method using drawInRect but I've also read that drawInRect does not give the best performance

我已经阅读了几篇不同的文章,并且我自己尝试了一些方法。但是在阅读了关于使用 UIViews 和 Core Graphics 之间的性能差异的 few more posts 之后,我想知道哪种抗锯齿方法的图像性能最好?

最好的解决方案是始终在您的 UIImageView 中使用正确的图像尺寸。但是,如果你不能得到正确的图像大小,而你需要调整它的大小,另一个好的解决方案是使用 CoreGraphics 在主线程之外执行图像缩放操作。

自 SDK 4.0 起,CoreGraphics operations are thread safe,因此您可以将所有调整大小的内容放入后台队列并在其中处理调整大小。调整大小完成后,您必须在主线程的 UIImageView 中分配裁剪后的图像,因为所有 UIKit 内容都必须在该线程中完成。使用这种方法,您不会在每次调整图像大小时都阻塞主线程。

完成后,您还可以缓存裁剪结果,以避免重复裁剪计算(即每次滚动到相同的UITableViewCell)并提高性能。

你可以把这个实现成一个UIImage类,以我的代码为例:

- (void)resizeImageWithSize:(CGSize)size
                   cacheKey:(NSString *)cacheKey
            completionBlock:(void (^)(UIImage *croppedImage))completionBlock
{
    dispatch_async([[self class] sharedBackgroundQueue], ^{
        // Check if we have the image cached
        UIImage *resizedImage = [[[self class] resizedImageCache] objectForKey:cacheKey];
        if (nil == resizedImage) {
            // If not, resize and cache it
            @autoreleasepool {
               resizedImage = [self resizeImageWithSize:size];
               [[[self class] resizedImageCache] setObject:resizedImage forKey:cacheKey];
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            completionBlock(resizedImage);
        });
    });
}

然后,resizeImageWithSize: 方法实现是所有 CoreGraphics 东西发生的地方。您可能会对 Nick Lockwood 的 FXImageView 库感兴趣,它使用相同的方法:UIImageView 类别,具有调整大小缓存并使用后台线程来执行 Core Graphics 内容。

调查可用列表 Core Image Filters。具体来说,可通过 CILanczosScaleTransform 获得的 Lanczos Scale Transform 似乎正是您所需要的。它应该适用于所有 iOS 版本 >= 6.0.

通常,使用 Core Image 滤镜比手动使用 Core Graphics 的性能更高。但是,我敦促您在特定情况下验证结果和性能。

由于您在评论中询问了 Swift:

Alamofire 内置了所有这些,包括一个自动缓存层。如果您不将其用于网络请求,至少它是一个很好的工作示例。

示例:

// Note: this seamlessly handles both scaling AND caching the scaled image
let filter = AspectScaledToFillSizeFilter(size: imageView.frame)
imageView.af_setImage(withURL: url, filter: filter)

只需确保事先设置了 imageView.frame(例如,首先为自动布局调用 layoutIfNeeded()),否则它会断言。