2010-10-11 11 views
10

Tengo un hilo de fondo que carga imágenes y las muestra en el hilo principal. Me di cuenta de que el subproceso de fondo tiene casi nada que hacer, ya que la decodificación de imagen real parece estar hecho en el hilo principal:Decodifica imágenes en el hilo de fondo?

alt text

Hasta ahora he intentado llamar [UIImage imageNamed:], [UIImage imageWithData:] y CGImageCreateWithJPEGDataProvider en el fondo hilo sin diferencia ¿Hay alguna manera de forzar la decodificación en el hilo de fondo?

Ya hay a similar question aquí, pero no ayuda. Como he escrito allí, probé el siguiente truco:

@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 

que funciona (obliga a la imagen para decodificar), pero también desencadena una llamada al parecer costoso ImageIO_BGR_A_TO_RGB_A_8Bit.

Respuesta

7

Oficialmente hablando, UIKit no es seguro para subprocesos (aunque se ha vuelto un poco más seguro en iOS 4, creo), por lo que no deberías estar llamando a ninguno de los métodos de UIImage en un hilo de fondo. En la práctica, creo que tiendes a no atacar los problemas si no haces nada que haga que un objeto UIKit quiera volver a dibujar, pero esa es una regla general de la que nunca deberías depender en el código de envío. Lo menciono solo para explicar por qué probablemente no has tenido un problema.

Como ha visto, CGImageRefs (incluso cuando está envuelto en un UIImage) continúa haciendo referencia a las imágenes fuente en su forma codificada a menos y hasta que alguien las necesite decodificadas. El 'truco' que cita es efectivo para activar una decodificación; una mejor solución es evitar UIImage completamente hasta que esté nuevamente a salvo en el hilo principal, comience con CGImageCreateWithJPEGDataProvider, cree un contexto separado como lo hace arriba, CGContextDrawImage desde JPEGDataProvider al nuevo contexto y luego pase el nuevo contexto como el imagen, liberando el proveedor de datos JPEG.

Con respecto a ImageIO_BGR_A_TO_RGB_A_8Bit, puede ser vale la pena dar CGColorSpaceCreateDeviceRGB() un ir en lugar de CGImageGetColorSpace (CGImage), para obtener un contexto con un espacio de color que no depende de la fuente de archivo de imagen debajo.

10

Me encontré con problemas similares con imágenes de alta resolución en el nuevo iPad retina. Las imágenes más grandes que el tamaño de la pantalla (aproximadamente) causarían problemas importantes con la capacidad de respuesta de la IU. Estos eran JPG, por lo que hacer que decodificaran en el fondo parecía ser lo correcto. Todavía estoy trabajando en apretar todo esto, pero la solución de Tommy funcionó muy bien para mí. Solo quería contribuir con algún código para ayudar a la siguiente persona cuando intentan identificar por qué su interfaz de usuario tartamudea con imágenes grandes. Esto es lo que terminé haciendo (este código se ejecuta en una NSOperation en una cola de fondo). El ejemplo es una mezcla de mi código y el código anterior:

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); 

Todavía estoy optimizando esto. Pero parece funcionar bastante bien hasta ahora.

+0

gracias, funciona genial. – Morrowless

Cuestiones relacionadas