九锋边、SDWebImage源碼解析之編解碼(一)

SDWebImage里自己寫(xiě)了一個(gè)編解碼管理器,用于實(shí)現(xiàn)編碼编曼,解碼豆巨,壓縮,縮小圖片像素功能灵巧。涉及到的文件有SDWebImageCodersManager搀矫,SDWebImageCoder抹沪,SDWebImageImageIOCoder等等

一刻肄、SDWebImageCodersManager
我們先來(lái)看SDWebImageCodersManager,SDWebImageCodersManager是一個(gè)編碼解碼管理器融欧,處理多個(gè)圖片編碼解碼任務(wù)敏弃,編碼器數(shù)組是一個(gè)優(yōu)先級(jí)隊(duì)列,這意味著后面添加的編碼器將具有最高優(yōu)先級(jí)噪馏。

SDWebImageCodersManager.h

@interface SDWebImageCodersManager : NSObject<SDWebImageCoder>

/**
 Shared reusable instance
 單例
 */
+ (nonnull instancetype)sharedInstance;

/**
 All coders in coders manager. The coders array is a priority queue, which means the later added coder will have the highest priority
 編碼器管理器中的所有編碼器麦到。編碼器數(shù)組是一個(gè)優(yōu)先級(jí)隊(duì)列,這意味著后面添加的編碼器將具有最高優(yōu)先級(jí)欠肾。
 */
@property (nonatomic, strong, readwrite, nullable) NSArray<SDWebImageCoder>* coders;

/**
 Add a new coder to the end of coders array. Which has the highest priority.
增加一個(gè)新的coder 瓶颠,新加入的最先編解碼
 @param coder coder
 */
- (void)addCoder:(nonnull id<SDWebImageCoder>)coder;

/**
 Remove a coder in the coders array.
刪除一個(gè)coder
 @param coder coder
 */
- (void)removeCoder:(nonnull id<SDWebImageCoder>)coder;

初始化方法

+ (nonnull instancetype)sharedInstance {
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}

- (instancetype)init {
    if (self = [super init]) {
        // initialize with default coders
        _mutableCoders = [@[[SDWebImageImageIOCoder sharedCoder]] mutableCopy]; //初始化SDWebImageImageIOCoder支持PNG,JPEG刺桃,TIFF粹淋,包括支持漸進(jìn)解碼的解碼器
#ifdef SD_WEBP
        [_mutableCoders addObject:[SDWebImageWebPCoder sharedCoder]]; //添加WebP
#endif
        _mutableCodersAccessQueue = dispatch_queue_create("com.hackemist.SDWebImageCodersManager", DISPATCH_QUEUE_CONCURRENT); //創(chuàng)建隊(duì)列
    }
    return self;
}

添加編碼器和刪除編碼器

//添加編碼器
- (void)addCoder:(nonnull id<SDWebImageCoder>)coder { //增加編碼器,遵守SDWebImageCoder協(xié)議
    if ([coder conformsToProtocol:@protocol(SDWebImageCoder)]) {//該編碼器是否實(shí)現(xiàn)了SDWebImageCoder協(xié)議的方法
        dispatch_barrier_sync(self.mutableCodersAccessQueue, ^{ //同步添加編碼器瑟慈,將自己的任務(wù)插入到隊(duì)列的時(shí)候桃移,需要等待自己的任務(wù)結(jié)束之后才會(huì)繼續(xù)插入被寫(xiě)在它后面的任務(wù),然后執(zhí)行它們
            [self.mutableCoders addObject:coder];
        });
    }
}

//刪除編碼器
- (void)removeCoder:(nonnull id<SDWebImageCoder>)coder {//遵守SDWebImageCoder協(xié)議
    dispatch_barrier_sync(self.mutableCodersAccessQueue, ^{//同步刪除編碼器
        [self.mutableCoders removeObject:coder];
    });
}

coders的getter和setter方法

//coders getter 方法
- (NSArray<SDWebImageCoder> *)coders {
    __block NSArray<SDWebImageCoder> *sortedCoders = nil;
    dispatch_sync(self.mutableCodersAccessQueue, ^{
        sortedCoders = (NSArray<SDWebImageCoder> *)[[[self.mutableCoders copy] reverseObjectEnumerator] allObjects];
    });
    return sortedCoders;
}

//coders setter 方法
- (void)setCoders:(NSArray<SDWebImageCoder> *)coders {
    dispatch_barrier_sync(self.mutableCodersAccessQueue, ^{
        self.mutableCoders = [coders mutableCopy];
    });
}

reverseObjectEnumerator 將數(shù)組倒序
e.g.
//1.原始數(shù)組
NSMutableArray array = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",nil];
//2.倒序的數(shù)組
NSArray
reversedArray = [[array reverseObjectEnumerator] allObjects];

SDWebImageCoder delegate 方法

1.該圖片是否可以編碼葛碧。 遍歷self.coders中遵守SDWebImageCoder 的 coder借杰, 然后通過(guò)canEncodeToFormat 這個(gè)方法來(lái)判斷是否可以編碼

- (BOOL)canEncodeToFormat:(SDImageFormat)format {
    for (id<SDWebImageCoder> coder in self.coders) {
        if ([coder canEncodeToFormat:format]) {
            return YES;
        }
    }
    return NO;
}

2.解碼。 遍歷self.coders中遵守SDWebImageCoder 的 coder进泼, 然后通過(guò)canDecodeFromData 這個(gè)方法來(lái)判斷是否可以解碼蔗衡,如果可以纤虽,將圖像解碼

- (UIImage *)decodedImageWithData:(NSData *)data {
    if (!data) {
        return nil;
    }
    for (id<SDWebImageCoder> coder in self.coders) {
        if ([coder canDecodeFromData:data]) {
            return [coder decodedImageWithData:data];
        }
    }
    return nil;
}

3.壓縮圖像. 遍歷self.coders中遵守SDWebImageCoder 的 coder, 然后通過(guò)canDecodeFromData 這個(gè)方法來(lái)判斷是否可以解碼绞惦,如果可以廓推,將圖片壓縮顯示

- (UIImage *)decompressedImageWithImage:(UIImage *)image
                                   data:(NSData *__autoreleasing  _Nullable *)data
                                options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict {
    if (!image) {
        return nil;
    }
    for (id<SDWebImageCoder> coder in self.coders) {
        if ([coder canDecodeFromData:*data]) {
            return [coder decompressedImageWithImage:image data:data options:optionsDict];
        }
    }
    return nil;
}

4.根據(jù)image和format(類(lèi)型)編碼圖像

- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format {
    if (!image) {
        return nil;
    }
    for (id<SDWebImageCoder> coder in self.coders) {
        if ([coder canEncodeToFormat:format]) {
            return [coder encodedDataWithImage:image format:format];
        }
    }
    return nil;
}

二、SDWebImageCoder
SDWebImageCoder是單個(gè)coder翩隧,是管理器的操作對(duì)象樊展,SDWebImage里寫(xiě)了兩個(gè)協(xié)議:SDWebImageCoder和SDWebImageProgressiveCoder。SDWebImageCodersManager遵守SDWebImageCoder協(xié)議堆生,SDWebImageImageIOCoder等遵守SDWebImageProgressiveCoder专缠,主要用于設(shè)置一些協(xié)議和公用標(biāo)識(shí)

SDWebImageCoder.h

公共定義

/**
 A Boolean value indicating whether to scale down large images during decompressing. (NSNumber)
 標(biāo)識(shí)是否在壓縮圖片的時(shí)候縮小圖片的大小
 */
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageCoderScaleDownLargeImagesKey;

/**
 Return the shared device-dependent RGB color space created with CGColorSpaceCreateDeviceRGB.

 @return The device-dependent RGB color space
 色彩空間:(Color Space)這是一個(gè)色彩范圍的容器,類(lèi)型必須是CGColorSpaceRef.對(duì)于這個(gè)參數(shù)淑仆,我們可以傳入CGColorSpaceCreateDeviceRGB函數(shù)的返回值涝婉,它將給我們一個(gè)RGB色彩空間。
 */
CG_EXTERN CGColorSpaceRef _Nonnull SDCGColorSpaceGetDeviceRGB(void);

/**
 Check whether CGImageRef contains alpha channel.

 @param imageRef The CGImageRef
 @return Return YES if CGImageRef contains alpha channel, otherwise return NO
 是否有透明度
 */
CG_EXTERN BOOL SDCGImageRefContainsAlpha(_Nullable CGImageRef imageRef);

SDWebImageCoder協(xié)議

/**
 Returns YES if this coder can decode some data. Otherwise, the data should be passed to another coder.
 
 @param data The image data so we can look at it
 @return YES if this coder can decode the data, NO otherwise
 */
- (BOOL)canDecodeFromData:(nullable NSData *)data;

/**
 Decode the image data to image.

 @param data The image data to be decoded
 @return The decoded image from data
 */
- (nullable UIImage *)decodedImageWithData:(nullable NSData *)data;

/**
 Decompress the image with original image and image data.

 @param image The original image to be decompressed
 @param data The pointer to original image data. The pointer itself is nonnull but image data can be null. This data will set to cache if needed. If you do not need to modify data at the sametime, ignore this param.
 @param optionsDict A dictionary containing any decompressing options. Pass {SDWebImageCoderScaleDownLargeImagesKey: @(YES)} to scale down large images
 @return The decompressed image
 */
- (nullable UIImage *)decompressedImageWithImage:(nullable UIImage *)image
                                            data:(NSData * _Nullable * _Nonnull)data
                                         options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict;

#pragma mark - Encoding

/**
 Returns YES if this coder can encode some image. Otherwise, it should be passed to another coder.
 
 @param format The image format
 @return YES if this coder can encode the image, NO otherwise
 */
- (BOOL)canEncodeToFormat:(SDImageFormat)format;

/**
 Encode the image to image data.

 @param image The image to be encoded
 @param format The image format to encode, you should note `SDImageFormatUndefined` format is also  possible
 @return The encoded image data
 */
- (nullable NSData *)encodedDataWithImage:(nullable UIImage *)image format:(SDImageFormat)format;

SDWebImageProgressiveCoder協(xié)議


@required
/**
 Returns YES if this coder can incremental decode some data. Otherwise, it should be passed to another coder.
 
 @param data The image data so we can look at it
 @return YES if this coder can decode the data, NO otherwise
 */
- (BOOL)canIncrementallyDecodeFromData:(nullable NSData *)data;

/**
 Incremental decode the image data to image.
 
 @param data The image data has been downloaded so far
 @param finished Whether the download has finished
 @warning because incremental decoding need to keep the decoded context, we will alloc a new instance with the same class for each download operation to avoid conflicts
 @return The decoded image from data
 */
- (nullable UIImage *)incrementallyDecodedImageWithData:(nullable NSData *)data finished:(BOOL)finished;

SDWebImageCoder.m比較簡(jiǎn)單
獲取顏色空間和判斷圖片是否包含透明度

//獲取顏色空間
CGColorSpaceRef SDCGColorSpaceGetDeviceRGB(void) {
    static CGColorSpaceRef colorSpace;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        colorSpace = CGColorSpaceCreateDeviceRGB();
    });
    return colorSpace;
}

//判斷圖片是否包含透明度
BOOL SDCGImageRefContainsAlpha(CGImageRef imageRef) {
    if (!imageRef) {
        return NO;
    }
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
    BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
                      alphaInfo == kCGImageAlphaNoneSkipFirst ||
                      alphaInfo == kCGImageAlphaNoneSkipLast);
    return hasAlpha;
}

三蔗怠、SDWebImageImageIOCoder墩弯,SDWebImageGIFCoder,SDWebImageWebPCoder
這三個(gè)類(lèi)主要實(shí)現(xiàn)SDWebImageCoder的SDWebImageProgressiveCoder協(xié)議
(1)SDWebImageImageIOCoder
內(nèi)置的編碼器寞射,支持PNG渔工,JPEG,TIFF桥温,包括支持漸進(jìn)解碼引矩。
GIF 只處理第一幀
HEIC 取決于 設(shè)備性能
(2)SDWebImageGIFCoder 處理GIF圖像的編解碼
(3)SDWebImageWebPCoder 處理webP的編解碼

四、SDWebImageImageIOCoder
我們首先熟悉下一些定義

static const size_t kBytesPerPixel = 4; //kBytesPerPixel用來(lái)說(shuō)明每個(gè)像素占用內(nèi)存多少個(gè)字節(jié)侵浸,在這里是占用4個(gè)字節(jié)旺韭。(圖像在iOS設(shè)備上是以像素為單位顯示的)。
static const size_t kBitsPerComponent = 8;//kBitsPerComponent表示每一個(gè)組件占多少位掏觉。這個(gè)不太好理解区端,我們先舉個(gè)例子,比方說(shuō)RGBA澳腹,其中R(紅色)G(綠色)B(藍(lán)色)A(透明度)是4個(gè)組件织盼,每個(gè)像素由這4個(gè)組件組成,那么我們就用8位來(lái)表示著每一個(gè)組件遵湖,所以這個(gè)RGBA就是8*4 = 32位悔政。
//知道了kBitsPerComponent和每個(gè)像素有多少組件組成就能計(jì)算kBytesPerPixel了。計(jì)算公式是:(bitsPerComponent * number of components + 7)/8.


/*
 * Defines the maximum size in MB of the decoded image when the flag `SDWebImageScaleDownLargeImages` is set
 * Suggested value for iPad1 and iPhone 3GS: 60.
 * Suggested value for iPad2 and iPhone 4: 120.
 * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30.
 */
static const CGFloat kDestImageSizeMB = 60.0f; //最大支持壓縮圖像源的大小默認(rèn)的單位是MB延旧,這里設(shè)置了60MB谋国。當(dāng)我們要壓縮一張圖像的時(shí)候,首先就是要定義最大支持的源文件的大小迁沫,不能沒(méi)有任何限制芦瘾。

/*
 * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set
 * Suggested value for iPad1 and iPhone 3GS: 20.
 * Suggested value for iPad2 and iPhone 4: 40.
 * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10.
 */
static const CGFloat kSourceImageTileSizeMB = 20.0f;//原圖方塊的大小,這個(gè)方塊將會(huì)被用來(lái)分割原圖捌蚊,默認(rèn)設(shè)置為20M。

static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;//1M有多少字節(jié)
static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;//1M有多少像素
static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;//目標(biāo)總像素
static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;//原圖放款總像素

static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to overlap the seems where tiles meet.重疊像素大小

是否可以解碼,根據(jù)data判斷圖片類(lèi)型,WebP不支持近弟,根據(jù)本機(jī)類(lèi)型來(lái)判斷是否支持SDImageFormatHEIC

- (BOOL)canDecodeFromData:(nullable NSData *)data {
    switch ([NSData sd_imageFormatForImageData:data]) {
        case SDImageFormatWebP:
            // Do not support WebP decoding
            return NO;
        case SDImageFormatHEIC:
            // Check HEIC decoding compatibility
            return [[self class] canDecodeFromHEICFormat];
        default:
            return YES;
    }
}

是否可以編碼,并且實(shí)現(xiàn)逐漸顯示效果,根據(jù)data判斷圖片類(lèi)型,WebP不支持缅糟,根據(jù)本機(jī)類(lèi)型來(lái)判斷是否支持SDImageFormatHEIC

- (BOOL)canIncrementallyDecodeFromData:(NSData *)data {
    switch ([NSData sd_imageFormatForImageData:data]) {
        case SDImageFormatWebP:
            // Do not support WebP progressive decoding
            return NO;
        case SDImageFormatHEIC:
            // Check HEIC decoding compatibility
            return [[self class] canDecodeFromHEICFormat];
        default:
            return YES;
    }
}

解碼圖像
先通過(guò)initWithData獲取到一個(gè)image,然后獲取data的類(lèi)型
如果是GIF祷愉,用 animatedImageWithImages:duration:方法獲取完整圖像
如果是其他的類(lèi)型窗宦,則通過(guò)sd_imageOrientationFromImageData獲取orientation,然后合成新的圖片
注意: imageWithCGImage: scale: orientation: 這個(gè)方法可以準(zhǔn)確還原原image的方向二鳄,尺寸

- (UIImage *)decodedImageWithData:(NSData *)data {
    if (!data) {
        return nil;
    }
    
    UIImage *image = [[UIImage alloc] initWithData:data];
    
#if SD_MAC
    return image;
#else
    if (!image) {
        return nil;
    }
    
    SDImageFormat format = [NSData sd_imageFormatForImageData:data];
    if (format == SDImageFormatGIF) {
        // static single GIF need to be created animated for `FLAnimatedImage` logic
        // GIF does not support EXIF image orientation
        image = [UIImage animatedImageWithImages:@[image] duration:image.duration];
        return image;
    }
    UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data];
    if (orientation != UIImageOrientationUp) {
        image = [UIImage imageWithCGImage:image.CGImage
                                    scale:image.scale
                              orientation:orientation];
    }
    
    return image;
#endif
}

解碼圖像 逐漸顯示

繪制背景漸變

CGCradientCreateWithColorComponents函數(shù)需要四個(gè)參數(shù):

色彩空間:(Color Space)這是一個(gè)色彩范圍的容器赴涵,類(lèi)型必須是CGColorSpaceRef.對(duì)于這個(gè)參數(shù),我們可以傳入CGColorSpaceCreateDeviceRGB函數(shù)的返回值订讼,它將給我們一個(gè)RGB色彩空間髓窜。

顏色分量的數(shù)組:這個(gè)數(shù)組必須包含CGFloat類(lèi)型的紅、綠欺殿、藍(lán)和alpha值寄纵。數(shù)組中元素的數(shù)量和接下來(lái)兩個(gè)參數(shù)密切。從本質(zhì)來(lái)講脖苏,你必須讓這個(gè)數(shù)組包含足夠的值程拭,用來(lái)指定第四個(gè)參數(shù)中位置的數(shù)量。所以如果你需要兩個(gè)位置位置(起點(diǎn)和終點(diǎn))帆阳,那么你必須為數(shù)組提供兩種顏色

位置數(shù)組哺壶,顏色數(shù)組中各個(gè)顏色的位置:此參數(shù)控制該漸變從一種顏色過(guò)渡到另一種顏色的速度有多快屋吨。

位置的數(shù)量:這個(gè)參數(shù)指明了我們需要多少顏色和位置蜒谤。

CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();

CGFloat colors[] =

{

51.0 / 255.0, 160.0 / 255.0, 0.0 / 255.0, 1.00,

68.0 / 255.0, 198.0 / 255.0, 0.0 / 255.0, 1.00,

//        0.0 / 255.0,  50.0 / 255.0, 126.0 / 255.0, 1.00,

};

CGGradientRef myGradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
// Allocate bitmap context

CGContextRef bitmapContext = CGBitmapContextCreate(NULL, 320, TITLE_CONTROL_HEIGHT, 8, 4 * 320, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);

創(chuàng)建好線性漸變后,我們將使用CGContextDrawLinearGradient過(guò)程在圖形上下文中繪制至扰,此過(guò)程需要五個(gè)參數(shù):

Graphics context 指定用于繪制線性漸變的圖形上下文鳍徽。

Axial gradient 我們使用CGGradientCreateWithColorComponents函數(shù)創(chuàng)建的線性漸變對(duì)象的句柄

start point 圖形上下文中的一個(gè)CGPoint類(lèi)型的點(diǎn),表示漸變的起點(diǎn)敢课。

End Point表示漸變的終點(diǎn)阶祭。

Gradient drawing options 當(dāng)你的起點(diǎn)或者終點(diǎn)不在圖形上下文的邊緣內(nèi)時(shí),指定該如何處理直秆。你可以使用你的開(kāi)始或結(jié)束顏色來(lái)填充漸變以外的空間濒募。此參數(shù)為以下值之一:KCGGradientDrawsAfterEndLocation擴(kuò)展整個(gè)漸變到漸變的終點(diǎn)之后的所有點(diǎn) KCGGradientDrawsBeforeStartLocation擴(kuò)展整個(gè)漸變到漸變的起點(diǎn)之前的所有點(diǎn)。0不擴(kuò)展該漸變圾结。

- (UIImage *)incrementallyDecodedImageWithData:(NSData *)data finished:(BOOL)finished {
    if (!_imageSource) {
        _imageSource = CGImageSourceCreateIncremental(NULL);
    }
    UIImage *image;
    
    // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
    // Thanks to the author @Nyx0uf
    
    // Update the data source, we must pass ALL the data, not just the new bytes
    CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)data, finished);
    
    if (_width + _height == 0) {
        CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL);//獲取圖像的屬性信息
        if (properties) {
            NSInteger orientationValue = 1;
            CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);//獲取圖片高度信息
            if (val) CFNumberGetValue(val, kCFNumberLongType, &_height); //將圖片高度綁定在_height上
            val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); //獲取圖片寬度信息
            if (val) CFNumberGetValue(val, kCFNumberLongType, &_width);//將圖片寬度綁定在_width上
            val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); //獲取圖像方向信息
            if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);//將圖片的方向信息綁定在orientationValue上
            CFRelease(properties);
            
            // When we draw to Core Graphics, we lose orientation information,
            // which means the image below born of initWithCGIImage will be
            // oriented incorrectly sometimes. (Unlike the image born of initWithData
            // in didCompleteWithError.) So save it here and pass it on later.
#if SD_UIKIT || SD_WATCH
            _orientation = [SDWebImageCoderHelper imageOrientationFromEXIFOrientation:orientationValue]; //轉(zhuǎn)換方向信息
#endif
        }
    }
    
    if (_width + _height > 0) {
        // Create the image
        CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL); //獲取圖像
        
#if SD_UIKIT || SD_WATCH
        // Workaround for iOS anamorphic image
        if (partialImageRef) {
            const size_t partialHeight = CGImageGetHeight(partialImageRef);
            CGColorSpaceRef colorSpace = SDCGColorSpaceGetDeviceRGB();//色彩空間
            CGContextRef bmContext = CGBitmapContextCreate(NULL, _width, _height, 8, 0, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); //創(chuàng)建上下文
            if (bmContext) {
                CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = _width, .size.height = partialHeight}, partialImageRef); //繪制圖像
                CGImageRelease(partialImageRef);
                partialImageRef = CGBitmapContextCreateImage(bmContext);
                CGContextRelease(bmContext);
            }
            else {
                CGImageRelease(partialImageRef);
                partialImageRef = nil;
            }
        }
#endif
        
        if (partialImageRef) { //拿到CGImageRef 轉(zhuǎn)換成image并且返回
#if SD_UIKIT || SD_WATCH
            image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:_orientation];
#elif SD_MAC
            image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize];
#endif
            CGImageRelease(partialImageRef);
        }
    }
    
    if (finished) { //下載完成瑰剃,銷(xiāo)毀對(duì)象
        if (_imageSource) {
            CFRelease(_imageSource);
            _imageSource = NULL;
        }
    }
    
    return image;
}

壓縮圖片

- (UIImage *)decompressedImageWithImage:(UIImage *)image
                                   data:(NSData *__autoreleasing  _Nullable *)data
                                options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict {
#if SD_MAC
    return image;
#endif
#if SD_UIKIT || SD_WATCH
    BOOL shouldScaleDown = NO;
    if (optionsDict != nil) { //判斷是否有在壓縮過(guò)程中縮小圖片尺寸的標(biāo)識(shí)
        NSNumber *scaleDownLargeImagesOption = nil;
        if ([optionsDict[SDWebImageCoderScaleDownLargeImagesKey] isKindOfClass:[NSNumber class]]) {
            scaleDownLargeImagesOption = (NSNumber *)optionsDict[SDWebImageCoderScaleDownLargeImagesKey];
        }
        if (scaleDownLargeImagesOption != nil) {
            shouldScaleDown = [scaleDownLargeImagesOption boolValue];
        }
    }
    if (!shouldScaleDown) {//如果沒(méi)有壓縮過(guò)程中縮小圖片尺寸的標(biāo)識(shí)
        return [self sd_decompressedImageWithImage:image];
    } else {//如果有壓縮過(guò)程中縮小圖片尺寸的標(biāo)識(shí)
        UIImage *scaledDownImage = [self sd_decompressedAndScaledDownImageWithImage:image];
        if (scaledDownImage && !CGSizeEqualToSize(scaledDownImage.size, image.size)) {
            // if the image is scaled down, need to modify the data pointer as well
            SDImageFormat format = [NSData sd_imageFormatForImageData:*data];
            NSData *imageData = [self encodedDataWithImage:scaledDownImage format:format]; //解碼
            if (imageData) {
                *data = imageData;
            }
        }
        return scaledDownImage;
    }
#endif
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市筝野,隨后出現(xiàn)的幾起案子晌姚,更是在濱河造成了極大的恐慌粤剧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挥唠,死亡現(xiàn)場(chǎng)離奇詭異抵恋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宝磨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)弧关,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人唤锉,你說(shuō)我怎么就攤上這事梯醒。” “怎么了腌紧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵茸习,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我壁肋,道長(zhǎng)号胚,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任浸遗,我火速辦了婚禮猫胁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跛锌。我一直安慰自己弃秆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布髓帽。 她就那樣靜靜地躺著菠赚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪郑藏。 梳的紋絲不亂的頭發(fā)上衡查,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音必盖,去河邊找鬼拌牲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛歌粥,可吹牛的內(nèi)容都是我干的塌忽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼失驶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼土居!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤装盯,失蹤者是張志新(化名)和其女友劉穎坷虑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體埂奈,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迄损,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了账磺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芹敌。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垮抗,靈堂內(nèi)的尸體忽然破棺而出氏捞,到底是詐尸還是另有隱情,我是刑警寧澤冒版,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布液茎,位于F島的核電站,受9級(jí)特大地震影響辞嗡,放射性物質(zhì)發(fā)生泄漏捆等。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一续室、第九天 我趴在偏房一處隱蔽的房頂上張望栋烤。 院中可真熱鬧,春花似錦挺狰、人聲如沸明郭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薯定。三九已至,卻和暖如春趁耗,著一層夾襖步出監(jiān)牢的瞬間沉唠,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工苛败, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人径簿。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓罢屈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親篇亭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缠捌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)桥爽、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評(píng)論 4 62
  • “青春是段跌跌撞撞的旅行炊苫,擁有著后知后覺(jué)的美麗衙猪。忘不了我們一起踩著驚濤駭浪,去知識(shí)的海洋遠(yuǎn)航哑芹;忘不了我們一起攀著懸...
    哩啦創(chuàng)新君閱讀 306評(píng)論 0 1
  • 因?yàn)楸煌塘怂灾匕l(fā)炎辨,其他被吞的修改后重發(fā)。 七夕特別篇聪姿,現(xiàn)代AU碴萧,私設(shè)眾多,OOC沒(méi)救末购,全員病入膏肓破喻,雜敘文風(fēng)。 ...
    稻香村拿破侖閱讀 1,733評(píng)論 0 1
  • 右鍵單擊"新窗口查看圖片"可以查看大圖
    urmyfaith閱讀 3,373評(píng)論 0 1
  • 丫頭妮妮是只溫順有加盟榴,人見(jiàn)人愛(ài)的薩摩耶犬曹质,2010年5月14日出生在包頭的一家個(gè)體繁殖場(chǎng)。她的母親叫丫丫擎场,是一條一...
    王家老四1閱讀 476評(píng)論 0 2