CGImage / UIImage ленивая загрузка на stream пользовательского интерфейса вызывает заикание

Моя программа отображает горизонтальную прокручиваемую поверхность, облицованную UIImageViews слева направо. Код работает в streamе пользовательского интерфейса, чтобы убедиться, что вновь видимые UIImageViews имеют недавно загруженный UIImage, назначенный им. Погрузка происходит в фоновом streamе.

Все работает почти отлично, за исключением того, что заикается, когда каждое изображение становится видимым. Сначала я подумал, что мой фоновый работник блокировал что-то в streamе пользовательского интерфейса. Я потратил много времени на это, и в конце концов понял, что UIImage делает некоторую дополнительную ленивую обработку в streamе пользовательского интерфейса, когда она становится видимой. Это меня озадачивает, так как мой рабочий stream имеет явный код для распаковки данных JPEG.

Во всяком случае, по догадке я написал код для визуализации во временный графический контекст на фоновом streamе и – конечно же, заикание ушло. UIImage теперь предварительно загружается в рабочий stream. Все идет нормально.

Проблема в том, что мой новый метод «ленивая загрузка изображения» ненадежен. Это вызывает прерывистый EXC_BAD_ACCESS. Я не знаю, что делает UIImage за кадром. Возможно, это распаковка данных JPEG. В любом случае, метод:

+ (void)forceLazyLoadOfImage: (UIImage*)image { CGImageRef imgRef = image.CGImage; CGFloat currentWidth = CGImageGetWidth(imgRef); CGFloat currentHeight = CGImageGetHeight(imgRef); CGRect bounds = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); CGAffineTransform transform = CGAffineTransformIdentity; CGFloat scaleRatioX = bounds.size.width / currentWidth; CGFloat scaleRatioY = bounds.size.height / currentHeight; UIGraphicsBeginImageContext(bounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextScaleCTM(context, scaleRatioX, -scaleRatioY); CGContextTranslateCTM(context, 0, -currentHeight); CGContextConcatCTM(context, transform); CGContextDrawImage(context, CGRectMake(0, 0, currentWidth, currentHeight), imgRef); UIGraphicsEndImageContext(); } 

И EXC_BAD_ACCESS происходит в строке CGContextDrawImage. ВОПРОС 1: Могу ли я сделать это в streamе, отличном от streamа пользовательского интерфейса? ВОПРОС 2: Что такое UIImage на самом деле «до загрузки»? ВОПРОС 3: Каков официальный способ решения этой проблемы?

Спасибо, что прочитали все это, любой совет будет очень благодарен!

UIGraphics* предназначены для UIGraphics* только из основного streamа. Вероятно, это источник вашей проблемы.

Вы можете заменить UIGraphicsBeginImageContext() вызовом CGBitmapContextCreate() ; это немного более активно (вам нужно создать цветовое пространство, определить нужный буфер для создания и выделить его самостоятельно). Методы CG* отлично подходят для работы из другого streamа.


Я не уверен, как вы инициализируете UIImage, но если вы делаете это с помощью imageNamed: или initWithFile: тогда вы можете заставить его загрузить загрузку данных самостоятельно, а затем вызвать initWithData: Заикание, вероятно, связано с ленивым вводом-выводом файлов, поэтому инициализация его объектом данных не даст ему возможности чтения из файла.

У меня была такая же проблема заикания, с некоторой помощью я выяснил правильное решение здесь: Загрузка неликого изображения в iOS

Два важных момента:

  • Не используйте методы UIKit в рабочем streamе. Вместо этого используйте CoreGraphics.
  • Даже если у вас есть фоновый stream для загрузки и распаковки изображений, вы все равно будете немного заикаться, если вы используете неправильную битовую маску для вашего CGBitmapContext. Это варианты, которые вам нужно выбрать (мне все еще немного непонятно):

 CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); 

Я разместил здесь пример проекта: SwapTest , он имеет примерно такую ​​же производительность, что и приложение «Яблоки» для загрузки / отображения изображений.

Я использовал категорию SwapTest UIImage @ jasamer, чтобы заставить загружать мой большой UIImage (около 3000×2100 пикселей) в рабочий stream (с NSOperationQueue). Это уменьшает время заикания при установке изображения в UIImageView до приемлемого значения (около 0,5 секунды на iPad1).

Вот категория SwapTest UIImage … еще раз спасибо @jasamer 🙂

Файл UIImage + ImmediateLoading.h

 @interface UIImage (UIImage_ImmediateLoading) - (UIImage*)initImmediateLoadWithContentsOfFile:(NSString*)path; + (UIImage*)imageImmediateLoadWithContentsOfFile:(NSString*)path; @end 

Файл UIImage + ImmediateLoading.m

 #import "UIImage+ImmediateLoading.h" @implementation UIImage (UIImage_ImmediateLoading) + (UIImage*)imageImmediateLoadWithContentsOfFile:(NSString*)path { return [[[UIImage alloc] initImmediateLoadWithContentsOfFile: path] autorelease]; } - (UIImage*)initImmediateLoadWithContentsOfFile:(NSString*)path { UIImage *image = [[UIImage alloc] initWithContentsOfFile:path]; CGImageRef imageRef = [image CGImage]; CGRect rect = CGRectMake(0.f, 0.f, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); CGContextRef bitmapContext = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little ); //kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little are the bit flags required so that the main thread doesn't have any conversions to do. CGContextDrawImage(bitmapContext, rect, imageRef); CGImageRef decompressedImageRef = CGBitmapContextCreateImage(bitmapContext); UIImage* decompressedImage = [[UIImage alloc] initWithCGImage: decompressedImageRef]; CGImageRelease(decompressedImageRef); CGContextRelease(bitmapContext); [image release]; return decompressedImage; } @end 

И вот как я создаю NSOpeationQueue и устанавливаю изображение в основной stream …

 // Loads low-res UIImage at a given index and start loading a hi-res one in background. // After finish loading, set the hi-res image into UIImageView. Remember, we need to // update UI "on main thread" otherwise its result will be unpredictable. -(void)loadPageAtIndex:(int)index { prevPage = index; //load low-res imageViewForZoom.image = [images objectAtIndex:index]; //load hi-res on another thread [operationQueue cancelAllOperations]; NSInvocationOperation *operation = [NSInvocationOperation alloc]; filePath = [imagesHD objectAtIndex:index]; operation = [operation initWithTarget:self selector:@selector(loadHiResImage:) object:[imagesHD objectAtIndex:index]]; [operationQueue addOperation:operation]; [operation release]; operation = nil; } // background thread -(void)loadHiResImage:(NSString*)file { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSLog(@"loading"); // This doesn't load the image. //UIImage *hiRes = [UIImage imageNamed:file]; // Loads UIImage. There is no UI updating so it should be thread-safe. UIImage *hiRes = [[UIImage alloc] initImmediateLoadWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType: nil]]; [imageViewForZoom performSelectorOnMainThread:@selector(setImage:) withObject:hiRes waitUntilDone:NO]; [hiRes release]; NSLog(@"loaded"); [pool release]; } 

У меня была та же проблема, хотя я инициализировал изображение, используя данные. (Я думаю, что данные загружаются лениво, тоже?) Мне удалось заставить декодировать, используя следующую категорию:

 @interface UIImage (Loading) - (void) forceLoad; @end @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); CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); CGContextRelease(context); } @end 
  • UIImage imageNamed требует pathForResource?
  • Изменение размера UI-изображений, снятых с камеры, также приводит к вращению UI-образа?
  • Создание UIImage из повернутого UIImageView
  • Получить пиксельный цвет UIImage
  • UIImage Сохранение изображения с именем файла на iPhone
  • didFinishPickingMediaWithInfo return nil фото
  • Как сравнить один образ с другим, чтобы увидеть, похожи ли они на определенный процент, на iPhone?
  • Как обрезать UIImage?
  • Как отобразить изображение base64 в UIImageView?
  • Могу ли я загрузить UIImage из URL-адреса?
  • Как создать цветной 1x1 UIImage на iPhone динамически?
  • Давайте будем гением компьютера.