iphone - framework - Descompresión de UIImage causando retraso de desplazamiento
swift ios documentation (3)
El consejo de Jason sobre el dibujo previo de la imagen para descomprimirlo es la clave, pero obtendrá un rendimiento aún mejor si copia la imagen completa y descarta el original.
Las imágenes creadas en el tiempo de ejecución en iOS parecen estar mejor optimizadas para dibujar que las que se han cargado desde un archivo, incluso después de haberlas forzado a descomprimir. Por esa razón, debería cargar así (también es una buena idea colocar la imagen descomprimida en un NSCache para que no tenga que volver a cargarla):
- (void)loadImageWithPath:(NSString *)path block:(void(^)(UIImage *image))block
{
static NSCache *cache = nil;
if (!cache)
{
cache = [[NSCache alloc] init];
}
//check cache first
UIImage *image = [cache objectForKey:path];
if (image)
{
block(image);
return;
}
//switch to background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//load image
UIImage *image = [UIImage imageWithContentsOfFile:path];
//redraw image using device context
UIGraphicsBeginImageContextWithOptions(image.size, YES, 0);
[image drawAtPoint:CGPointZero];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//back to main thread
dispatch_async(dispatch_get_main_queue(), ^{
//cache the image
[cache setObject:image forKey:path];
//return the image
block(image);
});
});
}
Tengo esta aplicación con una tabla de pantalla completa que muestra un montón de imágenes diminutas. Esas imágenes se extraen de la web, se procesan en un hilo de fondo y luego se guardan en el disco utilizando algo como:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
// code that adds some glosses, shadows, etc
UIImage *output = UIGraphicsGetImageFromCurrentImageContext();
NSData* cacheData = UIImagePNGRepresentation(output);
[cacheData writeToFile:thumbPath atomically:YES];
dispatch_async(dispatch_get_main_queue(), ^{
self.image = output; // refreshes the cell using KVO
});
});
Este código solo se ejecuta la primera vez que se muestra la celda (ya que después la imagen ya está en el disco). En ese caso, la celda se carga utilizando:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *savedImage = [UIImage imageWithContentsOfFile:thumbPath];
if(savedImage) {
dispatch_async(dispatch_get_main_queue(), ^{
self.image = savedImage; // refreshes the cell using KVO
});
}
});
Mi problema es que en el primer caso, el desplazamiento es suave como la mantequilla. Pero en el segundo caso (donde se lee la imagen directamente desde el disco), el desplazamiento es súper desigual, incluso una vez que se carga la imagen. Dibujar es lo que está causando el retraso. Al usar Instruments, veo que copyImageBlockSetPNG
, png_read_now
e png_read_now
están ocupando la mayor parte de la CPU (no lo están al asignar self.image
a UIGraphicsGetImageFromCurrentImageContext()
)
Supongo que esto sucede porque en el primer caso el UIImage es una salida en bruto del dibujo, mientras que en el segundo caso tiene que descomprimir el PNG cada vez que lo está dibujando. Intenté usar JPG en lugar de PNG y obtengo resultados similares.
¿Hay una manera de solucionar esto? ¿Tal vez tener solo descomprimir el PNG la primera vez que se dibuja?
Otra forma de descompresión de la imagen:
NS_INLINE void forceImageDecompression(UIImage *image)
{
CGImageRef imageRef = [image CGImage];
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef), 8, CGImageGetWidth(imageRef) * 4, colorSpace,kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
CGColorSpaceRelease(colorSpace);
if (!context) { NSLog(@"Could not create context for image decompression"); return; }
CGContextDrawImage(context, (CGRect){{0.0f, 0.0f}, {CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}}, imageRef);
CFRelease(context);
}
Utilizando:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%u.jpg", pageIndex]];
forceImageDecompression(image);
dispatch_async(dispatch_get_main_queue(), ^{
[((UIImageView*)page)setImage:image];
});
}
Su problema es que +imageWithContentsOfFile:
está en caché y se carga de forma perezosa. Si desea hacer algo como esto, use este código en su cola de fondo:
// Assuming ARC
NSData* imageFileData = [[NSData alloc] initWithContentsOfFile:thumbPath];
UIImage* savedImage = [[UIImage alloc] initWithData:imageFileData];
// Dispatch back to main queue and set image...
Ahora, con este código, la descompresión real de los datos de la imagen seguirá siendo perezosa y costará un poco, pero no tanto como el impacto de acceso al archivo que se obtiene con la carga diferida en su ejemplo de código.
Ya que todavía estás viendo un problema de rendimiento, también puedes forzar a UIImage a descomprimir la imagen en el hilo de fondo:
// Still on background, before dispatching to main
UIGraphicsBeginImageContext(CGSizeMake(100, 100)); // this isn''t that important since you just want UIImage to decompress the image data before switching back to main thread
[savedImage drawAtPoint:CGPointZero];
UIGraphicsEndImageContext();
// dispatch back to main thread...