如何使用UIKit更快地渲染具有效果的图像



我正在制作一个iOS应用程序,它有一个用几个UIImageView切换大量图片的过程(一个用一堆图像设置UIImageView的图像属性的循环)。有时一些图像需要一些图形效果,比如乘法。

最简单的方法是使用CIFilter来做这件事,但问题是iOS上的CALayer不支持"filters"属性,所以你需要在设置"image"属性之前将效果应用于图像。但当你频繁刷新屏幕时,这真的太慢了。

所以接下来我尝试直接使用Core Graphics与UIGraphics上下文和kCGBlendModeMultiply相乘。这确实比使用CIFilter快得多,但由于在渲染图像之前必须应用乘法,因此在尝试渲染具有乘法效果的图像时,您仍然会感觉到程序运行速度比渲染正常图像慢。

我的猜测是,这两种方法的根本问题是,你必须用GPU处理对图像的效果,然后用CPU得到结果图像,最后用GPU渲染结果图像,这意味着CPU和GPU之间的数据传输浪费了很多时间,因此,我尝试将基类从UIImageView更改为UIView,并将CGGraphics上下文代码实现为drawRect方法,然后当我设置"image"属性时,我在didSet中调用setNeedsDisplay方法。但这不太好用。。。实际上,每次调用setNeedsDisplay时,程序都会变得慢得多,甚至比使用CIFilter还要慢,可能是因为有几个视图在显示。

我想我可能可以用OpenGL解决这个问题,但我想知道我是否只能用UIKit解决这个问题?

据我所知,您必须对不同的图像进行相同的更改。所以初始初始化的时间对您来说并不重要,但每个图像都应该尽快处理。首先,在后台队列/线程中生成新图像是非常关键的。有两种快速处理/生成图像的好方法:

  1. 使用CoreImage 中的CIFilter

  2. 使用GPUImage库

如果使用CoreImage,请检查是否正确使用了CIFilter和CIContext。CIContext的创建需要相当长的时间,但它可能在不同的CIFilters和映像之间被共享,因此您应该只创建一次CIContext!CIFilter也可以在不同的映像之间SHARED,但由于它不是线程安全的,因此每个线程都应该有一个单独的CIFilter。

在我的代码中,我有以下内容:

+ (UIImage*)roundShadowImageForImage:(UIImage*)image {
static CIFilter *_filter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
NSLog(@"CIContext and CIFilter generating...");
_context = [CIContext contextWithOptions:@{ kCIContextUseSoftwareRenderer: @NO,
kCIContextWorkingColorSpace : [NSNull null] }];
CIImage *roundShadowImage = [CIImage imageWithCGImage:[[self class] roundShadowImage].CGImage];
CIImage *maskImage = [CIImage imageWithCGImage:[[self class] roundWhiteImage].CGImage];
_filter = [CIFilter filterWithName:@"CIBlendWithAlphaMask" 
keysAndValues:
kCIInputBackgroundImageKey, roundShadowImage,
kCIInputMaskImageKey, maskImage, nil];
NSLog(@"CIContext and CIFilter are generated");
});
if (image == nil) {
return nil;
}
NSAssert(_filter, @"Error: CIFilter for cover images is not generated");
CGSize imageSize = CGSizeMake(image.size.width * image.scale, image.size.height * image.scale);
// CIContext and CIImage objects are immutable, which means each can be shared safely among threads
CIFilter *filterForThread = [_filter copy]; // CIFilter could not be shared between different threads.
CGAffineTransform imageTransform = CGAffineTransformIdentity;
if (!CGSizeEqualToSize(imageSize, coverSize)) {
NSLog(@"Cover image. Resizing image %@ to required size %@", NSStringFromCGSize(imageSize), NSStringFromCGSize(coverSize));
CGFloat scaleFactor = MAX(coverSide / imageSize.width, coverSide / imageSize.height);
imageTransform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
}
imageTransform = CGAffineTransformTranslate(imageTransform, extraBorder, extraBorder);
CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];
ciImage = [ciImage imageByApplyingTransform:imageTransform];
if (image.hasAlpha) {
CIImage *ciWhiteImage = [CIImage imageWithCGImage:[self whiteImage].CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CISourceOverCompositing"
keysAndValues:
kCIInputBackgroundImageKey, ciWhiteImage,
kCIInputImageKey, ciImage, nil];
[filterForThread setValue:filter.outputImage forKey:kCIInputImageKey];
}
else
{
[filterForThread setValue:ciImage forKey:kCIInputImageKey];
}
CIImage *outputCIImage = [filterForThread outputImage];
CGImageRef cgimg = [_context createCGImage:outputCIImage fromRect:[outputCIImage extent]];
UIImage *newImage = [UIImage imageWithCGImage:cgimg];
CGImageRelease(cgimg);
return newImage;
}

如果你仍然对速度不满意,试试GPUImage。它是一个非常好的库,它也非常快,因为它使用OpenGL生成图像。

相关内容

最新更新