Merge pull request #2849 from dreampiggy/feature_heics_animated_coder

Supports HEIC sequence (animated) image on iOS 13/macOS 10.15+
This commit is contained in:
DreamPiggy 2019-09-25 14:57:37 +08:00 committed by GitHub
commit dfc118d2f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 317 additions and 92 deletions

View File

@ -1,6 +1,6 @@
language: objective-c
osx_image: xcode10.3
osx_image: xcode11
env:
global:
@ -63,9 +63,9 @@ script:
- echo Build the Demo apps
- pod install
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage OSX Demo' -destination 'platform=macOS,arch=x86_64' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage iOS Demo' -destination 'platform=iOS Simulator,name=iPhone XS' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage iOS Demo' -destination 'platform=iOS Simulator,name=iPhone 11 Pro' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage TV Demo' -destination 'platform=tvOS Simulator,name=Apple TV 4K' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage Watch Demo' -destination 'platform=iOS Simulator,name=iPhone XS' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage Watch Demo' -destination 'platform=iOS Simulator,name=iPhone 11 Pro' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
# - xcodebuild build -workspace SDWebImage.xcworkspace -scheme 'SDWebImage iOS Demo' -destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst' -configuration Debug CODE_SIGNING_ALLOWED=NO | xcpretty -c
- echo Clean DerivedData
@ -73,7 +73,7 @@ script:
- mkdir DerivedData
- echo Run the tests
- xcodebuild clean test -workspace SDWebImage.xcworkspace -scheme 'Tests' -destination 'platform=iOS Simulator,name=iPhone XS' -configuration Debug -UseModernBuildSystem=NO CODE_SIGNING_ALLOWED=NO | xcpretty -c
- xcodebuild clean test -workspace SDWebImage.xcworkspace -scheme 'Tests' -destination 'platform=iOS Simulator,name=iPhone 11 Pro' -configuration Debug -UseModernBuildSystem=NO CODE_SIGNING_ALLOWED=NO | xcpretty -c
- mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/iOS
- xcodebuild clean test -workspace SDWebImage.xcworkspace -scheme 'Tests Mac' -destination 'platform=macOS,arch=x86_64' -configuration Debug -UseModernBuildSystem=NO CODE_SIGNING_ALLOWED=NO | xcpretty -c
- mv ~/Library/Developer/Xcode/DerivedData/ ./DerivedData/macOS

View File

@ -72,6 +72,7 @@
@"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://nokiatech.github.io/heif/content/image_sequences/starfield_animation.heic",
@"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png",
@"http://via.placeholder.com/200x200.jpg",
nil];

View File

@ -149,6 +149,11 @@
32935D2D22A4FEDE0049C068 /* UIImageView+WebCache.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; };
32935D2E22A4FEDE0049C068 /* UIView+WebCache.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; };
32935D2F22A4FEE50049C068 /* SDWebImage.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4A2CAE031AB4BB5400B6BC39 /* SDWebImage.h */; };
3298655C2337230C0071958B /* SDImageHEICCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 3298655A2337230C0071958B /* SDImageHEICCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
3298655D2337230C0071958B /* SDImageHEICCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3298655B2337230C0071958B /* SDImageHEICCoder.m */; };
3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3298655B2337230C0071958B /* SDImageHEICCoder.m */; };
3298655F233723220071958B /* SDImageHEICCoder.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3298655A2337230C0071958B /* SDImageHEICCoder.h */; };
32986562233737C70071958B /* SDImageHEICCoderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 32986560233737C70071958B /* SDImageHEICCoderInternal.h */; settings = {ATTRIBUTES = (Private, ); }; };
329A185B1FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; };
329A185F1FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */; };
329A18611FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */; };
@ -275,6 +280,7 @@
dstPath = include/SDWebImage;
dstSubfolderSpec = 16;
files = (
3298655F233723220071958B /* SDImageHEICCoder.h in Copy Headers */,
32C78E3823336FC800C6B7F8 /* SDImageIOAnimatedCoder.h in Copy Headers */,
32E5690822B1FFCA00CBABC6 /* SDWebImageOptionsProcessor.h in Copy Headers */,
32935D2F22A4FEE50049C068 /* SDWebImage.h in Copy Headers */,
@ -388,6 +394,9 @@
328BB6C02082581100760D6C /* SDMemoryCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDMemoryCache.m; path = Core/SDMemoryCache.m; sourceTree = "<group>"; };
3290FA021FA478AF0047D20C /* SDImageFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDImageFrame.h; path = Core/SDImageFrame.h; sourceTree = "<group>"; };
3290FA031FA478AF0047D20C /* SDImageFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDImageFrame.m; path = Core/SDImageFrame.m; sourceTree = "<group>"; };
3298655A2337230C0071958B /* SDImageHEICCoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDImageHEICCoder.h; path = Core/SDImageHEICCoder.h; sourceTree = "<group>"; };
3298655B2337230C0071958B /* SDImageHEICCoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDImageHEICCoder.m; path = Core/SDImageHEICCoder.m; sourceTree = "<group>"; };
32986560233737C70071958B /* SDImageHEICCoderInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDImageHEICCoderInternal.h; sourceTree = "<group>"; };
329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+Metadata.h"; path = "Core/UIImage+Metadata.h"; sourceTree = "<group>"; };
329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Metadata.m"; path = "Core/UIImage+Metadata.m"; sourceTree = "<group>"; };
329F1235223FAA3B00B309FD /* SDmetamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDmetamacros.h; sourceTree = "<group>"; };
@ -512,6 +521,8 @@
321E60A11F38E8F600405457 /* SDImageGIFCoder.m */,
327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */,
327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */,
3298655A2337230C0071958B /* SDImageHEICCoder.h */,
3298655B2337230C0071958B /* SDImageHEICCoder.m */,
3290FA021FA478AF0047D20C /* SDImageFrame.h */,
3290FA031FA478AF0047D20C /* SDImageFrame.m */,
32CF1C051FA496B000004BD1 /* SDImageCoderHelper.h */,
@ -582,6 +593,7 @@
325C460C223394D8004CAE11 /* SDImageCachesManagerOperation.h */,
325C460D223394D8004CAE11 /* SDImageCachesManagerOperation.m */,
32C78E39233371AD00C6B7F8 /* SDImageIOAnimatedCoderInternal.h */,
32986560233737C70071958B /* SDImageHEICCoderInternal.h */,
325C461E2233A02E004CAE11 /* UIColor+HexString.h */,
325C461F2233A02E004CAE11 /* UIColor+HexString.m */,
325C46242233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h */,
@ -789,7 +801,9 @@
buildActionMask = 2147483647;
files = (
32B5CC60222F89C2005EB74E /* SDAsyncBlockOperation.h in Headers */,
32986562233737C70071958B /* SDImageHEICCoderInternal.h in Headers */,
32D122202080B2EB003685A3 /* SDImageCacheDefine.h in Headers */,
3298655C2337230C0071958B /* SDImageHEICCoder.h in Headers */,
32B9B539206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */,
3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */,
32D3CDD121DDE87300C4DB49 /* UIImage+MemoryCacheCost.h in Headers */,
@ -1056,6 +1070,7 @@
321B37952083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
4A2CAE361AB4BB7500B6BC39 /* UIImageView+WebCache.m in Sources */,
4A2CAE1E1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.m in Sources */,
3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */,
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */,
327054DC206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */,
325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */,
@ -1120,6 +1135,7 @@
5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */,
321B37932083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */,
3298655D2337230C0071958B /* SDImageHEICCoder.m in Sources */,
321E609A1F38E8ED00405457 /* SDImageIOCoder.m in Sources */,
327054DA206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */,
325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */,

View File

@ -13,12 +13,10 @@
#else
#import <MobileCoreServices/MobileCoreServices.h>
#endif
#import "SDImageHEICCoderInternal.h"
// Currently Image/IO does not support WebP
#define kSDUTTypeWebP ((__bridge CFStringRef)@"public.webp")
// 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)

View File

@ -13,6 +13,8 @@
#import "SDImageIOAnimatedCoderInternal.h"
#import "SDImageGIFCoder.h"
#import "SDImageAPNGCoder.h"
#import "SDImageHEICCoder.h"
#import "SDImageHEICCoderInternal.h"
@implementation SDAnimatedImageRep {
CGImageSourceRef _imageSource;
@ -61,6 +63,13 @@
[self setProperty:NSImageCurrentFrame withValue:@(0)];
NSUInteger loopCount = [SDImageAPNGCoder imageLoopCountWithSource:imageSource];
[self setProperty:NSImageLoopCount withValue:@(loopCount)];
} else if (CFStringCompare(type, kSDUTTypeHEICS, 0) == kCFCompareEqualTo) {
// HEIC
// Do initilize about frame count, current frame/duration and loop count
[self setProperty:NSImageFrameCount withValue:@(frameCount)];
[self setProperty:NSImageCurrentFrame withValue:@(0)];
NSUInteger loopCount = [SDImageHEICCoder imageLoopCountWithSource:imageSource];
[self setProperty:NSImageLoopCount withValue:@(loopCount)];
}
}
return self;
@ -88,6 +97,9 @@
} else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
// APNG
frameDuration = [SDImageAPNGCoder frameDurationAtIndex:index source:imageSource];
} else if (CFStringCompare(type, kSDUTTypeHEICS, 0) == kCFCompareEqualTo) {
// HEIC
frameDuration = [SDImageHEICCoder frameDurationAtIndex:index source:imageSource];
}
if (!frameDuration) {
return;

View File

@ -14,14 +14,20 @@
#endif
// iOS 8 Image/IO framework binary does not contains these APNG contants, so we define them. Thanks Apple :)
#if (__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
const CFStringRef kCGImagePropertyAPNGLoopCount = (__bridge CFStringRef)@"LoopCount";
const CFStringRef kCGImagePropertyAPNGDelayTime = (__bridge CFStringRef)@"DelayTime";
const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef)@"UnclampedDelayTime";
#endif
static NSString * kSDCGImagePropertyAPNGLoopCount = @"LoopCount";
static NSString * kSDCGImagePropertyAPNGDelayTime = @"DelayTime";
static NSString * kSDCGImagePropertyAPNGUnclampedDelayTime = @"UnclampedDelayTime";
@implementation SDImageAPNGCoder
+ (void)initialize {
if (@available(iOS 9, *)) {
kSDCGImagePropertyAPNGLoopCount = (__bridge NSString *)kCGImagePropertyAPNGLoopCount;
kSDCGImagePropertyAPNGDelayTime = (__bridge NSString *)kCGImagePropertyAPNGDelayTime;
kSDCGImagePropertyAPNGUnclampedDelayTime = (__bridge NSString *)kCGImagePropertyAPNGUnclampedDelayTime;
}
}
+ (instancetype)sharedCoder {
static SDImageAPNGCoder *coder;
static dispatch_once_t onceToken;
@ -46,15 +52,15 @@ const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef
}
+ (NSString *)unclampedDelayTimeProperty {
return (__bridge NSString *)kCGImagePropertyAPNGUnclampedDelayTime;
return kSDCGImagePropertyAPNGUnclampedDelayTime;
}
+ (NSString *)delayTimeProperty {
return (__bridge NSString *)kCGImagePropertyAPNGDelayTime;
return kSDCGImagePropertyAPNGDelayTime;
}
+ (NSString *)loopCountProperty {
return (__bridge NSString *)kCGImagePropertyAPNGLoopCount;
return kSDCGImagePropertyAPNGLoopCount;
}
+ (NSUInteger)defaultLoopCount {

View File

@ -17,10 +17,10 @@
Note: the `coders` getter will return the coders in their reversed order
Example:
- by default we internally set coders = `IOCoder`, `GIFCoder`, `APNGCoder`
- calling `coders` will return `@[IOCoder, GIFCoder, APNGCoder]`
- by default we internally set coders = `IOCoder`, `GIFCoder`, `APNGCoder`, 'HEICCoder' (iOS 11+)
- calling `coders` will return `@[IOCoder, GIFCoder, APNGCoder, HEICCoder]`
- call `[addCoder:[MyCrazyCoder new]]`
- calling `coders` now returns `@[IOCoder, GIFCoder, APNGCoder, MyCrazyCoder]`
- calling `coders` now returns `@[IOCoder, GIFCoder, APNGCoder, HEICCoder, MyCrazyCoder]`
Coders
------

View File

@ -10,6 +10,7 @@
#import "SDImageIOCoder.h"
#import "SDImageGIFCoder.h"
#import "SDImageAPNGCoder.h"
#import "SDImageHEICCoder.h"
#import "SDInternalMacros.h"
@interface SDImageCodersManager ()
@ -36,6 +37,9 @@
if (self = [super init]) {
// initialize with default coders
_imageCoders = [NSMutableArray arrayWithArray:@[[SDImageIOCoder sharedCoder], [SDImageGIFCoder sharedCoder], [SDImageAPNGCoder sharedCoder]]];
if (@available(iOS 11, macOS 10.14, tvOS 11, watchOS 4, *)) {
[_imageCoders addObject:[SDImageHEICCoder sharedCoder]];
}
_codersLock = dispatch_semaphore_create(1);
}
return self;

View File

@ -0,0 +1,23 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <Foundation/Foundation.h>
#import "SDImageIOAnimatedCoder.h"
/**
This coder is used for HEIC (HEIF with HEVC container codec) image format.
Image/IO provide the static HEIC (.heic) support in iOS 11/macOS 10.13/tvOS 11/watchOS 4+.
Image/IO provide the animated HEIC (.heics) support in iOS 13/macOS 10.15/tvOS 13/watchOS 6+.
See https://nokiatech.github.io/heif/technical.html for the standard.
@note If you need to support lower firmware version for HEIF, you can have a try at https://github.com/SDWebImage/SDWebImageHEIFCoder
*/
@interface SDImageHEICCoder : SDImageIOAnimatedCoder <SDProgressiveImageCoder, SDAnimatedImageCoder>
@property (nonatomic, class, readonly, nonnull) SDImageHEICCoder *sharedCoder;
@end

View File

@ -0,0 +1,167 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDImageHEICCoder.h"
#import "SDImageHEICCoderInternal.h"
// These constantce are available from iOS 13+ and Xcode 11. This raw value is used for toolchain and firmware compatiblitiy
static NSString * kSDCGImagePropertyHEICSDictionary = @"{HEICS}";
static NSString * kSDCGImagePropertyHEICSLoopCount = @"LoopCount";
static NSString * kSDCGImagePropertyHEICSDelayTime = @"DelayTime";
static NSString * kSDCGImagePropertyHEICSUnclampedDelayTime = @"UnclampedDelayTime";
@implementation SDImageHEICCoder
+ (void)initialize {
#if __IPHONE_13_0 || __TVOS_13_0 || __MAC_10_15 || __WATCHOS_6_0
// Xcode 11
if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) {
// Use SDK instead of raw value
kSDCGImagePropertyHEICSDictionary = (__bridge NSString *)kCGImagePropertyHEICSDictionary;
kSDCGImagePropertyHEICSLoopCount = (__bridge NSString *)kCGImagePropertyHEICSLoopCount;
kSDCGImagePropertyHEICSDelayTime = (__bridge NSString *)kCGImagePropertyHEICSDelayTime;
kSDCGImagePropertyHEICSUnclampedDelayTime = (__bridge NSString *)kCGImagePropertyHEICSUnclampedDelayTime;
}
#endif
}
+ (instancetype)sharedCoder {
static SDImageHEICCoder *coder;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coder = [[SDImageHEICCoder alloc] init];
});
return coder;
}
#pragma mark - SDImageCoder
- (BOOL)canDecodeFromData:(nullable NSData *)data {
switch ([NSData sd_imageFormatForImageData:data]) {
case SDImageFormatHEIC:
// Check HEIC decoding compatibility
return [self.class canDecodeFromHEICFormat];
case SDImageFormatHEIF:
// Check HEIF decoding compatibility
return [self.class canDecodeFromHEIFFormat];
default:
return NO;
}
}
- (BOOL)canIncrementalDecodeFromData:(NSData *)data {
return [self canDecodeFromData:data];
}
- (BOOL)canEncodeToFormat:(SDImageFormat)format {
switch (format) {
case SDImageFormatHEIC:
// Check HEIC encoding compatibility
return [self.class canEncodeToHEICFormat];
case SDImageFormatHEIF:
// Check HEIF encoding compatibility
return [self.class canEncodeToHEIFFormat];
default:
return NO;
}
}
#pragma mark - HEIF Format
+ (BOOL)canDecodeFromFormat:(SDImageFormat)format {
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers();
if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) {
return YES;
}
return NO;
}
+ (BOOL)canDecodeFromHEICFormat {
static BOOL canDecode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canDecode = [self canDecodeFromFormat:SDImageFormatHEIC];
});
return canDecode;
}
+ (BOOL)canDecodeFromHEIFFormat {
static BOOL canDecode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canDecode = [self canDecodeFromFormat:SDImageFormatHEIF];
});
return canDecode;
}
+ (BOOL)canEncodeToFormat:(SDImageFormat)format {
NSMutableData *imageData = [NSMutableData data];
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
// Create an image destination.
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
if (!imageDestination) {
// Can't encode to HEIC
return NO;
} else {
// Can encode to HEIC
CFRelease(imageDestination);
return YES;
}
}
+ (BOOL)canEncodeToHEICFormat {
static BOOL canEncode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canEncode = [self canEncodeToFormat:SDImageFormatHEIC];
});
return canEncode;
}
+ (BOOL)canEncodeToHEIFFormat {
static BOOL canEncode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canEncode = [self canEncodeToFormat:SDImageFormatHEIF];
});
return canEncode;
}
#pragma mark - Subclass Override
+ (SDImageFormat)imageFormat {
return SDImageFormatHEIC;
}
+ (NSString *)imageUTType {
return (__bridge NSString *)kSDUTTypeHEIC;
}
+ (NSString *)dictionaryProperty {
return kSDCGImagePropertyHEICSDictionary;
}
+ (NSString *)unclampedDelayTimeProperty {
return kSDCGImagePropertyHEICSUnclampedDelayTime;
}
+ (NSString *)delayTimeProperty {
return kSDCGImagePropertyHEICSDelayTime;
}
+ (NSString *)loopCountProperty {
return kSDCGImagePropertyHEICSLoopCount;
}
+ (NSUInteger)defaultLoopCount {
return 0;
}
@end

View File

@ -25,6 +25,7 @@
@property (class, readonly) SDImageFormat imageFormat;
/**
The supported image format UTI Type. Such as `kUTTypeGIF`.
This can be used for cases when we can not detect `SDImageFormat. Such as progressive decoding's hint format `kCGImageSourceTypeIdentifierHint`.
@note Subclass override.
*/
@property (class, readonly, nonnull) NSString *imageUTType;

View File

@ -301,12 +301,12 @@
}
NSMutableData *imageData = [NSMutableData data];
NSString *imageUTType = self.class.imageUTType;
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image];
// Create an image destination. Animated Image does not support EXIF image orientation TODO
// The `CGImageDestinationCreateWithData` will log a warning when count is 0, use 1 instead.
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, (__bridge CFStringRef)imageUTType, frames.count ?: 1, NULL);
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frames.count ?: 1, NULL);
if (!imageDestination) {
// Handle failure.
return nil;

View File

@ -11,6 +11,7 @@
#import "NSImage+Compatibility.h"
#import <ImageIO/ImageIO.h>
#import "UIImage+Metadata.h"
#import "SDImageHEICCoderInternal.h"
@implementation SDImageIOCoder {
size_t _width, _height;
@ -54,10 +55,10 @@
return NO;
case SDImageFormatHEIC:
// Check HEIC decoding compatibility
return [[self class] canDecodeFromHEICFormat];
return [SDImageHEICCoder canDecodeFromHEICFormat];
case SDImageFormatHEIF:
// Check HEIF decoding compatibility
return [[self class] canDecodeFromHEIFFormat];
return [SDImageHEICCoder canDecodeFromHEIFFormat];
default:
return YES;
}
@ -170,10 +171,10 @@
return NO;
case SDImageFormatHEIC:
// Check HEIC encoding compatibility
return [[self class] canEncodeToHEICFormat];
return [SDImageHEICCoder canEncodeToHEICFormat];
case SDImageFormatHEIF:
// Check HEIF encoding compatibility
return [[self class] canEncodeToHEIFFormat];
return [SDImageHEICCoder canEncodeToHEIFFormat];
default:
return YES;
}
@ -230,65 +231,4 @@
return [imageData copy];
}
+ (BOOL)canDecodeFromFormat:(SDImageFormat)format {
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers();
if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) {
return YES;
}
return NO;
}
+ (BOOL)canDecodeFromHEICFormat {
static BOOL canDecode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canDecode = [self canDecodeFromFormat:SDImageFormatHEIC];
});
return canDecode;
}
+ (BOOL)canDecodeFromHEIFFormat {
static BOOL canDecode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canDecode = [self canDecodeFromFormat:SDImageFormatHEIF];
});
return canDecode;
}
+ (BOOL)canEncodeToFormat:(SDImageFormat)format {
NSMutableData *imageData = [NSMutableData data];
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
// Create an image destination.
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
if (!imageDestination) {
// Can't encode to HEIC
return NO;
} else {
// Can encode to HEIC
CFRelease(imageDestination);
return YES;
}
}
+ (BOOL)canEncodeToHEICFormat {
static BOOL canEncode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canEncode = [self canEncodeToFormat:SDImageFormatHEIC];
});
return canEncode;
}
+ (BOOL)canEncodeToHEIFFormat {
static BOOL canEncode = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
canEncode = [self canEncodeToFormat:SDImageFormatHEIF];
});
return canEncode;
}
@end

View File

@ -0,0 +1,25 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import <Foundation/Foundation.h>
#import "SDImageHEICCoder.h"
// 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")
// HEIC Sequence (Animated Image)
#define kSDUTTypeHEICS ((__bridge CFStringRef)@"public.heics")
@interface SDImageHEICCoder ()
+ (BOOL)canDecodeFromHEICFormat;
+ (BOOL)canDecodeFromHEIFFormat;
+ (BOOL)canEncodeToHEICFormat;
+ (BOOL)canEncodeToHEIFFormat;
@end

View File

@ -32,6 +32,8 @@
328BB6DE20825E9800760D6C /* SDWebImageTestCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */; };
32905E64211D786E00460FCF /* TestImage.heif in Resources */ = {isa = PBXBuildFile; fileRef = 32905E63211D786E00460FCF /* TestImage.heif */; };
32905E65211D786E00460FCF /* TestImage.heif in Resources */ = {isa = PBXBuildFile; fileRef = 32905E63211D786E00460FCF /* TestImage.heif */; };
3297A09F23374D1700814590 /* TestImageAnimated.heic in Resources */ = {isa = PBXBuildFile; fileRef = 3297A09E23374D1600814590 /* TestImageAnimated.heic */; };
3297A0A023374D1700814590 /* TestImageAnimated.heic in Resources */ = {isa = PBXBuildFile; fileRef = 3297A09E23374D1600814590 /* TestImageAnimated.heic */; };
32A571562037DB2D002EDAAE /* SDAnimatedImageTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */; };
32B99E8B203AF8690017FD66 /* SDCategoriesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */; };
32B99E9B203B2EDD0017FD66 /* SDTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D7AF05F1F329763000083C2 /* SDTestCase.m */; };
@ -93,6 +95,7 @@
328BB6DB20825E9800760D6C /* SDWebImageTestCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestCache.h; sourceTree = "<group>"; };
328BB6DC20825E9800760D6C /* SDWebImageTestCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestCache.m; sourceTree = "<group>"; };
32905E63211D786E00460FCF /* TestImage.heif */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImage.heif; sourceTree = "<group>"; };
3297A09E23374D1600814590 /* TestImageAnimated.heic */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageAnimated.heic; sourceTree = "<group>"; };
32A571552037DB2D002EDAAE /* SDAnimatedImageTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageTest.m; sourceTree = "<group>"; };
32B99E8A203AF8690017FD66 /* SDCategoriesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDCategoriesTests.m; sourceTree = "<group>"; };
32B99E92203B2DF90017FD66 /* Tests Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -182,6 +185,7 @@
327A418B211D660600495442 /* TestImage.heic */,
32905E63211D786E00460FCF /* TestImage.heif */,
327054E1206CEFF3006EA328 /* TestImageAnimated.apng */,
3297A09E23374D1600814590 /* TestImageAnimated.heic */,
);
path = Images;
sourceTree = "<group>";
@ -360,6 +364,7 @@
324047452271956F007C53E1 /* TestEXIF.png in Resources */,
32B99EA4203B31360017FD66 /* TestImage.jpg in Resources */,
32B99EA6203B31360017FD66 /* TestImage.png in Resources */,
3297A0A023374D1700814590 /* TestImageAnimated.heic in Resources */,
32B99EA2203B31360017FD66 /* MonochromeTestImage.jpg in Resources */,
32905E65211D786E00460FCF /* TestImage.heif in Resources */,
327A418D211D660600495442 /* TestImage.heic in Resources */,
@ -379,6 +384,7 @@
433BBBB71D7EF8200086B6E9 /* TestImage.gif in Resources */,
DA248D61195472AA00390AB0 /* InfoPlist.strings in Resources */,
433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */,
3297A09F23374D1700814590 /* TestImageAnimated.heic in Resources */,
327054E2206CEFF3006EA328 /* TestImageAnimated.apng in Resources */,
326E69472334C0C300B7252C /* TestLoopCount.gif in Resources */,
433BBBBB1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg in Resources */,

Binary file not shown.

View File

@ -153,11 +153,7 @@ static const NSUInteger kTestGIFFrameCount = 5; // local TestImage.gif loop coun
#if SD_MAC
expect(imageView.wantsUpdateLayer).beTruthy();
#else
#if TARGET_OS_MACCATALYST
// macOS's UIImageView seems does not setup layer.contents until render on screen
#else
expect(imageView.layer.contents).notTo.beNil();
#endif
expect(imageView.layer).notTo.beNil();
#endif
}

View File

@ -140,9 +140,35 @@
expect([manager encodedDataWithImage:nil format:SDImageFormatUndefined options:nil]).beNil();
}
- (void)test16ThatHEICAnimatedWorks {
if (@available(iOS 11, macOS 10.13, *)) {
NSURL *heicURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"TestImageAnimated" withExtension:@"heic"];
#if SD_UIKIT
BOOL isAnimatedImage = YES;
BOOL supportsEncoding = YES; // iPhone Simulator after Xcode 9.3 support HEIC encoding
#else
BOOL isAnimatedImage = NO; // Travis-CI Mac env does not upgrade to macOS 10.15
BOOL supportsEncoding = NO; // Travis-CI Mac env currently does not support HEIC encoding
#endif
[self verifyCoder:[SDImageHEICCoder sharedCoder]
withLocalImageURL:heicURL
supportsEncoding:supportsEncoding
encodingFormat:SDImageFormatHEIC
isAnimatedImage:isAnimatedImage];
}
}
- (void)verifyCoder:(id<SDImageCoder>)coder
withLocalImageURL:(NSURL *)imageUrl
supportsEncoding:(BOOL)supportsEncoding
isAnimatedImage:(BOOL)isAnimated {
[self verifyCoder:coder withLocalImageURL:imageUrl supportsEncoding:supportsEncoding encodingFormat:SDImageFormatUndefined isAnimatedImage:isAnimated];
}
- (void)verifyCoder:(id<SDImageCoder>)coder
withLocalImageURL:(NSURL *)imageUrl
supportsEncoding:(BOOL)supportsEncoding
encodingFormat:(SDImageFormat)encodingFormat
isAnimatedImage:(BOOL)isAnimated {
NSData *inputImageData = [NSData dataWithContentsOfURL:imageUrl];
expect(inputImageData).toNot.beNil();
@ -173,10 +199,13 @@
if (supportsEncoding) {
// 3 - check if we can encode to the original format
expect([coder canEncodeToFormat:inputImageFormat]).to.beTruthy();
if (encodingFormat == SDImageFormatUndefined) {
encodingFormat = inputImageFormat;
}
expect([coder canEncodeToFormat:encodingFormat]).to.beTruthy();
// 4 - encode from UIImage to NSData using the inputImageFormat and check it
NSData *outputImageData = [coder encodedDataWithImage:inputImage format:inputImageFormat options:nil];
NSData *outputImageData = [coder encodedDataWithImage:inputImage format:encodingFormat options:nil];
expect(outputImageData).toNot.beNil();
UIImage *outputImage = [coder decodedImageWithData:outputImageData options:nil];
expect(outputImage.size).to.equal(inputImage.size);

View File

@ -68,6 +68,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
#import <SDWebImage/SDWebImageError.h>
#import <SDWebImage/SDWebImageOptionsProcessor.h>
#import <SDWebImage/SDImageIOAnimatedCoder.h>
#import <SDWebImage/SDImageHEICCoder.h>
// Mac
#if __has_include(<SDWebImage/NSImage+Compatibility.h>)