2014-04-10 33 views
11

Problem dekompresji obrazu został znacznie omówiony w przepełnieniu stosu, ale aż do tego pytania było 0 wzmianek o kCGImageSourceShouldCacheImmediately, opcji wprowadzonej w iOS 7, która w teorii zajmuje się tym problemem. Z nagłówków:Dekompresja obrazu w iOS 7

Określa, czy dekodowanie obrazu i buforowanie powinno odbywać się w czasie tworzenia obrazu.

W Objc.io #7 Peter Steinberger zasugerował to podejście:

+ (UIImage *)decompressedImageWithData:(NSData *)data 
{ 
    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); 
    CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)@{(id)kCGImageSourceShouldCacheImmediately: @YES}); 

    UIImage *image = [UIImage imageWithCGImage:cgImage]; 
    CGImageRelease(cgImage); 
    CFRelease(source); 
    return image; 
} 

Biblioteki jak AFNetworking i SDWebImage jeszcze zrobić zdjęcie dekompresję z metodą CGContextDrawImage. Od SDWebImage:

+ (UIImage *)decodedImageWithImage:(UIImage *)image { 
    if (image.images) { 
     // Do not decode animated images 
     return image; 
    } 

    CGImageRef imageRef = image.CGImage; 
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); 
    CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize}; 

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); 

    int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask); 
    BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || 
      infoMask == kCGImageAlphaNoneSkipFirst || 
      infoMask == kCGImageAlphaNoneSkipLast); 

    // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB. 
    // https://developer.apple.com/library/mac/#qa/qa1037/_index.html 
    if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) { 
     // Unset the old alpha info. 
     bitmapInfo &= ~kCGBitmapAlphaInfoMask; 

     // Set noneSkipFirst. 
     bitmapInfo |= kCGImageAlphaNoneSkipFirst; 
    } 
      // Some PNGs tell us they have alpha but only 3 components. Odd. 
    else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) { 
     // Unset the old alpha info. 
     bitmapInfo &= ~kCGBitmapAlphaInfoMask; 
     bitmapInfo |= kCGImageAlphaPremultipliedFirst; 
    } 

    // It calculates the bytes-per-row based on the bitsPerComponent and width arguments. 
    CGContextRef context = CGBitmapContextCreate(NULL, 
      imageSize.width, 
      imageSize.height, 
      CGImageGetBitsPerComponent(imageRef), 
      0, 
      colorSpace, 
      bitmapInfo); 
    CGColorSpaceRelease(colorSpace); 

    // If failed, return undecompressed image 
    if (!context) return image; 

    CGContextDrawImage(context, imageRect, imageRef); 
    CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); 

    CGContextRelease(context); 

    UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation]; 
    CGImageRelease(decompressedImageRef); 
    return decompressedImage; 
} 

Moje pytanie brzmi, powinniśmy przejść do podejścia kCGImageSourceShouldCacheImmediately w iOS 7?

+1

Nota prawna: Jestem autorem Haneke, alternatywy dla SDWebImage i proszę o to, aby poinformować, jak dekodować obrazy w mojej bibliotece. – hpique

+0

nie jest jasne, jaki jest problem, który wywołał to pytanie? Dlaczego się tym martwisz? – SpaceDog

Odpowiedz

-2

można spróbować tego kodu poniżej:

+(NSData *)imageData:(UIImage *)image 
{ 
    //1.0 == 100% 
    return UIImageJPEGRepresentation(image, 0.7); 
} 

Cheers!

0

może być w ten sposób:

+ (UIImage *)imageFromURL:(NSURL *)url { 
    UIImage *result = nil; 
    if ([url isFileURL]) { 
     CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL); 
     if (source) { 
      NSDictionary * attributes = @{ (id)kCGImageSourceShouldCache : @YES }; 
      CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)attributes); 
      if (cgImage) { 
       result = [UIImage imageWithCGImage:cgImage]; 
       CGImageRelease(cgImage); 
      } 
      CFRelease(source); 
     } 
    } 
    return result; 
} 
3

Istnieje kilka problemów związanych z realizacją miarę mogę powiedzieć.

  1. Ta "nowa metoda" wymaga pewnego rodzaju renderowania w głównym wątku. Możesz załadować obraz i ustawić flagę powinno natychmiast flagować, ale to ustawi pewne operacje w głównym wątku do przetworzenia. Spowoduje to zająknięcie podczas ładowania widoków przewijania i widoków kolekcji. Zaczyna się dla mnie bardziej niż na starej drodze z koleją wysyłkową w tle.

  2. Jeśli używasz własnych buforów pamięci zamiast plików, musisz utworzyć dostawców danych, którzy kopiują dane, ponieważ wygląda na to, że dostawcy danych oczekują od buforów pamięci. To brzmi banalnie, ale flagi w tej funkcji ołowiu Ci, że można to zrobić:

    • wypełnić swój własny bufor sprężonym danych JPEG z jakiegoś źródła
    • stworzyć dostawcy danych i dołączyć dane w formacie JPEG do niego
    • utworzyć źródło obrazu z dostawcą danych z CACHE niezwłocznie
    • wykorzystywać źródła obrazu aby utworzyć obraz CG
    • rzut wszystkie pośrednie obiekty z dala i przekazać swoją ładnie rozpakowane CGImage obiektu aż do UIImage obiekt gotowy do przewijanie

To nie zrobić chociaż, ponieważ będzie ona czekać na głównym wątku, gdzie Dekompresja odbędzie. Uważa, że ​​wszystko jest w porządku, ponieważ zawiera odniesienia do wszystkich tych obiektów pośredniczących, które zostały wydane. Wydałeś wszystkie te obiekty, myśląc, że NATYCHMIAST rozpakował się tak, jak powiedziały flagi. Jeśli wyrzucisz również ten bufor pamięci i bufor pamięci zostanie przekazany w trybie bez kopiowania, skończysz z śmieciami.Lub, jeśli bufor pamięci został ponownie użyty, tak jak w moim przypadku, aby załadować kolejny obraz, również dostaniesz śmieci.

Nie możesz w żaden sposób dowiedzieć się, kiedy obraz zostanie zdekompresowany i gotowy do użycia.

TL; DR = "uważają kCGImageSourceShouldCacheImmediately oznaczać kiedy dogodna do OS"

Kiedy robisz to "stary sposób", to 100%, co będzie dostępne i kiedy. Ponieważ nie jest to odroczenie, możesz uniknąć kopiowania. Nie sądzę, że ten API i tak robi coś magicznego, myślę, że to po prostu trzyma bufor pamięci, a następnie robi rzeczy "w starym stylu" pod maską.

W zasadzie nie ma tu bezpłatnego lunchu. Patrząc na ślad stosu z miejsca, w którym to się zdarzyło, gdy ponownie użyłem bufora pamięci po tym, jak myślałem, że wszystko zostało podpisane, widzę, że to wywołuje CA: Transaction, CA :: Layer, CA :: Render, i od tam do ImageProviderCopy ... aż do JPEGParseJPEGInfo (gdzie rozbił się dostęp do mojego bufora).

Oznacza to, że kCGImageSourceShouldCacheImmediately robi nic wyjątkiem ustawić flagę powiedzieć obraz rozpakować w głównym wątku jak najszybciej po utworzeniu go, a nie faktycznie NATYCHMIAST jak myślisz NATYCHMIAST oznacza (na czytaniu). Zrobiłoby to dokładnie to samo, gdybyś podał obraz do widoku przewijania, aby wyświetlić, a obraz poszedł do losowania. Jeśli masz szczęście, były jakieś wolne cykle pomiędzy przewijaniem, a to poprawiałoby sytuację, ale w zasadzie myślę, że to brzmi o wiele bardziej obiecująco, że będzie robił więcej, niż faktycznie robi.