Merge branch 'master' of github.com:rs/SDWebImage
# Conflicts: # Tests/SDWebImage Tests.xcodeproj/project.pbxproj
This commit is contained in:
commit
b9c17dcdd0
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -1,3 +1,33 @@
|
|||
## [4.1.0 - Swift API cleanup, on Jul 31st, 2017](https://github.com/rs/SDWebImage/releases/tag/4.1.0)
|
||||
See [all tickets marked for the 4.1.0 release](https://github.com/rs/SDWebImage/milestone/13)
|
||||
|
||||
#### Features
|
||||
|
||||
- add ability to change `NSURLSessionConfiguration` used by `SDWebImageDownloader` #1891 fixes #1870
|
||||
- support animated GIF on `macOS` #1975
|
||||
- cleanup the Swift interface by making unavailable all methods with missing params that have alternatives - see #1797 - this may cause require some changes in the Swift code
|
||||
|
||||
#### Fixes
|
||||
|
||||
- handle `NSURLErrorNetworkConnectionLost` #1767
|
||||
- fixed `CFBundleVersion` and `CFBundleShortVersionString` not valid for all platforms #1784 + 23a8be8 fixes #1780
|
||||
- fixed `UIActivityIndicator` not always initialized on main thread #1802 + a6af214 fixes #1801
|
||||
- `SDImageCacheConfig` forward declaration changed to import #1805
|
||||
- making image downloading cache policy more clearer #1737
|
||||
- added `@autoreleasepool` to `SDImageCache.storeImage` #1849
|
||||
- fixed 32bit machine `long long` type transfer to NSInteger may become negative #1879
|
||||
- fixed crash on multiple concurrent downloads when accessing `self.URLOperations` dictionary #1911 fixes #1909 #1950 #1835 #1838
|
||||
- fixed crash due to incorrectly retained pointer to operation `self` which appears to create a dangled pointer #1940 fixes #1807 #1858 #1859 #1821 #1925 #1883 #1816 #1716
|
||||
- fixed Swift naming collision (due to the Obj-C interface that offers multiple variants of the same method but with mixed and missing params) #1797 fixes #1764
|
||||
- coding style #1971
|
||||
- fixed Umbrella header warning for the FLAnimatedImage (while using Carthage) d9f7cf4 (replaces #1781) fixes #1776
|
||||
- fixed issue where animated image arrays could be populated out of order (order of download) #1452
|
||||
- fixed animated WebP decoding issue, including canvas size, the support for dispose method and the duration per frame #1952 (replaces #1694) fixes #1951
|
||||
|
||||
#### Docs
|
||||
|
||||
- #1778 #1779 #1788 #1799 b1c3bb7 (replaces #1806) 0df32ea #1847 5eb83c3 (replaces #1828) #1946 #1966
|
||||
|
||||
## [4.0.0 - New platforms (Mac OS X and watchOS) + refactoring, on Jan 28th, 2017](https://github.com/rs/SDWebImage/releases/tag/4.0.0)
|
||||
|
||||
See [all tickets marked for the 4.0.0 release](https://github.com/rs/SDWebImage/milestone/3)
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
@"http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp",
|
||||
@"http://littlesvr.ca/apng/images/SteamEngine.webp",
|
||||
@"http://littlesvr.ca/apng/images/world-cup-2014-42.webp",
|
||||
@"https://isparta.github.io/compare-webp/image/gif_webp/webp/2.webp",
|
||||
@"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png",
|
||||
nil];
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
// NOTE: https links or authentication ones do not work (there is a crash)
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
// For animated GIF rendering, set `animates` to YES or will only show the first frame
|
||||
self.imageView1.animates = YES;
|
||||
[self.imageView1 sd_setImageWithURL:[NSURL URLWithString:@"http://assets.sbnation.com/assets/2512203/dogflops.gif"]];
|
||||
[self.imageView2 sd_setImageWithURL:[NSURL URLWithString:@"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp"]];
|
||||
[self.imageView3 sd_setImageWithURL:[NSURL URLWithString:@"http://littlesvr.ca/apng/images/SteamEngine.webp"]];
|
||||
|
|
|
@ -92,7 +92,7 @@ imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg
|
|||
- If you use cocoapods, add `pod 'SDWebImage/GIF'` to your podfile.
|
||||
- To use it, simply make sure you use `FLAnimatedImageView` instead of `UIImageView`.
|
||||
- **Note**: there is a backwards compatible feature, so if you are still trying to load a GIF into a `UIImageView`, it will only show the 1st frame as a static image.
|
||||
- **Important**: FLAnimatedImage only works on the iOS platform, so for all the other platforms (OS X, tvOS, watchOS) we will fallback to the backwards compatibility feature described above
|
||||
- **Important**: FLAnimatedImage only works on the iOS platform. For OS X, use `NSImageView` with `animates` set to `YES` to show the entire animated images and `NO` to only show the 1st frame. For all the other platforms (tvOS, watchOS) we will fallback to the backwards compatibility feature described above
|
||||
|
||||
## Common Problems
|
||||
|
||||
|
@ -145,7 +145,7 @@ There are three ways to use SDWebImage in your project:
|
|||
#### Podfile
|
||||
```
|
||||
platform :ios, '7.0'
|
||||
pod 'SDWebImage', '~>3.8'
|
||||
pod 'SDWebImage', '~> 4.0'
|
||||
```
|
||||
|
||||
If you are using Swift, be sure to add `use_frameworks!` and set your target to iOS 8+:
|
||||
|
@ -197,6 +197,7 @@ community can help you solve it.
|
|||
- [Konstantinos K.](https://github.com/mythodeia)
|
||||
- [Bogdan Poplauschi](https://github.com/bpoplauschi)
|
||||
- [Chester Liu](https://github.com/skyline75489)
|
||||
- [DreamPiggy](https://github.com/dreampiggy)
|
||||
|
||||
## Licenses
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'SDWebImage'
|
||||
s.version = '4.0.0'
|
||||
s.version = '4.1.0'
|
||||
|
||||
s.osx.deployment_target = '10.8'
|
||||
s.ios.deployment_target = '7.0'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,7 @@
|
|||
*
|
||||
* @param url The url for the image.
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url;
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
|
||||
|
@ -42,7 +42,7 @@
|
|||
* @param placeholder The image to be set initially, until the image request finishes.
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder;
|
||||
placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
|
||||
|
@ -55,7 +55,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
|
||||
|
@ -86,7 +86,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Load the image at the given url (either from cache or download) and load it in this imageView. It works with both static and dynamic images
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
*
|
||||
* @param url The url for the image.
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url;
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url` and a placeholder.
|
||||
|
@ -37,7 +37,7 @@
|
|||
* @see sd_setImageWithURL:placeholderImage:options:
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder;
|
||||
placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
@ -51,7 +51,7 @@
|
|||
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`.
|
||||
|
@ -83,7 +83,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
|
|
@ -371,8 +371,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
|
|||
image = [UIImage decodedImageWithImage:image];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
// Apple's defines from TargetConditionals.h are a bit weird.
|
||||
// Seems like TARGET_OS_MAC is always defined (on all platforms).
|
||||
// To determine if we are running on OSX, we can only relly on TARGET_OS_IPHONE=0 and all the other platforms
|
||||
// To determine if we are running on OSX, we can only rely on TARGET_OS_IPHONE=0 and all the other platforms
|
||||
#if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
|
||||
#define SD_MAC 1
|
||||
#else
|
||||
|
@ -93,11 +93,11 @@
|
|||
#define SDDispatchQueueSetterSementics assign
|
||||
#endif
|
||||
|
||||
extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
|
||||
FOUNDATION_EXPORT UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
|
||||
|
||||
typedef void(^SDWebImageNoParamsBlock)();
|
||||
typedef void(^SDWebImageNoParamsBlock)(void);
|
||||
|
||||
extern NSString *const SDWebImageErrorDomain;
|
||||
FOUNDATION_EXPORT NSString *const SDWebImageErrorDomain;
|
||||
|
||||
#ifndef dispatch_main_async_safe
|
||||
#define dispatch_main_async_safe(block)\
|
||||
|
@ -107,5 +107,3 @@ extern NSString *const SDWebImageErrorDomain;
|
|||
dispatch_async(dispatch_get_main_queue(), block);\
|
||||
}
|
||||
#endif
|
||||
|
||||
static int64_t kAsyncTestTimeout = 5;
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#import "SDWebImageCompat.h"
|
||||
|
||||
#import "objc/runtime.h"
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
|
||||
#endif
|
||||
|
@ -26,10 +28,20 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl
|
|||
for (UIImage *tempImage in image.images) {
|
||||
[scaledImages addObject:SDScaledImageForKey(key, tempImage)];
|
||||
}
|
||||
|
||||
return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
|
||||
}
|
||||
else {
|
||||
|
||||
UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];
|
||||
#ifdef SD_WEBP
|
||||
if (animatedImage) {
|
||||
SEL sd_webpLoopCount = NSSelectorFromString(@"sd_webpLoopCount");
|
||||
NSNumber *value = objc_getAssociatedObject(image, sd_webpLoopCount);
|
||||
NSInteger loopCount = value.integerValue;
|
||||
if (loopCount) {
|
||||
objc_setAssociatedObject(animatedImage, sd_webpLoopCount, @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return animatedImage;
|
||||
} else {
|
||||
#if SD_WATCH
|
||||
if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
|
||||
#elif SD_UIKIT
|
||||
|
|
|
@ -115,16 +115,10 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
|
||||
size_t bytesPerRow = kBytesPerPixel * destResolution.width;
|
||||
|
||||
// Allocate enough pixel data to hold the output image.
|
||||
void* destBitmapData = malloc( bytesPerRow * destResolution.height );
|
||||
if (destBitmapData == NULL) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
|
||||
// Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
|
||||
// to create bitmap graphics contexts without alpha info.
|
||||
destContext = CGBitmapContextCreate(destBitmapData,
|
||||
destContext = CGBitmapContextCreate(NULL,
|
||||
destResolution.width,
|
||||
destResolution.height,
|
||||
kBitsPerComponent,
|
||||
|
@ -133,7 +127,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
|
||||
|
||||
if (destContext == NULL) {
|
||||
free(destBitmapData);
|
||||
return image;
|
||||
}
|
||||
CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh);
|
||||
|
|
|
@ -68,8 +68,8 @@ typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
|
|||
SDWebImageDownloaderLIFOExecutionOrder
|
||||
};
|
||||
|
||||
extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
|
||||
extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;
|
||||
|
||||
typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL);
|
||||
|
||||
|
@ -119,6 +119,15 @@ typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterB
|
|||
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
|
||||
|
||||
|
||||
/**
|
||||
* The configuration in use by the internal NSURLSession.
|
||||
* Mutating this object directly has no effect.
|
||||
*
|
||||
* @see createNewSessionWithConfiguration:
|
||||
*/
|
||||
@property (readonly, nonatomic, nonnull) NSURLSessionConfiguration *sessionConfiguration;
|
||||
|
||||
|
||||
/**
|
||||
* Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`.
|
||||
*/
|
||||
|
@ -230,4 +239,14 @@ typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterB
|
|||
*/
|
||||
- (void)cancelAllDownloads;
|
||||
|
||||
/**
|
||||
* Forces SDWebImageDownloader to create and use a new NSURLSession that is
|
||||
* initialized with the given configuration.
|
||||
* *Note*: All existing download operations in the queue will be cancelled.
|
||||
* *Note*: `timeoutIntervalForRequest` is going to be overwritten.
|
||||
*
|
||||
* @param sessionConfiguration The configuration to use for the new NSURLSession
|
||||
*/
|
||||
- (void)createNewSessionWithConfiguration:(nonnull NSURLSessionConfiguration *)sessionConfiguration;
|
||||
|
||||
@end
|
||||
|
|
|
@ -84,20 +84,30 @@
|
|||
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
_downloadTimeout = 15.0;
|
||||
|
||||
sessionConfiguration.timeoutIntervalForRequest = _downloadTimeout;
|
||||
|
||||
/**
|
||||
* Create the session for this task
|
||||
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
|
||||
* method calls and completion handler calls.
|
||||
*/
|
||||
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
[self createNewSessionWithConfiguration:sessionConfiguration];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)createNewSessionWithConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
|
||||
[self cancelAllDownloads];
|
||||
|
||||
if (self.session) {
|
||||
[self.session invalidateAndCancel];
|
||||
}
|
||||
|
||||
sessionConfiguration.timeoutIntervalForRequest = self.downloadTimeout;
|
||||
|
||||
/**
|
||||
* Create the session for this task
|
||||
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
|
||||
* method calls and completion handler calls.
|
||||
*/
|
||||
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self.session invalidateAndCancel];
|
||||
self.session = nil;
|
||||
|
@ -109,8 +119,7 @@
|
|||
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
|
||||
if (value) {
|
||||
self.HTTPHeaders[field] = value;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[self.HTTPHeaders removeObjectForKey:field];
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +140,10 @@
|
|||
return _downloadQueue.maxConcurrentOperationCount;
|
||||
}
|
||||
|
||||
- (NSURLSessionConfiguration *)sessionConfiguration {
|
||||
return self.session.configuration;
|
||||
}
|
||||
|
||||
- (void)setOperationClass:(nullable Class)operationClass {
|
||||
if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
|
||||
_operationClass = operationClass;
|
||||
|
@ -211,7 +224,7 @@
|
|||
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
|
||||
forURL:(nullable NSURL *)url
|
||||
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
|
||||
createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback {
|
||||
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
|
||||
if (url == nil) {
|
||||
if (completedBlock != nil) {
|
||||
|
@ -230,11 +243,13 @@
|
|||
|
||||
__weak SDWebImageDownloaderOperation *woperation = operation;
|
||||
operation.completionBlock = ^{
|
||||
SDWebImageDownloaderOperation *soperation = woperation;
|
||||
if (!soperation) return;
|
||||
if (self.URLOperations[url] == soperation) {
|
||||
[self.URLOperations removeObjectForKey:url];
|
||||
};
|
||||
dispatch_barrier_sync(self.barrierQueue, ^{
|
||||
SDWebImageDownloaderOperation *soperation = woperation;
|
||||
if (!soperation) return;
|
||||
if (self.URLOperations[url] == soperation) {
|
||||
[self.URLOperations removeObjectForKey:url];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
|
||||
|
@ -248,7 +263,7 @@
|
|||
}
|
||||
|
||||
- (void)setSuspended:(BOOL)suspended {
|
||||
(self.downloadQueue).suspended = suspended;
|
||||
self.downloadQueue.suspended = suspended;
|
||||
}
|
||||
|
||||
- (void)cancelAllDownloads {
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
#import "SDWebImageDownloader.h"
|
||||
#import "SDWebImageOperation.h"
|
||||
|
||||
extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
|
||||
extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
|
||||
extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
|
||||
extern NSString * _Nonnull const SDWebImageDownloadFinishNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -169,8 +169,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
|
|||
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
|
||||
progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:weakSelf];
|
||||
});
|
||||
} else {
|
||||
[self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}]];
|
||||
|
@ -201,8 +202,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
|
|||
|
||||
if (self.dataTask) {
|
||||
[self.dataTask cancel];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
|
||||
});
|
||||
|
||||
// As we cancelled the connection, its callback won't be called and thus won't
|
||||
|
@ -221,8 +223,9 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
|
|||
}
|
||||
|
||||
- (void)reset {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_barrier_async(self.barrierQueue, ^{
|
||||
[self.callbackBlocks removeAllObjects];
|
||||
[weakSelf.callbackBlocks removeAllObjects];
|
||||
});
|
||||
self.dataTask = nil;
|
||||
self.imageData = nil;
|
||||
|
@ -266,11 +269,11 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
|
||||
self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
|
||||
self.response = response;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:weakSelf];
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
NSUInteger code = ((NSHTTPURLResponse *)response).statusCode;
|
||||
|
||||
//This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
|
||||
|
@ -280,8 +283,9 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
} else {
|
||||
[self.dataTask cancel];
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
|
||||
});
|
||||
|
||||
[self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:((NSHTTPURLResponse *)response).statusCode userInfo:nil]];
|
||||
|
@ -373,7 +377,9 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
}
|
||||
}
|
||||
|
||||
CFRelease(imageSource);
|
||||
if (imageSource) {
|
||||
CFRelease(imageSource);
|
||||
}
|
||||
}
|
||||
|
||||
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
|
||||
|
@ -402,10 +408,11 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
@synchronized(self) {
|
||||
self.dataTask = nil;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
|
||||
if (!error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:weakSelf];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls,
|
|||
/**
|
||||
* Queue options for Prefetcher. Defaults to Main Queue.
|
||||
*/
|
||||
@property (nonatomic, assign, nonnull) dispatch_queue_t prefetcherQueue;
|
||||
@property (SDDispatchQueueSetterSementics, nonatomic, nonnull) dispatch_queue_t prefetcherQueue;
|
||||
|
||||
@property (weak, nonatomic, nullable) id <SDWebImagePrefetcherDelegate> delegate;
|
||||
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
*/
|
||||
@interface UIButton (WebCache)
|
||||
|
||||
#pragma mark - Image
|
||||
|
||||
/**
|
||||
* Get the current image URL.
|
||||
*/
|
||||
- (nullable NSURL *)sd_currentImageURL;
|
||||
|
||||
#pragma mark - Image
|
||||
|
||||
/**
|
||||
* Get the image URL for a control state.
|
||||
*
|
||||
|
@ -40,7 +40,7 @@
|
|||
* @param state The state that uses the specified title. The values are described in UIControlState.
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state;
|
||||
forState:(UIControlState)state NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url` and a placeholder.
|
||||
|
@ -54,7 +54,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder;
|
||||
placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
@ -69,7 +69,7 @@
|
|||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`.
|
||||
|
@ -105,7 +105,7 @@
|
|||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
@ -130,6 +130,18 @@
|
|||
|
||||
#pragma mark - Background image
|
||||
|
||||
/**
|
||||
* Get the current background image URL.
|
||||
*/
|
||||
- (nullable NSURL *)sd_currentBackgroundImageURL;
|
||||
|
||||
/**
|
||||
* Get the background image URL for a control state.
|
||||
*
|
||||
* @param state Which state you want to know the URL for. The values are described in UIControlState.
|
||||
*/
|
||||
- (nullable NSURL *)sd_backgroundImageURLForState:(UIControlState)state;
|
||||
|
||||
/**
|
||||
* Set the backgroundImageView `image` with an `url`.
|
||||
*
|
||||
|
@ -139,7 +151,7 @@
|
|||
* @param state The state that uses the specified title. The values are described in UIControlState.
|
||||
*/
|
||||
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state;
|
||||
forState:(UIControlState)state NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the backgroundImageView `image` with an `url` and a placeholder.
|
||||
|
@ -153,7 +165,7 @@
|
|||
*/
|
||||
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder;
|
||||
placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
|
||||
|
@ -168,7 +180,7 @@
|
|||
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the backgroundImageView `image` with an `url`.
|
||||
|
@ -204,7 +216,7 @@
|
|||
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
|
||||
forState:(UIControlState)state
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the backgroundImageView `image` with an `url`, placeholder and custom options.
|
||||
|
|
|
@ -16,26 +16,34 @@
|
|||
|
||||
static char imageURLStorageKey;
|
||||
|
||||
typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
|
||||
typedef NSMutableDictionary<NSString *, NSURL *> SDStateImageURLDictionary;
|
||||
|
||||
static inline NSString * imageURLKeyForState(UIControlState state) {
|
||||
return [NSString stringWithFormat:@"image_%lu", (unsigned long)state];
|
||||
}
|
||||
|
||||
static inline NSString * backgroundImageURLKeyForState(UIControlState state) {
|
||||
return [NSString stringWithFormat:@"backgroundImage_%lu", (unsigned long)state];
|
||||
}
|
||||
|
||||
@implementation UIButton (WebCache)
|
||||
|
||||
#pragma mark - Image
|
||||
|
||||
- (nullable NSURL *)sd_currentImageURL {
|
||||
NSURL *url = self.imageURLStorage[@(self.state)];
|
||||
NSURL *url = self.imageURLStorage[imageURLKeyForState(self.state)];
|
||||
|
||||
if (!url) {
|
||||
url = self.imageURLStorage[@(UIControlStateNormal)];
|
||||
url = self.imageURLStorage[imageURLKeyForState(UIControlStateNormal)];
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
- (nullable NSURL *)sd_imageURLForState:(UIControlState)state {
|
||||
return self.imageURLStorage[@(state)];
|
||||
return self.imageURLStorage[imageURLKeyForState(state)];
|
||||
}
|
||||
|
||||
#pragma mark - Image
|
||||
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
|
||||
[self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
|
||||
}
|
||||
|
@ -62,11 +70,11 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
|
|||
options:(SDWebImageOptions)options
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock {
|
||||
if (!url) {
|
||||
[self.imageURLStorage removeObjectForKey:@(state)];
|
||||
[self.imageURLStorage removeObjectForKey:imageURLKeyForState(state)];
|
||||
return;
|
||||
}
|
||||
|
||||
self.imageURLStorage[@(state)] = url;
|
||||
self.imageURLStorage[imageURLKeyForState(state)] = url;
|
||||
|
||||
__weak typeof(self)weakSelf = self;
|
||||
[self sd_internalSetImageWithURL:url
|
||||
|
@ -82,6 +90,20 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
|
|||
|
||||
#pragma mark - Background image
|
||||
|
||||
- (nullable NSURL *)sd_currentBackgroundImageURL {
|
||||
NSURL *url = self.imageURLStorage[backgroundImageURLKeyForState(self.state)];
|
||||
|
||||
if (!url) {
|
||||
url = self.imageURLStorage[backgroundImageURLKeyForState(UIControlStateNormal)];
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
- (nullable NSURL *)sd_backgroundImageURLForState:(UIControlState)state {
|
||||
return self.imageURLStorage[backgroundImageURLKeyForState(state)];
|
||||
}
|
||||
|
||||
- (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
|
||||
[self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
|
||||
}
|
||||
|
@ -108,11 +130,11 @@ typedef NSMutableDictionary<NSNumber *, NSURL *> SDStateImageURLDictionary;
|
|||
options:(SDWebImageOptions)options
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock {
|
||||
if (!url) {
|
||||
[self.imageURLStorage removeObjectForKey:@(state)];
|
||||
[self.imageURLStorage removeObjectForKey:backgroundImageURLKeyForState(state)];
|
||||
return;
|
||||
}
|
||||
|
||||
self.imageURLStorage[@(state)] = url;
|
||||
self.imageURLStorage[backgroundImageURLKeyForState(state)] = url;
|
||||
|
||||
__weak typeof(self)weakSelf = self;
|
||||
[self sd_internalSetImageWithURL:url
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if SD_MAC
|
||||
return [[UIImage alloc] initWithData:data];
|
||||
#else
|
||||
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
|
||||
|
||||
|
@ -42,8 +46,6 @@
|
|||
#if SD_UIKIT || SD_WATCH
|
||||
UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
|
||||
staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
|
||||
#elif SD_MAC
|
||||
staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];
|
||||
#endif
|
||||
CGImageRelease(CGImage);
|
||||
}
|
||||
|
@ -51,6 +53,7 @@
|
|||
CFRelease(source);
|
||||
|
||||
return staticImage;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)isGIF {
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
|
||||
@interface UIImage (WebP)
|
||||
|
||||
/**
|
||||
* Get the current WebP image loop count, the default value is 0.
|
||||
* For static WebP image, the value is 0.
|
||||
* For animated WebP image, 0 means repeat the animation indefinitely.
|
||||
* Note that because of the limitations of categories this property can get out of sync
|
||||
* if you create another instance with CGImage or other methods.
|
||||
* @return WebP image loop count
|
||||
*/
|
||||
- (NSInteger)sd_webpLoopCount;
|
||||
|
||||
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data;
|
||||
|
||||
@end
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#import "webp/demux.h"
|
||||
#import "NSImage+WebCache.h"
|
||||
|
||||
#import "objc/runtime.h"
|
||||
|
||||
// Callback for CGDataProviderRelease
|
||||
static void FreeImageData(void *info, const void *data, size_t size) {
|
||||
free((void *)data);
|
||||
|
@ -21,6 +23,12 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
|
||||
@implementation UIImage (WebP)
|
||||
|
||||
- (NSInteger)sd_webpLoopCount
|
||||
{
|
||||
NSNumber *value = objc_getAssociatedObject(self, @selector(sd_webpLoopCount));
|
||||
return value.integerValue;
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
|
@ -38,7 +46,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
|
||||
if (!(flags & ANIMATION_FLAG)) {
|
||||
// for static single webp image
|
||||
UIImage *staticImage = [self sd_rawWepImageWithData:webpData];
|
||||
UIImage *staticImage = [self sd_rawWebpImageWithData:webpData];
|
||||
WebPDemuxDelete(demuxer);
|
||||
return staticImage;
|
||||
}
|
||||
|
@ -50,15 +58,37 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray *images = [NSMutableArray array];
|
||||
NSTimeInterval duration = 0;
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
|
||||
int frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
|
||||
#endif
|
||||
int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
|
||||
int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
|
||||
CGBitmapInfo bitmapInfo;
|
||||
if (!(flags & ALPHA_FLAG)) {
|
||||
bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast;
|
||||
} else {
|
||||
bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast;
|
||||
}
|
||||
CGContextRef canvas = CGBitmapContextCreate(NULL, canvasWidth, canvasHeight, 8, 0, SDCGColorSpaceGetDeviceRGB(), bitmapInfo);
|
||||
if (!canvas) {
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demuxer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableArray<UIImage *> *images = [NSMutableArray array];
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
NSTimeInterval totalDuration = 0;
|
||||
int durations[frameCount];
|
||||
#endif
|
||||
|
||||
do {
|
||||
UIImage *image;
|
||||
if (iter.blend_method == WEBP_MUX_BLEND) {
|
||||
image = [self sd_blendWebpImageWithOriginImage:[images lastObject] iterator:iter];
|
||||
image = [self sd_blendWebpImageWithCanvas:canvas iterator:iter];
|
||||
} else {
|
||||
image = [self sd_rawWepImageWithData:iter.fragment];
|
||||
image = [self sd_nonblendWebpImageWithCanvas:canvas iterator:iter];
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
|
@ -66,46 +96,56 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
}
|
||||
|
||||
[images addObject:image];
|
||||
duration += iter.duration / 1000.0f;
|
||||
|
||||
#if SD_MAC
|
||||
break;
|
||||
#else
|
||||
|
||||
int duration = iter.duration;
|
||||
if (duration <= 10) {
|
||||
// WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms
|
||||
// Some animated WebP images also created without duration, we should keep compatibility
|
||||
duration = 100;
|
||||
}
|
||||
totalDuration += duration;
|
||||
size_t count = images.count;
|
||||
durations[count - 1] = duration;
|
||||
#endif
|
||||
} while (WebPDemuxNextFrame(&iter));
|
||||
|
||||
WebPDemuxReleaseIterator(&iter);
|
||||
WebPDemuxDelete(demuxer);
|
||||
CGContextRelease(canvas);
|
||||
|
||||
UIImage *finalImage = nil;
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
finalImage = [UIImage animatedImageWithImages:images duration:duration];
|
||||
#elif SD_MAC
|
||||
if ([images count] > 0) {
|
||||
finalImage = images[0];
|
||||
NSArray<UIImage *> *animatedImages = [self sd_animatedImagesWithImages:images durations:durations totalDuration:totalDuration];
|
||||
finalImage = [UIImage animatedImageWithImages:animatedImages duration:totalDuration / 1000.0];
|
||||
if (finalImage) {
|
||||
objc_setAssociatedObject(finalImage, @selector(sd_webpLoopCount), @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
#elif SD_MAC
|
||||
finalImage = images.firstObject;
|
||||
#endif
|
||||
return finalImage;
|
||||
}
|
||||
|
||||
|
||||
+ (nullable UIImage *)sd_blendWebpImageWithOriginImage:(nullable UIImage *)originImage iterator:(WebPIterator)iter {
|
||||
if (!originImage) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGSize size = originImage.size;
|
||||
CGFloat tmpX = iter.x_offset;
|
||||
CGFloat tmpY = size.height - iter.height - iter.y_offset;
|
||||
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
|
||||
|
||||
UIImage *image = [self sd_rawWepImageWithData:iter.fragment];
|
||||
+ (nullable UIImage *)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter {
|
||||
UIImage *image = [self sd_rawWebpImageWithData:iter.fragment];
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
||||
uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0;
|
||||
CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo);
|
||||
CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage);
|
||||
CGContextDrawImage(blendCanvas, imageRect, image.CGImage);
|
||||
CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas);
|
||||
size_t canvasWidth = CGBitmapContextGetWidth(canvas);
|
||||
size_t canvasHeight = CGBitmapContextGetHeight(canvas);
|
||||
CGSize size = CGSizeMake(canvasWidth, canvasHeight);
|
||||
CGFloat tmpX = iter.x_offset;
|
||||
CGFloat tmpY = size.height - iter.height - iter.y_offset;
|
||||
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
|
||||
|
||||
CGContextDrawImage(canvas, imageRect, image.CGImage);
|
||||
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
|
||||
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
image = [UIImage imageWithCGImage:newImageRef];
|
||||
|
@ -114,13 +154,47 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
#endif
|
||||
|
||||
CGImageRelease(newImageRef);
|
||||
CGContextRelease(blendCanvas);
|
||||
CGColorSpaceRelease(colorSpaceRef);
|
||||
|
||||
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
CGContextClearRect(canvas, imageRect);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)sd_rawWepImageWithData:(WebPData)webpData {
|
||||
+ (nullable UIImage *)sd_nonblendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter {
|
||||
UIImage *image = [self sd_rawWebpImageWithData:iter.fragment];
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
size_t canvasWidth = CGBitmapContextGetWidth(canvas);
|
||||
size_t canvasHeight = CGBitmapContextGetHeight(canvas);
|
||||
CGSize size = CGSizeMake(canvasWidth, canvasHeight);
|
||||
CGFloat tmpX = iter.x_offset;
|
||||
CGFloat tmpY = size.height - iter.height - iter.y_offset;
|
||||
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
|
||||
|
||||
CGContextClearRect(canvas, imageRect);
|
||||
CGContextDrawImage(canvas, imageRect, image.CGImage);
|
||||
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
|
||||
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
image = [UIImage imageWithCGImage:newImageRef];
|
||||
#elif SD_MAC
|
||||
image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize];
|
||||
#endif
|
||||
|
||||
CGImageRelease(newImageRef);
|
||||
|
||||
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
|
||||
CGContextClearRect(canvas, imageRect);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)sd_rawWebpImageWithData:(WebPData)webpData {
|
||||
WebPDecoderConfig config;
|
||||
if (!WebPInitDecoderConfig(&config)) {
|
||||
return nil;
|
||||
|
@ -133,7 +207,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB;
|
||||
config.options.use_threads = 1;
|
||||
|
||||
// Decode the WebP image data into a RGBA value array.
|
||||
// Decode the WebP image data into a RGBA value array
|
||||
if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
|
||||
return nil;
|
||||
}
|
||||
|
@ -145,16 +219,15 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
height = config.options.scaled_height;
|
||||
}
|
||||
|
||||
// Construct a UIImage from the decoded RGBA value array.
|
||||
// Construct a UIImage from the decoded RGBA value array
|
||||
CGDataProviderRef provider =
|
||||
CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData);
|
||||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0;
|
||||
CGColorSpaceRef colorSpaceRef = SDCGColorSpaceGetDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast;
|
||||
size_t components = config.input.has_alpha ? 4 : 3;
|
||||
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
|
||||
CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
|
||||
|
||||
CGColorSpaceRelease(colorSpaceRef);
|
||||
CGDataProviderRelease(provider);
|
||||
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
|
@ -167,6 +240,63 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
return image;
|
||||
}
|
||||
|
||||
+ (NSArray<UIImage *> *)sd_animatedImagesWithImages:(NSArray<UIImage *> *)images durations:(int const * const)durations totalDuration:(NSTimeInterval)totalDuration
|
||||
{
|
||||
// [UIImage animatedImageWithImages:duration:] only use the average duration for per frame
|
||||
// divide the total duration to implement per frame duration for animated WebP
|
||||
NSUInteger count = images.count;
|
||||
if (!count) {
|
||||
return nil;
|
||||
}
|
||||
if (count == 1) {
|
||||
return images;
|
||||
}
|
||||
|
||||
int const gcd = gcdArray(count, durations);
|
||||
NSMutableArray<UIImage *> *animatedImages = [NSMutableArray arrayWithCapacity:count];
|
||||
[images enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
int duration = durations[idx];
|
||||
int repeatCount;
|
||||
if (gcd) {
|
||||
repeatCount = duration / gcd;
|
||||
} else {
|
||||
repeatCount = 1;
|
||||
}
|
||||
for (int i = 0; i < repeatCount; ++i) {
|
||||
[animatedImages addObject:image];
|
||||
}
|
||||
}];
|
||||
|
||||
return animatedImages;
|
||||
}
|
||||
|
||||
static CGColorSpaceRef SDCGColorSpaceGetDeviceRGB() {
|
||||
static CGColorSpaceRef space;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
space = CGColorSpaceCreateDeviceRGB();
|
||||
});
|
||||
return space;
|
||||
}
|
||||
|
||||
static int gcdArray(size_t const count, int const * const values) {
|
||||
int result = values[0];
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
result = gcd(values[i], result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int gcd(int a,int b) {
|
||||
int c;
|
||||
while (a != 0) {
|
||||
c = a;
|
||||
a = b % a;
|
||||
b = c;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*
|
||||
* @param url The url for the image.
|
||||
*/
|
||||
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url;
|
||||
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `highlightedImage` with an `url` and custom options.
|
||||
|
@ -35,7 +35,7 @@
|
|||
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
|
||||
*/
|
||||
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `highlightedImage` with an `url`.
|
||||
|
@ -50,7 +50,7 @@
|
|||
* The fourth parameter is the original image url.
|
||||
*/
|
||||
- (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `highlightedImage` with an `url` and custom options.
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
*
|
||||
* @param url The url for the image.
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url;
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url` and a placeholder.
|
||||
|
@ -66,7 +66,7 @@
|
|||
* @see sd_setImageWithURL:placeholderImage:options:
|
||||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder;
|
||||
placeholderImage:(nullable UIImage *)placeholder NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
@ -79,7 +79,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options;
|
||||
options:(SDWebImageOptions)options NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`.
|
||||
|
@ -111,7 +111,7 @@
|
|||
*/
|
||||
- (void)sd_setImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url`, placeholder and custom options.
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
NSMutableArray<id<SDWebImageOperation>> *operationsArray = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSURL *logoImageURL in arrayOfURLs) {
|
||||
[arrayOfURLs enumerateObjectsUsingBlock:^(NSURL *logoImageURL, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
||||
if (!wself) return;
|
||||
dispatch_main_async_safe(^{
|
||||
|
@ -86,7 +86,16 @@
|
|||
if (!currentImages) {
|
||||
currentImages = [[NSMutableArray alloc] init];
|
||||
}
|
||||
[currentImages addObject:image];
|
||||
|
||||
// We know what index objects should be at when they are returned so
|
||||
// we will put the object at the index, filling any empty indexes
|
||||
// with the image that was returned too "early". These images will
|
||||
// be overwritten. (does not require additional sorting datastructure)
|
||||
while ([currentImages count] < idx) {
|
||||
[currentImages addObject:image];
|
||||
}
|
||||
|
||||
currentImages[idx] = image;
|
||||
|
||||
sself.animationImages = currentImages;
|
||||
[sself setNeedsLayout];
|
||||
|
@ -95,7 +104,7 @@
|
|||
});
|
||||
}];
|
||||
[operationsArray addObject:operation];
|
||||
}
|
||||
}];
|
||||
|
||||
[self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */; };
|
||||
37D122881EC48B5E00D98CEB /* MockFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 37D122871EC48B5E00D98CEB /* MockFileManager.m */; };
|
||||
2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D7AF05F1F329763000083C2 /* SDTestCase.m */; };
|
||||
433BBBB51D7EF5C00086B6E9 /* SDWebImageDecoderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */; };
|
||||
433BBBB71D7EF8200086B6E9 /* TestImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 433BBBB61D7EF8200086B6E9 /* TestImage.gif */; };
|
||||
433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 433BBBB81D7EF8260086B6E9 /* TestImage.png */; };
|
||||
|
@ -32,6 +33,8 @@
|
|||
1E3C51E819B46E370092B5E6 /* SDWebImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderTests.m; sourceTree = "<group>"; };
|
||||
37D122861EC48B5E00D98CEB /* MockFileManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockFileManager.h; sourceTree = "<group>"; };
|
||||
37D122871EC48B5E00D98CEB /* MockFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MockFileManager.m; sourceTree = "<group>"; };
|
||||
2D7AF05E1F329763000083C2 /* SDTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDTestCase.h; sourceTree = "<group>"; };
|
||||
2D7AF05F1F329763000083C2 /* SDTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDTestCase.m; sourceTree = "<group>"; };
|
||||
433BBBB41D7EF5C00086B6E9 /* SDWebImageDecoderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDecoderTests.m; sourceTree = "<group>"; };
|
||||
433BBBB61D7EF8200086B6E9 /* TestImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = TestImage.gif; sourceTree = "<group>"; };
|
||||
433BBBB81D7EF8260086B6E9 /* TestImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestImage.png; sourceTree = "<group>"; };
|
||||
|
@ -125,6 +128,8 @@
|
|||
4369C2731D9804B1007E863A /* SDCategoriesTests.m */,
|
||||
37D122861EC48B5E00D98CEB /* MockFileManager.h */,
|
||||
37D122871EC48B5E00D98CEB /* MockFileManager.m */,
|
||||
2D7AF05E1F329763000083C2 /* SDTestCase.h */,
|
||||
2D7AF05F1F329763000083C2 /* SDTestCase.m */,
|
||||
);
|
||||
path = Tests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -259,6 +264,7 @@
|
|||
1E3C51E919B46E370092B5E6 /* SDWebImageDownloaderTests.m in Sources */,
|
||||
37D122881EC48B5E00D98CEB /* MockFileManager.m in Sources */,
|
||||
4369C2741D9804B1007E863A /* SDCategoriesTests.m in Sources */,
|
||||
2D7AF0601F329763000083C2 /* SDTestCase.m in Sources */,
|
||||
4369C1D11D97F80F007E863A /* SDWebImagePrefetcherTests.m in Sources */,
|
||||
DA248D69195475D800390AB0 /* SDImageCacheTests.m in Sources */,
|
||||
DA248D6B195476AC00390AB0 /* SDWebImageManagerTests.m in Sources */,
|
||||
|
|
|
@ -7,11 +7,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/UIImageView+WebCache.h>
|
||||
#import <SDWebImage/UIImageView+HighlightedWebCache.h>
|
||||
#import <SDWebImage/MKAnnotationView+WebCache.h>
|
||||
|
@ -20,7 +16,7 @@
|
|||
|
||||
@import FLAnimatedImage;
|
||||
|
||||
@interface SDCategoriesTests : XCTestCase
|
||||
@interface SDCategoriesTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
@ -39,7 +35,7 @@
|
|||
expect(imageView.image).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testUIImageViewSetHighlightedImageWithURL {
|
||||
|
@ -55,7 +51,7 @@
|
|||
expect(imageView.highlightedImage).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testMKAnnotationViewSetImageWithURL {
|
||||
|
@ -71,7 +67,7 @@
|
|||
expect(annotationView.image).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testUIButtonSetImageWithURLNormalState {
|
||||
|
@ -88,7 +84,7 @@
|
|||
expect([button imageForState:UIControlStateNormal]).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testUIButtonSetImageWithURLHighlightedState {
|
||||
|
@ -105,7 +101,7 @@
|
|||
expect([button imageForState:UIControlStateHighlighted]).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testUIButtonSetBackgroundImageWithURLNormalState {
|
||||
|
@ -122,7 +118,7 @@
|
|||
expect([button backgroundImageForState:UIControlStateNormal]).to.equal(image);
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)testFLAnimatedImageViewSetImageWithURL {
|
||||
|
@ -140,7 +136,7 @@
|
|||
expect(imageView.animatedImage).toNot.beNil();
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,18 +6,13 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/SDImageCache.h>
|
||||
#import "MockFileManager.h"
|
||||
|
||||
NSString *kImageTestKey = @"TestImageKey.jpg";
|
||||
|
||||
@interface SDImageCacheTests : XCTestCase
|
||||
@interface SDImageCacheTests : SDTestCase
|
||||
@property (strong, nonatomic) SDImageCache *sharedImageCache;
|
||||
@end
|
||||
|
||||
|
@ -57,7 +52,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
|
|||
}];
|
||||
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.equal([self imageForTesting]);
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test05ClearMemoryCache{
|
||||
|
@ -73,7 +68,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
|
|||
XCTFail(@"Image should be in cache");
|
||||
}
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
// Testing storeImage:forKey:
|
||||
|
@ -90,7 +85,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
|
|||
XCTFail(@"Image should be in cache");
|
||||
}
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
// Testing storeImage:forKey:toDisk:YES
|
||||
|
@ -107,7 +102,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
|
|||
XCTFail(@"Image should be in cache");
|
||||
}
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
// Testing storeImage:forKey:toDisk:NO
|
||||
|
@ -126,7 +121,7 @@ NSString *kImageTestKey = @"TestImageKey.jpg";
|
|||
}];
|
||||
[self.sharedImageCache clearMemory];
|
||||
expect([self.sharedImageCache imageFromMemoryCacheForKey:kImageTestKey]).to.beNil();
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test09RetrieveImageThroughNSOperation{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
* (c) Matt Galloway
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
extern const int64_t kAsyncTestTimeout;
|
||||
|
||||
@interface SDTestCase : XCTestCase
|
||||
|
||||
- (void)waitForExpectationsWithCommonTimeout;
|
||||
- (void)waitForExpectationsWithCommonTimeoutUsingHandler:(XCWaitCompletionHandler)handler;
|
||||
|
||||
@end
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
* (c) Matt Galloway
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDTestCase.h"
|
||||
|
||||
const int64_t kAsyncTestTimeout = 5;
|
||||
|
||||
@implementation SDTestCase
|
||||
|
||||
- (void)waitForExpectationsWithCommonTimeout {
|
||||
[self waitForExpectationsWithCommonTimeoutUsingHandler:nil];
|
||||
}
|
||||
|
||||
- (void)waitForExpectationsWithCommonTimeoutUsingHandler:(XCWaitCompletionHandler)handler {
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:handler];
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,14 +7,10 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/SDWebImageDecoder.h>
|
||||
|
||||
@interface SDWebImageDecoderTests : XCTestCase
|
||||
@interface SDWebImageDecoderTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/SDWebImageDownloader.h>
|
||||
#import <SDWebImage/SDWebImageDownloaderOperation.h>
|
||||
|
||||
|
@ -55,7 +50,7 @@
|
|||
|
||||
|
||||
|
||||
@interface SDWebImageDownloaderTests : XCTestCase
|
||||
@interface SDWebImageDownloaderTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
@ -91,8 +86,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test05ThatSetAndGetMaxConcurrentDownloadsWorks {
|
||||
|
@ -142,8 +136,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
[SDWebImageDownloader sharedDownloader].username = nil;
|
||||
[SDWebImageDownloader sharedDownloader].password = nil;
|
||||
}
|
||||
|
@ -160,8 +153,7 @@
|
|||
// progressive updates
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test10That404CaseCallsCompletionWithError {
|
||||
|
@ -175,8 +167,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test11ThatCancelWorks {
|
||||
|
@ -197,7 +188,7 @@
|
|||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test12ThatWeCanUseAnotherSessionForEachDownloadOperation {
|
||||
|
@ -223,7 +214,7 @@
|
|||
|
||||
[operation start];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test13ThatDownloadCanContinueWhenTheAppEntersBackground {
|
||||
|
@ -236,8 +227,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test14ThatPNGWorks {
|
||||
|
@ -250,8 +240,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test15ThatWEBPWorks {
|
||||
|
@ -264,8 +253,7 @@
|
|||
XCTFail(@"Something went wrong");
|
||||
}
|
||||
}];
|
||||
expect([SDWebImageDownloader sharedDownloader].currentDownloadCount).to.equal(1);
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,7 +291,7 @@
|
|||
|
||||
[[SDWebImageDownloader sharedDownloader] cancel:token1];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -342,7 +330,7 @@
|
|||
}];
|
||||
expect(token2).toNot.beNil();
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,17 +6,12 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/SDWebImageManager.h>
|
||||
|
||||
NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-images/FICDDemoImage001.jpg";
|
||||
|
||||
@interface SDWebImageManagerTests : XCTestCase
|
||||
@interface SDWebImageManagerTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
@ -45,7 +40,7 @@ NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-imag
|
|||
}];
|
||||
expect([[SDWebImageManager sharedManager] isRunning]).to.equal(YES);
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test03ThatDownloadWithIncorrectURLInvokesCompletionBlockWithAnErrorAsync {
|
||||
|
@ -65,7 +60,7 @@ NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-imag
|
|||
expectation = nil;
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test04CachedImageExistsForURL {
|
||||
|
@ -91,7 +86,7 @@ NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-imag
|
|||
XCTFail(@"Image should be in cache");
|
||||
}
|
||||
}];
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test06CancellAll {
|
||||
|
@ -110,7 +105,7 @@ NSString *workingImageURL = @"http://s3.amazonaws.com/fast-image-cache/demo-imag
|
|||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/SDWebImagePrefetcher.h>
|
||||
|
||||
@interface SDWebImagePrefetcherTests : XCTestCase
|
||||
@interface SDWebImagePrefetcherTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
@ -51,7 +46,7 @@
|
|||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
- (void)test03PrefetchWithEmptyArrayWillCallTheCompletionWithAllZeros {
|
||||
|
@ -63,7 +58,7 @@
|
|||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
// TODO: test the prefetcher delegate works
|
||||
|
|
|
@ -6,28 +6,51 @@
|
|||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#define EXP_SHORTHAND // required by Expecta
|
||||
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <Expecta/Expecta.h>
|
||||
|
||||
#import "SDTestCase.h"
|
||||
#import <SDWebImage/UIImage+MultiFormat.h>
|
||||
|
||||
|
||||
@interface UIImageMultiFormatTests : XCTestCase
|
||||
@interface UIImageMultiFormatTests : SDTestCase
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation UIImageMultiFormatTests
|
||||
|
||||
- (void)testImageOrientationFromImageDataWithInvalidData {
|
||||
- (void)test01ImageOrientationFromImageDataWithInvalidData {
|
||||
// sync download image
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
SEL selector = @selector(sd_imageOrientationFromImageData:);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
UIImageOrientation orientation = [[UIImage class] performSelector:selector withObject:nil];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
UIImageOrientation orientation = (UIImageOrientation)[[UIImage class] performSelector:selector withObject:nil];
|
||||
#pragma clang diagnostic pop
|
||||
expect(orientation).to.equal(UIImageOrientationUp);
|
||||
}
|
||||
|
||||
- (void)test02AnimatedWebPImageArrayWithEqualSizeAndScale {
|
||||
NSURL *webpURL = [NSURL URLWithString:@"https://isparta.github.io/compare-webp/image/gif_webp/webp/2.webp"];
|
||||
NSData *data = [NSData dataWithContentsOfURL:webpURL];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
SEL selector = @selector(sd_imageWithWebPData:);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
UIImage *animatedImage = [[UIImage class] performSelector:selector withObject:data];
|
||||
#pragma clang diagnostic pop
|
||||
CGSize imageSize = animatedImage.size;
|
||||
CGFloat imageScale = animatedImage.scale;
|
||||
[animatedImage.images enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
CGSize size = image.size;
|
||||
CGFloat scale = image.scale;
|
||||
expect(imageSize.width).to.equal(size.width);
|
||||
expect(imageSize.height).to.equal(size.height);
|
||||
expect(imageScale).to.equal(scale);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3d97bb75144147e47db278ec76e5e70c6b2243db
|
||||
Subproject commit 50d1a848bc56554af8413cfe681f94286a6371b3
|
|
@ -15,11 +15,11 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.0</string>
|
||||
<string>4.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4.0.0</string>
|
||||
<string>4.1.0</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
|
|
|
@ -34,16 +34,29 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
|
|||
#import <SDWebImage/UIImage+MultiFormat.h>
|
||||
#import <SDWebImage/SDWebImageOperation.h>
|
||||
#import <SDWebImage/SDWebImageDownloader.h>
|
||||
|
||||
#if SD_MAC || SD_UIKIT
|
||||
#import <SDWebImage/MKAnnotationView+WebCache.h>
|
||||
#import <SDWebImage/MKAnnotationView+WebCache.h>
|
||||
#endif
|
||||
|
||||
#import <SDWebImage/SDWebImageDecoder.h>
|
||||
#import <SDWebImage/UIImage+WebP.h>
|
||||
#import <SDWebImage/UIImage+GIF.h>
|
||||
#import <SDWebImage/NSData+ImageContentType.h>
|
||||
|
||||
#if SD_MAC
|
||||
#import <SDWebImage/NSImage+WebCache.h>
|
||||
#import <SDWebImage/NSImage+WebCache.h>
|
||||
#endif
|
||||
|
||||
#if SD_UIKIT
|
||||
#import <SDWebImage/FLAnimatedImageView+WebCache.h>
|
||||
#import <SDWebImage/FLAnimatedImageView+WebCache.h>
|
||||
|
||||
#if __has_include(<SDWebImage/FLAnimatedImage.h>)
|
||||
#import <SDWebImage/FLAnimatedImage.h>
|
||||
#endif
|
||||
|
||||
#if __has_include(<SDWebImage/FLAnimatedImageView.h>)
|
||||
#import <SDWebImage/FLAnimatedImageView.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue