iOS性能之WebP


當今互聯網,無論網頁還是APP,流量占用最大的,多數都是因為圖片,越是良好的用戶體驗,對圖片的依賴度越高。但是圖片是一把雙刃劍,帶來了用戶體驗,吸引了用戶注意,卻影響了性能,因為網絡請求時間會相對比較長。

圖片分很多種,比較主流的就是:位圖(BMP),jpg(JPEG,有損壓縮格式),png(無損壓縮格式)等,這三種,按照圖片大小和清晰度來看,依次是:BMP > png > jpg。因為jpg是有損壓縮格式,所以jpg圖片相對最小。iOS普遍選擇的是png來作為最優先選擇的圖片(蘋果官方也是這樣建議的)。

不過,有一種圖片格式,在大小上比png小,圖片質量上跟png差不多,就是WebP。

什么是WebP?

簡單描述一下,WebP是google創造出的一種圖片格式,圖片的壓縮和解碼都由google提供的API完成(各種語言都有,不過目前好像沒看到js可以解碼WebP的),在無損壓縮的情況下,比png要小28%左右

現在已經被各大瀏覽器廠商兼容(如:Chrome,Firefox等),不過蘋果的Safri還沒有兼容這種格式,所以如果UIWebView里面含有WebP的圖片的話,就會顯示不出來(但是我們可以通過NSUrlProtocol來做處理)。如果要在APP中使用得話,我們需要引入SDWebImage這個第三方庫。

SDWebImage使用WebP

這個第三方庫封裝得很好,使用起來與我們以前用他來加載網絡圖片方式一樣,如下:

[imageView sd_setImageWithURL:[NSURL URLWithString:圖片路徑] placeholderImage:[UIImage imageNamed:@"默認圖片"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { }]; 

不過,我們要深入看看他究竟是怎么實現的。

我們打開:

SDWebImageDownloaderOperation

這個類繼承了NSOperation,主要使用NSUrlSession來下載網絡圖片,我們來看他下載完成的委托方法:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error

我們截取部分代碼塊來集中分析一下:

UIImage *image = [UIImage sd_imageWithData:self.imageData];

調試進去:

    UIImage *image;
    NSString *imageContentType = [NSData sd_contentTypeForImageData:data]; //根據數據流的前8位來判斷圖片類型
    if ([imageContentType isEqualToString:@"image/gif"]) {
        image = [UIImage sd_animatedGIFWithData:data];
    }
#ifdef SD_WEBP
    else if ([imageContentType isEqualToString:@"image/webp"])
    {
        image = [UIImage sd_imageWithWebPData:data]; //將WebP解碼成相應的格式(可能是jpg,png等)
    }
#endif
    else {
        image = [[UIImage alloc] initWithData:data];
        UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
        if (orientation != UIImageOrientationUp) {
            image = [UIImage imageWithCGImage:image.CGImage
                                        scale:image.scale
                                  orientation:orientation];
        }
    }
  • 我們來說下sd_contentTypeForImageData 這個方法,如下:
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return @"image/jpeg";
        case 0x89:
            return @"image/png";
        case 0x47:
            return @"image/gif";
        case 0x49:
        case 0x4D:
            return @"image/tiff";
        case 0x52:
            // R as RIFF for WEBP
            if ([data length] < 12) {
                return nil;
            }

            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                return @"image/webp";
            }

            return nil;
    }
    return nil;
}

里面的uint8_t就是取NSData的前8位,因為圖片變換成NSData后,是使用得ASCII碼來表示的,每種圖片都含有固定的頭信息塊。

png是:89 50 4E 47 0D 0A 1A 0A

bmp是:42 4D

jpg是:FF D8 FF

webp是:52 49 46 46 中間4個字符不定 57 45 42 50(翻譯過來就是:RIFF 其他4個字符 WEBP)

這樣來看,上面代碼的含義就比較清楚了。

如果想深入了解一下圖片格式及組成,這里有一篇不錯的文章:

http://blog.csdn.net/hherima/article/details/45846901

  • 我們再來看看 sd_imageWithWebPData 這個方法

里面封裝了將WebP解碼成其他格式圖片的過程。WebP是采用VP8的編碼格式。有興趣可以研究一下具體的算法實現過程,這里有幾篇文章介紹WebP的壓縮算法。

https://developers.google.com/speed/webp/docs/compression

http://blog.csdn.net/leixiaohua1020/article/details/12760173

  • 提醒

SDWebImage在對WebP做存儲的時候,存的是未解碼的NSData,而不是解碼后的NSData,如下代碼:

SDWebImageManager 里面的

if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                        // Image refresh hit the NSURLCache cache, do not call the completion block
                    }
                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage))) {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self transformDownloadedImage:downloadedImage imageData:data withURL:url];   //存儲以前,是否要將nsdata轉換為其他格式的圖片對象
                            if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
                            }
                            
                            dispatch_main_sync_safe(^{
                                if (strongOperation && !strongOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }
                    else {
                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];    //WebP的存儲走的是這一步
                        }
                        
                        dispatch_main_sync_safe(^{
                            if (strongOperation && !strongOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
                            }
                        });
                    }

里面提供了一個委托:

UIImage *transformedImage = [self transformDownloadedImage:downloadedImage imageData:data withURL:url];

也算是用心良苦,因為可能考慮到WebP的解碼會耗費一些時間(測試下來發現,120k左右的WebP,解碼會耗時30ms左右),所以提供一個委托,可以選擇將WebP的NSData轉換為png或者jpg之后,再存儲到內存,再存儲到磁盤。

不過,時間與空間就像魚和熊掌,不可兼得,如果選擇節省時間,就不可避免的要占用更大的空間。到底選時間還是選空間,仁者見仁智者見智吧。

WebP的劣勢

把WebP說得這么天花亂墜,但是WebP也是有自己的劣勢的:

  1. 壓縮時間長,大概是png的8倍左右(不過一般都是在服務端壓縮,客戶端解碼,所以服務端可以做個預壓縮)
  2. 解碼時間比png長,大概幾十毫秒。WebP是節省了流量(圖片小),增加了解碼時間,換句話說就是:同樣的圖片,網絡越快(圖片更小的WebP就沒有明顯優勢),圖片越多(WebP要解碼),WebP比png要慢。
  3. UIWebView,WKWebView都不支持WebP。(UIWebView可以用NSUrlProtocol來解決,但是WKWebView還沒有太完美的辦法,誰知道的請告訴我下)
  4. 不支持流式解壓縮(即圖片加載的時候會由模糊慢慢變清晰的過程,WebP貌似不支持這種解壓縮方式)

 

最后

關於WebP和jpg的圖片大小來比較的話,因為WebP是支持無損和有損壓縮的,而jpg是有損壓縮的格式,所以如果同樣的圖片都做有損壓縮,WebP是比jpg要小的。

這里有篇不錯的介紹WebP的文章:

https://isux.tencent.com/introduction-of-webp.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM