33

似乎 CGContextDrawImage(CGContextRef, CGRect, CGImageRef) 在绘制由 CoreGraphics(即使用 CGBitmapContextCreateImage)创建的 CGImage 时比在绘制支持 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 对象快得多。oO

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

更新 5(第 2 天):在创建了比昨天回答的问题之后,我今天有了一些扎实的东西。我可以肯定地说的是:

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

我通过 1) 首先创建一个非 alpha 上下文 (1936x2592) 证明了这一点。2) 用随机颜色的 2x2 方块填充它。3) 将 CGImage 绘制到该上下文中的全帧速度很快(0.17 秒) 4) 重复实验,但使用支持 UIImage 的绘制 CGImage 填充上下文。随后的全帧图像绘制时间超过 6 秒。慢速万维网。

不知何故,使用(大)UIImage 绘制到上下文中会大大减慢所有后续绘制到该上下文中的速度。

4

2 回答 2

46

好吧,经过大量的实验,我想我已经找到了处理这种情况的最快方法。上面的绘图操作需要 6+ 秒现在 0.1 秒。是的。这是我发现的:

  • 使用像素格式均匀化您的上下文和图像!我提出的问题的根源归结为 UIImage 中的 CGImage 使用相同的像素格式作为我的上下文。因此快。CGImages 是一种不同的格式,因此速度很慢。使用 CGImageGetAlphaInfo 检查您的图像以查看它们使用的像素格式。我现在到处都在使用 kCGImageAlphaNoneSkipLast,因为我不需要使用 alpha。如果你没有在所有地方使用相同的像素格式,当将图像绘制到上下文中时,Quartz 将被迫为每个像素执行昂贵的像素转换。= 慢

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

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

于 2011-09-29T15:36:05.943 回答
15

我有一个类似的问题。我的应用程序必须重新绘制几乎与屏幕大小一样大的图片。问题归结为尽可能快地绘制两个相同分辨率的图像,既不旋转也不翻转,但每次都缩放并定位在屏幕的不同位置。毕竟,我能够在 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 取自这里
  5. 确保在创建上下文时从 CGColorSpaceCreateDeviceRGB() 等获得 CGColorSpaceRef。报告了一些性能问题,以获得固定的色彩空间,而不是请求设备的色彩空间。
  6. 从 UIImageView 继承视图类并简单地将 self.image 设置为从上下文创建的图像对我很有用。但是,如果您想这样做,请先阅读有关使用 UIImageView 的信息,因为它需要对代码逻辑进行一些更改(因为不再调用 drawRect:)。
  7. 如果您可以避免在实际绘图时缩放图像,请尝试这样做。绘制非缩放图像要快得多 - 不幸的是,对我来说这不是一个选择。
于 2013-04-22T09:04:50.317 回答