【问题标题】:Decode images in background thread?在后台线程中解码图像?
【发布时间】:2011-04-23 16:55:40
【问题描述】:

我有一个后台线程来加载图像并在主线程中显示它们。我注意到后台线程几乎无事可做,因为实际的图像解码似乎是在主线程中完成的:

到目前为止,我已经尝试在后台线程中调用[UIImage imageNamed:][UIImage imageWithData:]CGImageCreateWithJPEGDataProvider,没有任何区别。有没有办法强制在后台线程上进行解码?

这里已经有a similar question,但它没有帮助。正如我在那里写的那样,我尝试了以下技巧:

@implementation UIImage (Loading)

- (void) forceLoad
{
    const CGImageRef cgImage = [self CGImage];  

    const int width = CGImageGetWidth(cgImage);
    const int height = CGImageGetHeight(cgImage);

    const CGColorSpaceRef colorspace = CGImageGetColorSpace(cgImage);
    const CGContextRef context = CGBitmapContextCreate(
        NULL, /* Where to store the data. NULL = don’t care */
        width, height, /* width & height */
        8, width * 4, /* bits per component, bytes per row */
        colorspace, kCGImageAlphaNoneSkipFirst);

    NSParameterAssert(context);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
    CGContextRelease(context);
}

@end

这有效(强制图像解码),但它也会触发对ImageIO_BGR_A_TO_RGB_A_8Bit 的显然昂贵的调用。

【问题讨论】:

    标签: iphone cocoa-touch uikit ios core-graphics


    【解决方案1】:

    我在新的 Retina iPad 上遇到了类似的高分辨率图像问题。大于屏幕尺寸(大致)的图像会导致 UI 响应能力出现重大问题。这些是 JPG,所以让它们在背景中解码似乎是正确的做法。我仍在努力收紧所有这些,但汤米的解决方案对我来说效果很好。我只是想贡献一些代码来帮助下一个人在他们试图确定为什么他们的 UI 与大图像结结巴巴时。这就是我最终做的事情(这段代码在后台队列的 NSOperation 中运行)。该示例是我的代码和上面代码的混合:

      CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)self.data);
      CGImageRef newImage = CGImageCreateWithJPEGDataProvider(dataProvider,
                                        NULL, NO, 
                                        kCGRenderingIntentDefault);
    
    
      //////////
      // force DECODE
    
      const int width = CGImageGetWidth(newImage);
      const int height = CGImageGetHeight(newImage);
    
      const CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
      const CGContextRef context = CGBitmapContextCreate(
                                                         NULL, /* Where to store the data. NULL = don’t care */
                                                         width, height, /* width & height */
                                                         8, width * 4, /* bits per component, bytes per row */
                                                         colorspace, kCGImageAlphaNoneSkipFirst);
    
      NSParameterAssert(context);
      CGContextDrawImage(context, CGRectMake(0, 0, width, height), newImage);
      CGImageRef drawnImage = CGBitmapContextCreateImage(context);
      CGContextRelease(context);
      CGColorSpaceRelease(colorspace);
    
      //////////
    
      self.downloadedImage = [UIImage imageWithCGImage:drawnImage];
    
      CGDataProviderRelease(dataProvider);
      CGImageRelease(newImage);
      CGImageRelease(drawnImage);
    

    我仍在优化它。但到目前为止,它似乎做得很好。

    【讨论】:

      【解决方案2】:

      正式来说,UIKit 不是线程安全的(尽管我相信它在 iOS 4 中变得更安全了),所以你不应该在后台线程上调用任何一个 UIImage 方法。在实践中,我认为如果你不做任何导致 UIKit 对象想要重绘的事情,你往往不会遇到问题,但这是你在交付代码中永远不应该依赖的经验法则——我提到它只是为了解释原因你可能没遇到过问题。

      正如您所发现的,CGImageRefs(包括包装在 UIImage 中时)会继续以其编码形式引用源图像,除非并且直到有人需要对它们进行解码。您引用的“技巧”可以有效触发解码;更好的解决方案是完全避免 UIImage,直到您安全返回主线程,从 CGImageCreateWithJPEGDataProvider 开始,像上面一样创建一个单独的上下文,将 JPEGDataProvider 中的 CGContextDrawImage 传递给新的上下文,然后将新的上下文作为图像,释放 JPEG 数据提供者。

      关于 ImageIO_BGR_A_TO_RGB_A_8Bit,可能值得用 CGColorSpaceCreateDeviceRGB() 代替 CGImageGetColorSpace(cgImage),以获取不依赖于下面源图像文件的颜色空间的上下文。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-24
        • 1970-01-01
        • 2017-03-03
        • 2013-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多