Merge pull request #2040 from dreampiggy/Fix_progressive_download_gray_background

Use CGImageSourceCreateIncremental to perform progressive decoding in…
This commit is contained in:
DreamPiggy 2017-09-29 23:25:53 -05:00 committed by GitHub
commit 52993c7150
2 changed files with 40 additions and 26 deletions

View File

@ -8,7 +8,6 @@
#import "SDWebImageDownloader.h" #import "SDWebImageDownloader.h"
#import "SDWebImageDownloaderOperation.h" #import "SDWebImageDownloaderOperation.h"
#import <ImageIO/ImageIO.h>
@implementation SDWebImageDownloadToken @implementation SDWebImageDownloadToken
@end @end

View File

@ -48,10 +48,11 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@end @end
@implementation SDWebImageDownloaderOperation { @implementation SDWebImageDownloaderOperation {
size_t width, height; size_t _width, _height;
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
UIImageOrientation orientation; UIImageOrientation _orientation;
#endif #endif
CGImageSourceRef _imageSource;
} }
@synthesize executing = _executing; @synthesize executing = _executing;
@ -80,6 +81,10 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
- (void)dealloc { - (void)dealloc {
SDDispatchQueueRelease(_barrierQueue); SDDispatchQueueRelease(_barrierQueue);
if (_imageSource) {
CFRelease(_imageSource);
_imageSource = NULL;
}
} }
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
@ -317,48 +322,55 @@ didReceiveResponse:(NSURLResponse *)response
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) { if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf // Thanks to the author @Nyx0uf
// Get the image data
NSData *imageData = [self.imageData copy];
// Get the total bytes downloaded // Get the total bytes downloaded
const NSInteger totalSize = self.imageData.length; const NSInteger totalSize = imageData.length;
// Get the finish status
BOOL finished = (self.expectedSize == totalSize);
if (!_imageSource) {
_imageSource = CGImageSourceCreateIncremental(NULL);
}
// Update the data source, we must pass ALL the data, not just the new bytes // Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL); CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)imageData, finished);
if (width + height == 0) { if (_width + _height == 0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL);
if (properties) { if (properties) {
NSInteger orientationValue = -1; NSInteger orientationValue = -1;
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height); if (val) CFNumberGetValue(val, kCFNumberLongType, &_height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width); if (val) CFNumberGetValue(val, kCFNumberLongType, &_width);
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation); val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue); if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
CFRelease(properties); CFRelease(properties);
// When we draw to Core Graphics, we lose orientation information, // When we draw to Core Graphics, we lose orientation information,
// which means the image below born of initWithCGIImage will be // which means the image below born of initWithCGIImage will be
// oriented incorrectly sometimes. (Unlike the image born of initWithData // oriented incorrectly sometimes. (Unlike the image born of initWithData
// in didCompleteWithError.) So save it here and pass it on later. // in didCompleteWithError.) So save it here and pass it on later.
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)]; _orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
#endif #endif
} }
} }
if (width + height > 0 && totalSize < self.expectedSize) { if (_width + _height > 0 && !finished) {
// Create the image // Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL);
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
// Workaround for iOS anamorphic image // Workaround for iOS anamorphic image
if (partialImageRef) { if (partialImageRef) {
const size_t partialHeight = CGImageGetHeight(partialImageRef); const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); CGContextRef bmContext = CGBitmapContextCreate(NULL, _width, _height, 8, _width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
if (bmContext) { if (bmContext) {
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = _width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef); CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext); partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext); CGContextRelease(bmContext);
@ -369,13 +381,14 @@ didReceiveResponse:(NSURLResponse *)response
} }
} }
#endif #endif
if (partialImageRef) { if (partialImageRef) {
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation]; UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:_orientation];
#elif SD_MAC #elif SD_MAC
UIImage *image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize]; UIImage *image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize];
#endif #endif
CGImageRelease(partialImageRef);
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
UIImage *scaledImage = [self scaledImageForKey:key image:image]; UIImage *scaledImage = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) { if (self.shouldDecompressImages) {
@ -384,14 +397,16 @@ didReceiveResponse:(NSURLResponse *)response
else { else {
image = scaledImage; image = scaledImage;
} }
CGImageRelease(partialImageRef);
[self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO]; [self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
} }
} }
if (imageSource) { if (finished) {
CFRelease(imageSource); if (_imageSource) {
CFRelease(_imageSource);
_imageSource = NULL;
}
} }
} }