如何使用UIKit更快地渲染图像

时间:2015-04-20 08:29:12

标签: ios uiimageview

我正在制作一个iOS应用程序,其中有一个用多个UIImageViews切换大量图片的过程(用一堆图像设置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 个答案:

答案 0 :(得分:0)

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

  1. 使用CoreImage中的CIFilter

  2. 使用GPUImage库

  3. 如果您使用CoreImage,请检查您是否正确使用了CIFilter和CIContext。 CIContext创建需要花费很多时间,但在不同的CIF过滤器和图像之间可能是 SHARED - 所以你应该只创建一次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进行图像生成。