Fix the issue for progressive decoding that do not parse the correct animated image scale. Update all coders

This commit is contained in:
DreamPiggy 2018-04-25 12:59:44 +08:00
parent a22a0e25f7
commit 072b832375
7 changed files with 79 additions and 61 deletions

View File

@ -33,9 +33,6 @@ const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef
@implementation SDWebImageAPNGCoder {
size_t _width, _height;
#if SD_UIKIT || SD_WATCH
UIImageOrientation _orientation;
#endif
CGImageSourceRef _imageSource;
NSData *_imageData;
CGFloat _scale;
@ -246,7 +243,16 @@ const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef
- (instancetype)initIncrementalWithOptions:(nullable SDWebImageCoderOptions *)options {
self = [super init];
if (self) {
_imageSource = CGImageSourceCreateIncremental((__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceShouldCache : @(YES)});
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatPNG];
_imageSource = CGImageSourceCreateIncremental((__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceTypeIdentifierHint : (__bridge_transfer NSString *)imageUTType});
CGFloat scale = 1;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
@ -290,7 +296,7 @@ const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL);
if (partialImageRef) {
CGFloat scale = 1;
CGFloat scale = _scale;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {

View File

@ -114,7 +114,7 @@ FOUNDATION_EXPORT SDWebImageCoderOption _Nonnull const SDWebImageCoderEncodeComp
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
This init method should not return nil
@param options A dictionary containing any progressive decoding options (instance-level). Currentlly there is no options for this and always pass nil. Kept for extensibility.
@param options A dictionary containing any progressive decoding options (instance-level). Pass @{SDWebImageCoderDecodeScaleFactor: @(1.0)} to specify scale factor for progressive animated image (each frames should use the same scale).
@return A new instance to do incremental decoding for the specify image format
*/
- (nonnull instancetype)initIncrementalWithOptions:(nullable SDWebImageCoderOptions *)options;
@ -129,7 +129,7 @@ FOUNDATION_EXPORT SDWebImageCoderOption _Nonnull const SDWebImageCoderEncodeComp
/**
Incremental decode the current image data to image.
@note Due to the performance issue for progressive decoding and the integration for image view. This method may only return the first frame image even if the image data is animated image. If you want progressive animated image decoding, also conform to `SDWebImageAnimatedCoder` and use `animatedImageFrameAtIndex` instead.
@note Due to the performance issue for progressive decoding and the integration for image view. This method may only return the first frame image even if the image data is animated image. If you want progressive animated image decoding, conform to `SDWebImageAnimatedCoder` protocol as well and use `animatedImageFrameAtIndex:` instead.
@param options A dictionary containing any progressive decoding options. Pass @{SDWebImageCoderDecodeScaleFactor: @(1.0)} to specify scale factor for progressive image
@return The decoded image from current data

View File

@ -337,7 +337,7 @@ didReceiveResponse:(NSURLResponse *)response
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
// Get the image data
__block NSData *imageData = [self.imageData copy];
NSData *imageData = [self.imageData copy];
// Get the total bytes downloaded
const NSUInteger totalSize = imageData.length;
// Get the finish status
@ -345,7 +345,7 @@ didReceiveResponse:(NSURLResponse *)response
// progressive decode the image in coder queue
dispatch_async(self.coderQueue, ^{
UIImage *image = SDWebImageLoaderDecodeProgressiveImageData(data, self.request.URL, finished, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
UIImage *image = SDWebImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, finished, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
if (image) {
// We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.

View File

@ -183,7 +183,16 @@
- (instancetype)initIncrementalWithOptions:(nullable SDWebImageCoderOptions *)options {
self = [super init];
if (self) {
_imageSource = CGImageSourceCreateIncremental((__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceShouldCache : @(YES)});
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatGIF];
_imageSource = CGImageSourceCreateIncremental((__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceTypeIdentifierHint : (__bridge_transfer NSString *)imageUTType});
CGFloat scale = 1;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
@ -227,7 +236,7 @@
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL);
if (partialImageRef) {
CGFloat scale = 1;
CGFloat scale = _scale;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {

View File

@ -14,13 +14,9 @@
@implementation SDWebImageImageIOCoder {
size_t _width, _height;
#if SD_UIKIT || SD_WATCH
UIImageOrientation _orientation;
#else
CGImagePropertyOrientation _orientation;
#endif
CGImageSourceRef _imageSource;
NSUInteger _frameCount;
CGFloat _scale;
BOOL _finished;
}
@ -37,9 +33,7 @@
- (void)didReceiveMemoryWarning:(NSNotification *)notification
{
if (_imageSource) {
for (size_t i = 0; i < _frameCount; i++) {
CGImageSourceRemoveCacheAtIndex(_imageSource, i);
}
CGImageSourceRemoveCacheAtIndex(_imageSource, 0);
}
}
@ -66,19 +60,6 @@
}
}
- (BOOL)canIncrementalDecodeFromData:(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;
}
}
- (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDWebImageCoderOptions *)options {
if (!data) {
return nil;
@ -96,10 +77,32 @@
}
#pragma mark - Progressive Decode
- (BOOL)canIncrementalDecodeFromData:(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;
}
}
- (instancetype)initIncrementalWithOptions:(nullable SDWebImageCoderOptions *)options {
self = [super init];
if (self) {
_imageSource = CGImageSourceCreateIncremental(NULL);
CGFloat scale = 1;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
#if SD_UIKIT
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
#endif
@ -118,7 +121,6 @@
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)data, finished);
_frameCount = CGImageSourceGetCount(_imageSource);
if (_width + _height == 0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL);
@ -136,11 +138,7 @@
// 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:(CGImagePropertyOrientation)orientationValue];
#else
_orientation = (CGImagePropertyOrientation)orientationValue;
#endif
}
}
}
@ -172,7 +170,7 @@
#endif
if (partialImageRef) {
CGFloat scale = 1;
CGFloat scale = _scale;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {
@ -180,7 +178,8 @@
}
}
#if SD_UIKIT || SD_WATCH
image = [[UIImage alloc] initWithCGImage:partialImageRef scale:scale orientation:_orientation];
UIImageOrientation imageOrientation = [SDWebImageCoderHelper imageOrientationFromEXIFOrientation:_orientation];
image = [[UIImage alloc] initWithCGImage:partialImageRef scale:scale orientation:imageOrientation];
#else
image = [[UIImage alloc] initWithCGImage:partialImageRef scale:scale orientation:_orientation];
#endif

View File

@ -77,23 +77,6 @@ UIImage * _Nullable SDWebImageLoaderDecodeProgressiveImageData(NSData * _Nonnull
NSCParameterAssert(imageURL);
NSCParameterAssert(operation);
id<SDWebImageProgressiveCoder> progressiveCoder = objc_getAssociatedObject(operation, SDWebImageLoaderProgressiveCoderKey);
if (!progressiveCoder) {
// We need to create a new instance for progressive decoding to avoid conflicts
for (id<SDWebImageCoder>coder in [SDWebImageCodersManager sharedManager].coders) {
if ([coder conformsToProtocol:@protocol(SDWebImageProgressiveCoder)] &&
[((id<SDWebImageProgressiveCoder>)coder) canIncrementalDecodeFromData:imageData]) {
progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:nil];
break;
}
}
objc_setAssociatedObject(operation, SDWebImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// If we can't find any progressive coder, disable progressive download
if (!progressiveCoder) {
return nil;
}
UIImage *image;
id<SDWebImageCacheKeyFilter> cacheKeyFilter = [context valueForKey:SDWebImageContextCacheKeyFilter];
NSString *cacheKey;
@ -108,6 +91,22 @@ UIImage * _Nullable SDWebImageLoaderDecodeProgressiveImageData(NSData * _Nonnull
if (scale < 1) {
scale = 1;
}
id<SDWebImageProgressiveCoder> progressiveCoder = objc_getAssociatedObject(operation, SDWebImageLoaderProgressiveCoderKey);
if (!progressiveCoder) {
// We need to create a new instance for progressive decoding to avoid conflicts
for (id<SDWebImageCoder>coder in [SDWebImageCodersManager sharedManager].coders.reverseObjectEnumerator) {
if ([coder conformsToProtocol:@protocol(SDWebImageProgressiveCoder)] &&
[((id<SDWebImageProgressiveCoder>)coder) canIncrementalDecodeFromData:imageData]) {
progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:@{SDWebImageCoderDecodeScaleFactor : @(scale)}];
break;
}
}
objc_setAssociatedObject(operation, SDWebImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// If we can't find any progressive coder, disable progressive download
if (!progressiveCoder) {
return nil;
}
[progressiveCoder updateIncrementalData:imageData finished:finished];
if (!decodeFirstFrame) {

View File

@ -211,6 +211,14 @@ dispatch_semaphore_signal(self->_lock);
if (self) {
// Progressive images need transparent, so always use premultiplied RGBA
_idec = WebPINewRGB(MODE_bgrA, NULL, 0, 0);
CGFloat scale = 1;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {
scale = 1;
}
}
_scale = scale;
}
return self;
}
@ -225,10 +233,7 @@ dispatch_semaphore_signal(self->_lock);
if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
return;
}
}
- (BOOL)incrementalFinished {
return _finished;
// libwebp current does not support progressive decoding for animated image, so no need to scan and update the frame information
}
- (UIImage *)incrementalDecodedImageWithOptions:(SDWebImageCoderOptions *)options {
@ -278,7 +283,7 @@ dispatch_semaphore_signal(self->_lock);
CGContextRelease(canvas);
return nil;
}
CGFloat scale = 1;
CGFloat scale = _scale;
if ([options valueForKey:SDWebImageCoderDecodeScaleFactor]) {
scale = [[options valueForKey:SDWebImageCoderDecodeScaleFactor] doubleValue];
if (scale < 1) {