2013-10-30 12 views
8

Nella mia app ho bisogno di caricare grandi immagini JPEG e visualizzarle in una vista di scorrimento. Per mantenere l'interfaccia utente reattiva, ho deciso di caricare le immagini sullo sfondo, quindi visualizzarle sul thread principale. Per caricarli completamente in background, costringo ogni immagine a essere decompressa. I ero utilizzando questo codice decomprimere un'immagine (si noti che la mia app è iOS solo il 7, così ho capito che l'utilizzo di questi metodi su un thread in background è OK):Perché questo codice decomprime un UIImage molto meglio dell'approccio ingenuo?

+ (UIImage *)decompressedImageFromImage:(UIImage *)image { 
    UIGraphicsBeginImageContextWithOptions(image.size, YES, 0); 
    [image drawAtPoint:CGPointZero]; 
    UIImage *decompressedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return decompressedImage; 
} 

Comunque, avevo ancora carico lungo tempi, balbuzie dell'interfaccia utente e molta pressione della memoria. Ho appena trovato another solution:

+ (UIImage *)decodedImageWithImage:(UIImage *)image { 
    CGImageRef imageRef = image.CGImage; 
    // System only supports RGB, set explicitly and prevent context error 
    // if the downloaded image is not the supported format 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    CGContextRef context = CGBitmapContextCreate(NULL, 
               CGImageGetWidth(imageRef), 
               CGImageGetHeight(imageRef), 
               8, 
               // width * 4 will be enough because are in ARGB format, don't read from the image 
               CGImageGetWidth(imageRef) * 4, 
               colorSpace, 
               // kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little 
               // makes system don't need to do extra conversion when displayed. 
               kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); 
    CGColorSpaceRelease(colorSpace); 

    if (! context) { 
     return nil; 
    } 
    CGRect rect = (CGRect){CGPointZero, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}; 
    CGContextDrawImage(context, rect, imageRef); 
    CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); 
    CGContextRelease(context); 
    UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef]; 
    CGImageRelease(decompressedImageRef); 
    return decompressedImage; 
} 

Questo codice è ordini di grandezza migliore. L'immagine viene caricata quasi immediatamente, non c'è la balbuzie dell'interfaccia utente e l'utilizzo della memoria è andato giù.

Quindi la mia domanda è duplice:

  1. Perché il secondo metodo molto meglio rispetto alla prima?
  2. Se il secondo metodo è migliore a causa dei parametri univoci del dispositivo, c'è un modo per garantire che funzionerà altrettanto bene per tutti i dispositivi iOS, presenti e futuri? Non vorrei assumere un formato bitmap nativo che cambi su di me, reintroducendo questo problema.

risposta

6

Suppongo che tu stia eseguendo su un dispositivo Retina. In UIGraphicsBeginImageContextWithOptions, hai chiesto la scala di default, che è la scala della schermata principale, che è 2. Ciò significa che sta generando una bitmap 4x come grande. Nella seconda funzione, stai disegnando su una scala 1x.

Provare a passare una scala da 1 a UIGraphicsBeginImageContextWithOptions e verificare se le prestazioni sono simili.

+0

Brillante. Sì, sto lavorando su un dispositivo retina, e sì, impostando scale = 1 mi dà prestazioni e memoria simili. Vado con il primo metodo ora, poiché mi aspetto che faccia la cosa giusta, a prescindere dalle caratteristiche del dispositivo. –

+0

Per ottenere il meglio da entrambi i mondi (velocità e bellezza, al costo di più codice), potresti voler visualizzare un rendering 1x veloce (o anche un rendering di 0,5x) e poi tornare e riempire un rendering retina in un secondo momento se l'immagine è ancora sullo schermo. Per un esempio, vedi https://github.com/iosptl/ios6ptl/tree/master/ch13/JuliaOp (questo è il capitolo 13 della Programmazione iOS 6 che spinge i limiti, sarà il capitolo 9 in iOS 7 PTL quando esce entro l'anno.) –

+0

Cambiare UIGraphicsBeginImageContextWithOptions to scale = 1 rende un rendering di immagini con una qualità molto scarsa durante il ridimensionamento. Il codice che utilizza CGImage esegue il rendering con alta qualità proprio come quando non viene utilizzata la decompressione forzata. Inoltre, UIGraphicsBeginImageContextWithOptions specificato opaco e non funziona con la trasparenza –

Problemi correlati