大型UIImage绘制后,CGContextDrawImage非常慢

typ*_*ter 28 performance core-graphics ios 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]);


}
Run Code Online (Sandbox Code Playgroud)

所以我想问题是,#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绘制到该上下文中的全帧是快速的(.17秒)4)重复实验但是用支持UIImage的绘制CGImage填充上下文.随后的全帧图像绘制为6秒以上.SLOWWWWW.

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

typ*_*ter 41

经过一段时间的实验,我认为我找到了处理这种情况的最快方法.上面的绘图操作现在需要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对象.在内存中闲逛的东西似乎会产生奇怪的减速情况,这可能是因为存在与这些强引用相关的回调或检查.只是一个猜测,但我可以说,尽快清理记忆有助于提高性能.


Abs*_*ion 13

我遇到了类似的问题.我的应用程序必须重新绘制几乎与屏幕大小一样大的图片.问题在于尽可能快地绘制两个相同分辨率的图像,既不旋转也不翻转,而是每次都缩放并定位在屏幕的不同位置.毕竟,我在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. 如果您可以避免在实际绘图时缩放图像,请尝试这样做.绘制非缩放图像要快得多 - 不幸的是,对我来说这不是一个选择.