大型UIImage绘制后,CGContextDrawImage非常慢

时间:2011-09-26 20:32:27

标签: ios performance core-graphics cgcontextdrawimage

CGContextDrawImage(CGContextRef,CGRect,CGImageRef)似乎在绘制由CoreGraphics(即使用CGBitmapContextCreateImage)创建的CGImage时执行MUCH WORSE,而不是在绘制支持UIImage的CGImage时绘制。请参阅此测试方法:

-(void)showStrangePerformanceOfCGContextDrawImage
{
    ///Setup : Load an image and start a context:
    UIImage *theImage = [UIImage imageNamed:@"reallyBigImage.png"];
    UIGraphicsBeginImageContext(theImage.size);
    CGContextRef ctxt = UIGraphicsGetCurrentContext();    
    CGRect imgRec = CGRectMake(0, 0, theImage.size.width, theImage.size.height);


    ///Why is this SO MUCH faster...
    NSDate * startingTimeForUIImageDrawing = [NSDate date];
    CGContextDrawImage(ctxt, imgRec, theImage.CGImage);  //Draw existing image into context Using the UIImage backing    
    NSLog(@"Time was %f", [[NSDate date] timeIntervalSinceDate:startingTimeForUIImageDrawing]);

    /// Create a new image from the context to use this time in CGContextDrawImage:
    CGImageRef theImageConverted = CGBitmapContextCreateImage(ctxt);

    ///This is WAY slower but why??  Using a pure CGImageRef (ass opposed to one behind a UIImage) seems like it should be faster but AT LEAST it should be the same speed!?
    NSDate * startingTimeForNakedGImageDrawing = [NSDate date];
    CGContextDrawImage(ctxt, imgRec, theImageConverted);
    NSLog(@"Time was %f", [[NSDate date] timeIntervalSinceDate:startingTimeForNakedGImageDrawing]);


}

所以我想问题是,#1可能导致这种情况,#2是否有解决方法,即创建可能更快的CGImageRef的其他方法?我意识到我可以先将所有内容转换为UIImages,但这是一个如此丑陋的解决方案。我已经有CGContextRef坐在那里。

更新:在绘制小图片时,这似乎不一定是真的吗?这可能是一个线索 - 当使用大图像(即全尺寸相机图片)时,该问题被放大。 640x480在任何一种方法的执行时间方面似乎非常相似

更新2:好的,所以我发现了一些新东西..它实际上并不是改变性能的CGImage的支持。我可以翻转2步的顺序,使UIImage方法表现得很慢,而“裸”的CGImage会超快。无论你在第二场比赛中表现出来都会遭遇糟糕的表现。这似乎就是这种情况,除非我通过在CGBitmapContextCreateImage创建的图像上调用CGImageRelease来释放内存。然后UIImage支持的方法随后会很快。反之则不然。是什么赋予了? “拥挤”的记忆不应该影响这样的表现,不是吗?

更新3:太快说了。之前的更新适用于尺寸为2048x2048的图像,但是踩到1936x2592(相机尺寸),裸CGImage方法仍然慢,无论操作顺序或内存情况如何。也许有一些CG内部限制使16MB图像有效,而21MB图像无法有效处理。它的绘制相机尺寸比2048x2048慢20倍。不知何故,UIImage提供的CGImage数据比纯CGImage对象快得多。 o.O

更新4:我认为这可能与某些内存缓存有关,但结果是相同的,无论UIImage是否加载了非缓存[UIImage imageWithContentsOfFile],就像使用了[UIImage imageNamed]一样。

更新5(第2天):在创建了比昨天回答的问题之后,我今天已经某事了。我可以肯定的是以下内容:

  • UIImage背后的CGImages不使用alpha。 (kCGImageAlphaNoneSkipLast)。我想也许他们绘制得更快,因为我的上下文使用alpha。所以我改变了上下文以使用kCGImageAlphaNoneSkipLast。这使得绘图更快,除非:
  • 使用UIImage FIRST绘制到CGContextRef中,使所有后续图像绘制变慢

我通过1)首先创建一个非alpha上下文(1936x2592)证明了这一点。 2)用随机着色的2x2正方形填充。 3)将CGImage绘制到该上下文中的全帧是快速的(.17秒)4)重复实验但是用支持UIImage的绘制CGImage填充上下文。随后的全帧图像绘制为6秒以上。 SLOWWWWW。

以某种方式绘制到具有(大)UIImage的上下文会大大减慢所有后续绘制到该上下文中的速度。

2 个答案:

答案 0 :(得分:40)

经过一段时间的实验,我认为我找到了处理这种情况的最快方法。上面的绘图操作现在需要6秒以上.1秒。是。这是我发现的:

  • 同化你的背景&像素格式的图像!我问的问题的根源归结为UIImage中的CGImages使用相同的像素格式作为我的上下文。因此快。 CGImages是一种不同的格式,因此很慢。使用CGImageGetAlphaInfo检查图像以查看它们使用的像素格式。我现在正在使用kCGImageAlphaNoneSkipLast,因为我不需要使用alpha。如果您不在任何地方使用相同的像素格式,则在将图像绘制到上下文中时,Quartz将被迫为每个像素执行昂贵的像素转换。 =慢速

  • 使用CGLayers!这些使得屏幕外绘图性能更好。这是如何工作的基本如下。 1)使用CGLayerCreateWithContext从上下文创建CGLayer。 2)在CGLayerGetContext获得的THIS LAYER上下文中绘制/设置绘图属性。从ORIGINAL上下文中读取任何像素或信息。 3)完成后,使用CGContextDrawLayerAtPoint将此CGLayer“标记”回原始上下文。只要您记住,这是快速的:

  • 1)释放从上下文创建的任何CGImages(即使用CGBitmapContextCreateImage创建的CGImages),然后使用CGContextDrawLayerAtPoint将图层“标记”回CGContextRef。绘制该图层时,这会使速度提高3-4倍。 2)保持你的像素格式在任何地方都一样! 3)尽可能清理CG对象。在内存中闲逛的东西似乎会产生奇怪的减速情况,这可能是因为存在与这些强引用相关的回调或检查。只是一个猜测,但我可以说尽快清理内存有助于提高性能。

答案 1 :(得分:12)

我有类似的问题。我的应用程序必须重新绘制几乎与屏幕大小一样大的图片。问题在于尽可能快地绘制两个相同分辨率的图像,既不旋转也不翻转,而是每次都缩放并定位在屏幕的不同位置。毕竟,我在iPad 1上获得了大约15-20 FPS,在iPad4上获得了大约20-25 FPS。所以...希望这有助于某人:

  1. 正如打字机所说,你必须使用相同的像素格式。使用AlphaNone可以提高速度。但更重要的是,在我的案例中,argb32_image调用会进行多次调用,将像素从ARGB转换为BGRA。所以对我来说最好的bitmapInfo值是(当时; Apple有可能在未来改变一些东西): const CGBitmabInfo g_bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast;
  2. 如果矩形参数是完整的(通过CGRectIntegral),CGContextDrawImage可以更快地工作。当图像按照接近1的系数缩放时,似乎会产生更大的效果。
  3. 使用图层实际上减慢了我的速度。自2011年以来,在一些内部电话中可能发生了一些变化。
  4. 设置低于默认值的上下文的插值质量(通过CGContextSetInterpolationQuality)非常重要。我建议使用(IS_RETINA_DISPLAY ? kCGInterpolationNone : kCGInterpolationLow)。宏IS_RETINA_DISPLAY取自here
  5. 确保在创建上下文时从CGColorSpaceCreateDeviceRGB()等获取CGColorSpaceRef。报告了一些性能问题,用于获取固定颜色空间,而不是请求设备的颜色空间。
  6. 从UIImageView继承视图类并简单地将self.image设置为从上下文创建的图像对我来说非常有用。但是,如果你想这样做,请先阅读有关使用UIImageView的内容,因为它需要对代码逻辑进行一些更改(因为不再调用drawRect:)。
  7. 如果您可以避免在实际绘图时缩放图像,请尝试这样做。绘制非缩放图像的速度要快得多 - 不幸的是,对我来说这不是一种选择。