Декодировать изображения в фоновом потоке? - PullRequest
11 голосов
/ 11 октября 2010

У меня есть фоновый поток, который загружает изображения и отображает их в основном потоке. Я заметил, что фоновый поток почти не имеет ничего общего, потому что фактическое декодирование изображения, похоже, выполняется в основном потоке:

alt text

До сих пор я пытался вызвать [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.

Ответы [ 2 ]

11 голосов
/ 16 июня 2012

Я столкнулся с похожими проблемами с изображениями высокого разрешения на новом сетчатке iPad. Изображения, размер которых превышает размер экрана (примерно), могут вызвать серьезные проблемы с отзывчивостью пользовательского интерфейса. Это были файлы JPG, поэтому правильное выполнение их декодирования на заднем плане. Я все еще работаю над ужесточением всего этого, но решение Томми отлично сработало для меня. Я просто хотел предоставить некоторый код, чтобы помочь следующему человеку, когда он пытается определить, почему его пользовательский интерфейс заикается с большими изображениями. Вот что я в итоге сделал (этот код выполняется в 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);

Я все еще оптимизирую это. Но, похоже, пока все идет хорошо.

7 голосов
/ 14 октября 2010

Официально UIKit не является потокобезопасным (хотя, по-моему, он стал немного безопаснее в iOS 4), поэтому не следует вызывать ни один из методов UIImage в фоновом потоке.На практике я думаю, что вы склонны не сталкиваться с проблемами, если вы не делаете ничего, что заставляет объект UIKit захотеть перерисовать, но это правило большого пальца, на которое вы никогда не должны полагаться в отгрузочном коде - я упоминаю это просто, чтобы объяснить, почемуу вас, вероятно, не было проблем.

Как вы заметили, CGImageRefs (в том числе и в оболочке UIImage) продолжают ссылаться на исходные изображения в их закодированной форме до тех пор, пока кто-то не нуждается в их декодировании.«Уловка», которую вы цитируете, эффективна для запуска декодирования;Лучшее решение - полностью избежать UIImage, пока вы не вернетесь в основной поток, начните с CGImageCreateWithJPEGDataProvider, создайте отдельный контекст, как вы делали выше, CGContextDrawImage из JPEGDataProvider в новый контекст, а затем передайте новый контекст какimage, освобождая поставщика данных JPEG.

Что касается ImageIO_BGR_A_TO_RGB_A_8Bit, возможно, стоит дать CGColorSpaceCreateDeviceRGB () вместо CGImageGetColorSpace (cgImage), чтобы получить контекст с цветовым пространством, которое не зависитв файле исходного изображения внизу.

...