iOS圖片解碼

參考iOS圖片解碼實踐 / iOS圖像解碼和最佳實踐 / SDWebImage / YYImage

imageNamedimageWithContentsOfFile的區(qū)別

1、+ (UIImage *)imageNamed:(NSString *)name;

name:圖片資源或文件的名稱。對于assets中的資源,是指定的圖片資源名稱,圖片資源中可以包含1x、2x、3x圖片,系統(tǒng)會自動選擇。對于png圖像可以省略擴展名,對于其他格式圖片需要始終包含文件擴展名。

當在assets目錄中查找時,查找symbol image優(yōu)先于同名當bitmap image。由于symbol image僅支持iOS13及之后版本,所以可能需要在assets中包含兩套圖片格式,系統(tǒng)會在早期iOS版本自動選擇bitmap image,不過我覺得一般沒人這么干吧~。不能使用這個方法加載系統(tǒng)的symbol image,需要使用systemImageNamed:方法。

這個方法會根據(jù)指定的名稱在系統(tǒng)緩存中查找,選擇最適合屏幕的image返回。如果系統(tǒng)緩存中沒有找到,則從asstes目錄或磁盤中找到正確的圖片創(chuàng)建一個image返回。系統(tǒng)會隨時清空圖片緩存,當然僅僅是清空未使用的圖片緩存。

這個方法在iOS9之后是線程安全的。

Tip:

如果僅加載一次圖片或者不想要添加到系統(tǒng)緩存中,可以使用imageWithContentsOfFile方法創(chuàng)建image。保證使用次數(shù)少的圖片不添加到系統(tǒng)緩存中,可以有效提高App內(nèi)存使用效率。

2、+ (UIImage *)imageWithContentsOfFile:(NSString *)path;

這個方法不會緩存image對象。而且只會根據(jù)文件名獲取,并不會根據(jù)2x、3x自動創(chuàng)建。

圖片解壓縮

由于png、jpeg等圖片格式均是壓縮的位圖圖片,在顯示時需要解碼成位圖。png是無損壓縮且支持alpha通道,jpeg是有損壓縮。

    UIImageView *imgV = [[UIImageView alloc] initWithFrame:self.view.bounds];
    UIImage *image = [UIImage imageNamed:@"test.jpeg"];
    [self.view addSubview:imgV];

上述代碼第一次加載時做了如下操作:

  • 從磁盤加載test.jpeg圖片到內(nèi)存中為 Data Buffer
  • 當圖片真正要被顯示在屏幕上時,會觸發(fā)解碼,Data Buffer ---> Image Buffer
  • 然后Image Buffer傳遞給GPU,GPU經(jīng)過頂點坐標轉(zhuǎn)換、頂點著色器、光柵化、片元著色器等步驟轉(zhuǎn)化為Frame Buffer存在幀緩沖區(qū),等待屏幕刷新顯示

下面代碼通過CGBitmapContextCreateImage從圖形上下文中獲取到解碼到位圖。

+ (void)decodeSourceImage:(UIImage *)sourceImage completion:(nonnull void (^)(UIImage * _Nonnull))completion {
    if (!sourceImage) return;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //獲取圖片寬高
        CGImageRef cgimage = sourceImage.CGImage;
        size_t width = CGImageGetWidth(cgimage);
        size_t height = CGImageGetHeight(cgimage);
        if (width == 0 || height == 0) return;
        
        //判斷是否有alpha通道
        CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgimage) & kCGBitmapAlphaInfoMask;
        BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
                          alphaInfo == kCGImageAlphaNoneSkipFirst ||
                          alphaInfo == kCGImageAlphaNoneSkipLast);
        
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
        bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
        
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        //創(chuàng)建圖形上下文
        CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, bitmapInfo);
        if (!context) return;
        //繪制到上下文中解壓
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgimage); // decode
        //從上下文中獲取
        CGImageRef newCgimage = CGBitmapContextCreateImage(context);
        UIImage *desImage = [UIImage imageWithCGImage:newCgimage];
        CGContextRelease(context);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(desImage);
            }
        });
    });
}

YYKit中提供了另一種解碼方式如下,在注釋中說使用原始數(shù)據(jù)解碼會造成丟失一些精度。

  + (void)decodeSourceImage1:(UIImage *)sourceImage completion:(nonnull void (^)(UIImage * _Nonnull))completion {
    if (!sourceImage) return;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        CGImageRef cgimage = sourceImage.CGImage;
        size_t width = CGImageGetWidth(cgimage);
        size_t height = CGImageGetHeight(cgimage);
        if (width == 0 || height == 0) return;
        
        CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgimage);
        size_t bitsPerComponent = CGImageGetBitsPerComponent(cgimage);
        size_t bitsPerPixel = CGImageGetBitsPerPixel(cgimage);
        size_t bytesPerRow = CGImageGetBytesPerRow(cgimage);
        CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgimage);
        if (bytesPerRow == 0 || width == 0 || height == 0) return;
        
        CGDataProviderRef provider = CGImageGetDataProvider(cgimage);
        if (!provider) return;
        //獲取原始數(shù)據(jù)
        CFDataRef data = CGDataProviderCopyData(provider);
        if (!data) return;
        
        CGDataProviderRef newProvider = CGDataProviderCreateWithCFData(data);
        CFRelease(data);
        if (!newProvider) return;
        
        CGImageRef newImageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, newProvider, NULL, false, kCGRenderingIntentDefault);
        UIImage *desImage = [UIImage imageWithCGImage:newImageRef]; 
        CFRelease(newImageRef);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(desImage);
            }
        });
        
    });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容