diff --git a/Examples/SDWebImage Demo/MasterViewController.m b/Examples/SDWebImage Demo/MasterViewController.m index 504a3f49..bb94f078 100644 --- a/Examples/SDWebImage Demo/MasterViewController.m +++ b/Examples/SDWebImage Demo/MasterViewController.m @@ -70,6 +70,7 @@ @"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://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic", @"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png", @"http://via.placeholder.com/200x200.jpg", nil]; diff --git a/README.md b/README.md index 0998de70..39659d2f 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ [![Pod Version](http://img.shields.io/cocoapods/v/SDWebImage.svg?style=flat)](http://cocoadocs.org/docsets/SDWebImage/) [![Pod Platform](http://img.shields.io/cocoapods/p/SDWebImage.svg?style=flat)](http://cocoadocs.org/docsets/SDWebImage/) [![Pod License](http://img.shields.io/cocoapods/l/SDWebImage.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0.html) -[![Dependency Status](https://www.versioneye.com/objective-c/sdwebimage/badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage) -[![Reference Status](https://www.versioneye.com/objective-c/sdwebimage/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/sdwebimage/references) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/rs/SDWebImage) [![codecov](https://codecov.io/gh/rs/SDWebImage/branch/master/graph/badge.svg)](https://codecov.io/gh/rs/SDWebImage) diff --git a/SDWebImage/NSData+ImageContentType.h b/SDWebImage/NSData+ImageContentType.h index 3555c49b..8c48f671 100644 --- a/SDWebImage/NSData+ImageContentType.h +++ b/SDWebImage/NSData+ImageContentType.h @@ -22,6 +22,7 @@ static const SDImageFormat SDImageFormatGIF = 2; static const SDImageFormat SDImageFormatTIFF = 3; static const SDImageFormat SDImageFormatWebP = 4; static const SDImageFormat SDImageFormatHEIC = 5; +static const SDImageFormat SDImageFormatHEIF = 6; @interface NSData (ImageContentType) diff --git a/SDWebImage/NSData+ImageContentType.m b/SDWebImage/NSData+ImageContentType.m index 89d5ae29..192b539a 100644 --- a/SDWebImage/NSData+ImageContentType.m +++ b/SDWebImage/NSData+ImageContentType.m @@ -16,8 +16,9 @@ // Currently Image/IO does not support WebP #define kSDUTTypeWebP ((__bridge CFStringRef)@"public.webp") -// AVFileTypeHEIC is defined in AVFoundation via iOS 11, we use this without import AVFoundation +// AVFileTypeHEIC/AVFileTypeHEIF is defined in AVFoundation via iOS 11, we use this without import AVFoundation #define kSDUTTypeHEIC ((__bridge CFStringRef)@"public.heic") +#define kSDUTTypeHEIF ((__bridge CFStringRef)@"public.heif") @implementation NSData (ImageContentType) @@ -59,6 +60,10 @@ || [testString isEqualToString:@"ftyphevx"]) { return SDImageFormatHEIC; } + //....ftypmif1 ....ftypmsf1 + if ([testString isEqualToString:@"ftypmif1"] || [testString isEqualToString:@"ftypmsf1"]) { + return SDImageFormatHEIF; + } } break; } @@ -87,6 +92,9 @@ case SDImageFormatHEIC: UTType = kSDUTTypeHEIC; break; + case SDImageFormatHEIF: + UTType = kSDUTTypeHEIF; + break; default: // default is kUTTypePNG UTType = kUTTypePNG; @@ -112,6 +120,8 @@ imageFormat = SDImageFormatWebP; } else if (CFStringCompare(uttype, kSDUTTypeHEIC, 0) == kCFCompareEqualTo) { imageFormat = SDImageFormatHEIC; + } else if (CFStringCompare(uttype, kSDUTTypeHEIF, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatHEIF; } else { imageFormat = SDImageFormatUndefined; } diff --git a/SDWebImage/SDImageIOCoder.m b/SDWebImage/SDImageIOCoder.m index 11ef5cf0..28bad837 100644 --- a/SDWebImage/SDImageIOCoder.m +++ b/SDWebImage/SDImageIOCoder.m @@ -55,6 +55,9 @@ case SDImageFormatHEIC: // Check HEIC decoding compatibility return [[self class] canDecodeFromHEICFormat]; + case SDImageFormatHEIF: + // Check HEIF decoding compatibility + return [[self class] canDecodeFromHEIFFormat]; default: return YES; } @@ -87,6 +90,9 @@ case SDImageFormatHEIC: // Check HEIC decoding compatibility return [[self class] canDecodeFromHEICFormat]; + case SDImageFormatHEIF: + // Check HEIF decoding compatibility + return [[self class] canDecodeFromHEIFFormat]; default: return YES; } @@ -183,6 +189,9 @@ case SDImageFormatHEIC: // Check HEIC encoding compatibility return [[self class] canEncodeToHEICFormat]; + case SDImageFormatHEIF: + // Check HEIF encoding compatibility + return [[self class] canEncodeToHEIFFormat]; default: return YES; } @@ -243,15 +252,24 @@ static BOOL canDecode = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ -#if TARGET_OS_SIMULATOR || SD_WATCH - canDecode = NO; -#else - if (@available(iOS 11.0, tvOS 11.0, macOS 10.13, *)) { + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatHEIC]; + NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers(); + if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) { + canDecode = YES; + } + }); + return canDecode; +} + ++ (BOOL)canDecodeFromHEIFFormat { + static BOOL canDecode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatHEIF]; + NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers(); + if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) { canDecode = YES; - } else { - canDecode = NO; } -#endif }); return canDecode; } @@ -277,4 +295,25 @@ return canEncode; } ++ (BOOL)canEncodeToHEIFFormat { + static BOOL canEncode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableData *imageData = [NSMutableData data]; + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:SDImageFormatHEIF]; + + // Create an image destination. + CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL); + if (!imageDestination) { + // Can't encode to HEIF + canEncode = NO; + } else { + // Can encode to HEIF + CFRelease(imageDestination); + canEncode = YES; + } + }); + return canEncode; +} + @end diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index ec67b5bd..709ad389 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -92,11 +92,32 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext; _downloadQueue.maxConcurrentOperationCount = _config.maxConcurrentDownloads; _downloadQueue.name = @"com.hackemist.SDWebImageDownloader"; _URLOperations = [NSMutableDictionary new]; -#ifdef SD_WEBP - _HTTPHeaders = @{@"Accept": @"image/webp,image/*;q=0.8"}; -#else - _HTTPHeaders = @{@"Accept": @"image/*;q=0.8"}; + NSMutableDictionary *headerDictionary = [NSMutableDictionary dictionary]; + NSString *userAgent = nil; +#if SD_UIKIT + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]]; +#elif SD_WATCH + // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43 + userAgent = [NSString stringWithFormat:@"%@/%@ (%@; watchOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[WKInterfaceDevice currentDevice] model], [[WKInterfaceDevice currentDevice] systemVersion], [[WKInterfaceDevice currentDevice] screenScale]]; +#elif SD_MAC + userAgent = [NSString stringWithFormat:@"%@/%@ (Mac OS X %@)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[NSProcessInfo processInfo] operatingSystemVersionString]]; #endif + if (userAgent) { + if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) { + NSMutableString *mutableUserAgent = [userAgent mutableCopy]; + if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) { + userAgent = mutableUserAgent; + } + } + headerDictionary[@"User-Agent"] = userAgent; + } +#ifdef SD_WEBP + headerDictionary[@"Accept"] = @"image/webp,image/*;q=0.8"; +#else + headerDictionary[@"Accept"] = @"image/*;q=0.8"; +#endif + _HTTPHeaders = [headerDictionary copy]; _operationsLock = dispatch_semaphore_create(1); NSURLSessionConfiguration *sessionConfiguration = _config.sessionConfiguration; if (!sessionConfiguration) {