Merge pull request #2801 from dreampiggy/feature_ensure_image_class_match
Add a new option `SDWebImageMatchAnimatedImageClass`, to ensure we always match the custom image class instead of UIImage/NSImage class
This commit is contained in:
commit
f5980e5fed
|
@ -46,7 +46,13 @@ typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
|
|||
/**
|
||||
* By default, for `SDAnimatedImage`, we decode the animated image frame during rendering to reduce memory usage. This flag actually trigger `preloadAllAnimatedImageFrames = YES` after image load from disk cache
|
||||
*/
|
||||
SDImageCachePreloadAllFrames = 1 << 6
|
||||
SDImageCachePreloadAllFrames = 1 << 6,
|
||||
/**
|
||||
* By default, when you use `SDWebImageContextAnimatedImageClass` context option (like using `SDAnimatedImageView` which designed to use `SDAnimatedImage`), we may still use `UIImage` when the memory cache hit, or image decoder is not available, to behave as a fallback solution.
|
||||
* Using this option, can ensure we always produce image with your provided class. If failed, a error with code `SDWebImageErrorBadImageData` will been used.
|
||||
* Note this options is not compatible with `SDImageCacheDecodeFirstFrameOnly`, which always produce a UIImage/NSImage.
|
||||
*/
|
||||
SDImageCacheMatchAnimatedImageClass = 1 << 7,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -377,13 +377,26 @@
|
|||
|
||||
// First check the in-memory cache...
|
||||
UIImage *image = [self imageFromMemoryCacheForKey:key];
|
||||
|
||||
if ((options & SDImageCacheDecodeFirstFrameOnly) && image.sd_isAnimated) {
|
||||
|
||||
if (image) {
|
||||
if (options & SDImageCacheDecodeFirstFrameOnly) {
|
||||
// Ensure static image
|
||||
Class animatedImageClass = image.class;
|
||||
if (image.sd_isAnimated || ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)])) {
|
||||
#if SD_MAC
|
||||
image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
|
||||
image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
|
||||
#else
|
||||
image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
|
||||
image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
|
||||
#endif
|
||||
}
|
||||
} else if (options & SDImageCacheMatchAnimatedImageClass) {
|
||||
// Check image class matching
|
||||
Class animatedImageClass = image.class;
|
||||
Class desiredImageClass = context[SDWebImageContextAnimatedImageClass];
|
||||
if (desiredImageClass && ![animatedImageClass isSubclassOfClass:desiredImageClass]) {
|
||||
image = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryMemoryData));
|
||||
|
@ -607,6 +620,7 @@
|
|||
if (cacheOptions & SDImageCacheDecodeFirstFrameOnly) options |= SDWebImageDecodeFirstFrameOnly;
|
||||
if (cacheOptions & SDImageCachePreloadAllFrames) options |= SDWebImagePreloadAllFrames;
|
||||
if (cacheOptions & SDImageCacheAvoidDecodeImage) options |= SDWebImageAvoidDecodeImage;
|
||||
if (cacheOptions & SDImageCacheMatchAnimatedImageClass) options |= SDWebImageMatchAnimatedImageClass;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -626,6 +640,8 @@
|
|||
if (options & SDWebImageAvoidDecodeImage) cacheOptions |= SDImageCacheAvoidDecodeImage;
|
||||
if (options & SDWebImageDecodeFirstFrameOnly) cacheOptions |= SDImageCacheDecodeFirstFrameOnly;
|
||||
if (options & SDWebImagePreloadAllFrames) cacheOptions |= SDImageCachePreloadAllFrames;
|
||||
if (options & SDWebImageMatchAnimatedImageClass) cacheOptions |= SDImageCacheMatchAnimatedImageClass;
|
||||
|
||||
return [self queryCacheOperationForKey:key options:cacheOptions context:context done:completionBlock];
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,16 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
|
|||
// check whether we should use `SDAnimatedImage`
|
||||
if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) {
|
||||
image = [[animatedImageClass alloc] initWithData:imageData scale:scale options:coderOptions];
|
||||
if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
|
||||
[((id<SDAnimatedImage>)image) preloadAllFrames];
|
||||
if (image) {
|
||||
// Preload frames if supported
|
||||
if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
|
||||
[((id<SDAnimatedImage>)image) preloadAllFrames];
|
||||
}
|
||||
} else {
|
||||
// Check image class matching
|
||||
if (options & SDWebImageMatchAnimatedImageClass) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,16 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
|
|||
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
|
||||
if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) {
|
||||
image = [[animatedImageClass alloc] initWithData:imageData scale:scale options:coderOptions];
|
||||
if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
|
||||
[((id<SDAnimatedImage>)image) preloadAllFrames];
|
||||
if (image) {
|
||||
// Preload frames if supported
|
||||
if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
|
||||
[((id<SDAnimatedImage>)image) preloadAllFrames];
|
||||
}
|
||||
} else {
|
||||
// Check image class matching
|
||||
if (options & SDWebImageMatchAnimatedImageClass) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +128,14 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
|
|||
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
|
||||
if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)] && [progressiveCoder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) {
|
||||
image = [[animatedImageClass alloc] initWithAnimatedCoder:(id<SDAnimatedImageCoder>)progressiveCoder scale:scale];
|
||||
if (image) {
|
||||
// Progressive decoding does not preload frames
|
||||
} else {
|
||||
// Check image class matching
|
||||
if (options & SDWebImageMatchAnimatedImageClass) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!image) {
|
||||
|
|
|
@ -176,7 +176,14 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
|
|||
* By default, for `SDAnimatedImage`, we decode the animated image frame during rendering to reduce memory usage. However, you can specify to preload all frames into memory to reduce CPU usage when the animated image is shared by lots of imageViews.
|
||||
* This will actually trigger `preloadAllAnimatedImageFrames` in the background queue(Disk Cache & Download only).
|
||||
*/
|
||||
SDWebImagePreloadAllFrames = 1 << 20
|
||||
SDWebImagePreloadAllFrames = 1 << 20,
|
||||
|
||||
/**
|
||||
* By default, when you use `SDWebImageContextAnimatedImageClass` context option (like using `SDAnimatedImageView` which designed to use `SDAnimatedImage`), we may still use `UIImage` when the memory cache hit, or image decoder is not available to produce one exactlly matching your custom class as a fallback solution.
|
||||
* Using this option, can ensure we always callback image with your provided class. If failed to produce one, a error with code `SDWebImageErrorBadImageData` will been used.
|
||||
* Note this options is not compatible with `SDWebImageDecodeFirstFrameOnly`, which always produce a UIImage/NSImage.
|
||||
*/
|
||||
SDWebImageMatchAnimatedImageClass = 1 << 21,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -83,7 +83,14 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
|
|||
/**
|
||||
* By default, for `SDAnimatedImage`, we decode the animated image frame during rendering to reduce memory usage. This flag actually trigger `preloadAllAnimatedImageFrames = YES` after image load from network
|
||||
*/
|
||||
SDWebImageDownloaderPreloadAllFrames = 1 << 11
|
||||
SDWebImageDownloaderPreloadAllFrames = 1 << 11,
|
||||
|
||||
/**
|
||||
* By default, when you use `SDWebImageContextAnimatedImageClass` context option (like using `SDAnimatedImageView` which designed to use `SDAnimatedImage`), we may still use `UIImage` when the memory cache hit, or image decoder is not available, to behave as a fallback solution.
|
||||
* Using this option, can ensure we always produce image with your provided class. If failed, a error with code `SDWebImageErrorBadImageData` will been used.
|
||||
* Note this options is not compatible with `SDWebImageDownloaderDecodeFirstFrameOnly`, which always produce a UIImage/NSImage.
|
||||
*/
|
||||
SDWebImageDownloaderMatchAnimatedImageClass = 1 << 12,
|
||||
};
|
||||
|
||||
FOUNDATION_EXPORT NSNotificationName _Nonnull const SDWebImageDownloadStartNotification;
|
||||
|
|
|
@ -536,6 +536,7 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
if (options & SDWebImageAvoidDecodeImage) downloaderOptions |= SDWebImageDownloaderAvoidDecodeImage;
|
||||
if (options & SDWebImageDecodeFirstFrameOnly) downloaderOptions |= SDWebImageDownloaderDecodeFirstFrameOnly;
|
||||
if (options & SDWebImagePreloadAllFrames) downloaderOptions |= SDWebImageDownloaderPreloadAllFrames;
|
||||
if (options & SDWebImageMatchAnimatedImageClass) downloaderOptions |= SDWebImageDownloaderMatchAnimatedImageClass;
|
||||
|
||||
if (cachedImage && options & SDWebImageRefreshCached) {
|
||||
// force progressive off if image already cached but forced refreshing
|
||||
|
|
|
@ -475,6 +475,7 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
if (downloadOptions & SDWebImageDownloaderDecodeFirstFrameOnly) options |= SDWebImageDecodeFirstFrameOnly;
|
||||
if (downloadOptions & SDWebImageDownloaderPreloadAllFrames) options |= SDWebImagePreloadAllFrames;
|
||||
if (downloadOptions & SDWebImageDownloaderAvoidDecodeImage) options |= SDWebImageAvoidDecodeImage;
|
||||
if (downloadOptions & SDWebImageDownloaderMatchAnimatedImageClass) options |= SDWebImageMatchAnimatedImageClass;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -367,6 +367,30 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
|
|||
expect(fileManager.lastError).equal(targetError);
|
||||
}
|
||||
|
||||
- (void)test41MatchAnimatedImageClassWorks {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"MatchAnimatedImageClass option should work"];
|
||||
UIImage *image = [[UIImage alloc] initWithContentsOfFile:self.testGIFPath];
|
||||
|
||||
NSString *kAnimatedImageKey = @"kAnimatedImageKey";
|
||||
|
||||
// Store UIImage into cache
|
||||
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:kAnimatedImageKey];
|
||||
|
||||
// `MatchAnimatedImageClass` will cause query failed because class does not match
|
||||
[SDImageCache.sharedImageCache queryCacheOperationForKey:kAnimatedImageKey options:SDImageCacheMatchAnimatedImageClass context:@{SDWebImageContextAnimatedImageClass : SDAnimatedImage.class} done:^(UIImage * _Nullable image1, NSData * _Nullable data1, SDImageCacheType cacheType1) {
|
||||
expect(image1).beNil();
|
||||
// This should query success with UIImage
|
||||
[SDImageCache.sharedImageCache queryCacheOperationForKey:kAnimatedImageKey options:0 context:@{SDWebImageContextAnimatedImageClass : SDAnimatedImage.class} done:^(UIImage * _Nullable image2, NSData * _Nullable data2, SDImageCacheType cacheType2) {
|
||||
expect(image2).notTo.beNil();
|
||||
expect(image2).equal(image);
|
||||
|
||||
[expectation fulfill];
|
||||
}];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
#pragma mark - SDMemoryCache & SDDiskCache
|
||||
- (void)test42CustomMemoryCache {
|
||||
SDImageCacheConfig *config = [[SDImageCacheConfig alloc] init];
|
||||
|
|
Loading…
Reference in New Issue