在后台线程解码图像?
我有一个后台线程加载图像,并显示在主线程中。 我注意到后台线程几乎没有任何关系,因为实际的图像解码似乎是在主线程中完成的:
到目前为止,我已经尝试在后台线程中调用[UIImage imageNamed:]
, [UIImage imageWithData:]
和CGImageCreateWithJPEGDataProvider
,没有区别。 有没有办法强制解码在后台线程上完成?
这里已经有类似的问题 ,但是没有帮助。 正如我在那里写的,我尝试了以下技巧:
@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
。
我遇到了类似的问题,在新的视网膜iPad的高分辨率图像。 大于屏幕尺寸(大致)的图像会导致UI响应性出现严重问题。 这些是JPG,所以让他们在背景上解码似乎是正确的做法。 我仍然在努力收紧所有这些,但汤米的解决scheme对我来说很好。 我只是想提供一些代码来帮助下一个人,当他们试图找出他们的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);
我仍然在优化这个。 但是到目前为止,这似乎performance得相当不错。
正式来说,UIKit不是线程安全的(尽pipe在iOS 4中它已经变得更安全了,我相信),所以你不应该在后台线程上调用任何一种UIImage方法。 在实践中,我认为如果你不做任何导致UIKit对象重绘的任何事情,你都不会遇到问题,但这是你在运输代码时永远不应该依赖的经验法则 – 我提到它只是为了解释为什么你可能没有问题。
正如你发现的那样,CGImageRefs(包括在UIImage中包装的时候)继续引用源代码图像,除非有人需要它们的解码。 您引用的“技巧”可以有效触发解码; 一个更好的解决scheme是完全避免UIImage,直到你安全地返回主线程,从CGImageCreateWithJPEGDataProvider开始,像上面那样创build一个单独的上下文,从JPEGDataProvider到新的上下文,创build一个CGContextDrawImage,然后将新的上下文作为图像,释放JPEG数据提供者。
就ImageIO_BGR_A_TO_RGB_A_8Bit而言,可能需要给CGColorSpaceCreateDeviceRGB()代替CGImageGetColorSpace(cgImage),以获得具有不依赖于源图像文件的色彩空间的上下文。