Authored by Bogdan Poplauschi
Committed by GitHub

Merge pull request #2080 from dreampiggy/feature_heif_image_format

Add image format detect of HEIC (One type of HEIF which use HEVC codec). This is supported natively in iOS 11 & macOS 10.13
@@ -16,7 +16,8 @@ typedef NS_ENUM(NSInteger, SDImageFormat) { @@ -16,7 +16,8 @@ typedef NS_ENUM(NSInteger, SDImageFormat) {
16 SDImageFormatPNG, 16 SDImageFormatPNG,
17 SDImageFormatGIF, 17 SDImageFormatGIF,
18 SDImageFormatTIFF, 18 SDImageFormatTIFF,
19 - SDImageFormatWebP 19 + SDImageFormatWebP,
  20 + SDImageFormatHEIC
20 }; 21 };
21 22
22 @interface NSData (ImageContentType) 23 @interface NSData (ImageContentType)
@@ -14,6 +14,11 @@ @@ -14,6 +14,11 @@
14 #import <MobileCoreServices/MobileCoreServices.h> 14 #import <MobileCoreServices/MobileCoreServices.h>
15 #endif 15 #endif
16 16
  17 +// Currently Image/IO does not support WebP
  18 +#define kSDUTTypeWebP ((__bridge CFStringRef)@"public.webp")
  19 +// AVFileTypeHEIC is defined in AVFoundation via iOS 11, we use this without import AVFoundation
  20 +#define kSDUTTypeHEIC ((__bridge CFStringRef)@"public.heic")
  21 +
17 @implementation NSData (ImageContentType) 22 @implementation NSData (ImageContentType)
18 23
19 + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data { 24 + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
@@ -21,6 +26,7 @@ @@ -21,6 +26,7 @@
21 return SDImageFormatUndefined; 26 return SDImageFormatUndefined;
22 } 27 }
23 28
  29 + // File signatures table: http://www.garykessler.net/library/file_sigs.html
24 uint8_t c; 30 uint8_t c;
25 [data getBytes:&c length:1]; 31 [data getBytes:&c length:1];
26 switch (c) { 32 switch (c) {
@@ -33,16 +39,26 @@ @@ -33,16 +39,26 @@
33 case 0x49: 39 case 0x49:
34 case 0x4D: 40 case 0x4D:
35 return SDImageFormatTIFF; 41 return SDImageFormatTIFF;
36 - case 0x52:  
37 - // R as RIFF for WEBP  
38 - if (data.length < 12) {  
39 - return SDImageFormatUndefined; 42 + case 0x52: {
  43 + if (data.length >= 12) {
  44 + //RIFF....WEBP
  45 + NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
  46 + if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
  47 + return SDImageFormatWebP;
  48 + }
40 } 49 }
41 -  
42 - NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];  
43 - if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {  
44 - return SDImageFormatWebP; 50 + break;
  51 + }
  52 + case 0x00: {
  53 + if (data.length >= 12) {
  54 + //....ftypheic
  55 + NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(4, 8)] encoding:NSASCIIStringEncoding];
  56 + if ([testString isEqualToString:@"ftypheic"]) {
  57 + return SDImageFormatHEIC;
  58 + }
45 } 59 }
  60 + break;
  61 + }
46 } 62 }
47 return SDImageFormatUndefined; 63 return SDImageFormatUndefined;
48 } 64 }
@@ -62,6 +78,12 @@ @@ -62,6 +78,12 @@
62 case SDImageFormatTIFF: 78 case SDImageFormatTIFF:
63 UTType = kUTTypeTIFF; 79 UTType = kUTTypeTIFF;
64 break; 80 break;
  81 + case SDImageFormatWebP:
  82 + UTType = kSDUTTypeWebP;
  83 + break;
  84 + case SDImageFormatHEIC:
  85 + UTType = kSDUTTypeHEIC;
  86 + break;
65 default: 87 default:
66 // default is kUTTypePNG 88 // default is kUTTypePNG
67 UTType = kUTTypePNG; 89 UTType = kUTTypePNG;
@@ -33,10 +33,12 @@ CG_EXTERN BOOL SDCGImageRefContainsAlpha(_Nullable CGImageRef imageRef); @@ -33,10 +33,12 @@ CG_EXTERN BOOL SDCGImageRefContainsAlpha(_Nullable CGImageRef imageRef);
33 33
34 /** 34 /**
35 This is the image coder protocol to provide custom image decoding/encoding. 35 This is the image coder protocol to provide custom image decoding/encoding.
  36 + These methods are all required to implement.
36 @note Pay attention that these methods are not called from main queue. 37 @note Pay attention that these methods are not called from main queue.
37 */ 38 */
38 @protocol SDWebImageCoder <NSObject> 39 @protocol SDWebImageCoder <NSObject>
39 40
  41 +@required
40 #pragma mark - Decoding 42 #pragma mark - Decoding
41 /** 43 /**
42 Returns YES if this coder can decode some data. Otherwise, the data should be passed to another coder. 44 Returns YES if this coder can decode some data. Otherwise, the data should be passed to another coder.
@@ -90,10 +92,12 @@ CG_EXTERN BOOL SDCGImageRefContainsAlpha(_Nullable CGImageRef imageRef); @@ -90,10 +92,12 @@ CG_EXTERN BOOL SDCGImageRefContainsAlpha(_Nullable CGImageRef imageRef);
90 92
91 /** 93 /**
92 This is the image coder protocol to provide custom progressive image decoding. 94 This is the image coder protocol to provide custom progressive image decoding.
  95 + These methods are all required to implement.
93 @note Pay attention that these methods are not called from main queue. 96 @note Pay attention that these methods are not called from main queue.
94 */ 97 */
95 @protocol SDWebImageProgressiveCoder <SDWebImageCoder> 98 @protocol SDWebImageProgressiveCoder <SDWebImageCoder>
96 99
  100 +@required
97 /** 101 /**
98 Returns YES if this coder can incremental decode some data. Otherwise, it should be passed to another coder. 102 Returns YES if this coder can incremental decode some data. Otherwise, it should be passed to another coder.
99 103
@@ -66,8 +66,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -66,8 +66,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
66 #pragma mark - Decode 66 #pragma mark - Decode
67 - (BOOL)canDecodeFromData:(nullable NSData *)data { 67 - (BOOL)canDecodeFromData:(nullable NSData *)data {
68 switch ([NSData sd_imageFormatForImageData:data]) { 68 switch ([NSData sd_imageFormatForImageData:data]) {
69 - // Do not support WebP decoding  
70 case SDImageFormatWebP: 69 case SDImageFormatWebP:
  70 + // Do not support WebP decoding
71 return NO; 71 return NO;
72 default: 72 default:
73 return YES; 73 return YES;
@@ -76,8 +76,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -76,8 +76,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
76 76
77 - (BOOL)canIncrementallyDecodeFromData:(NSData *)data { 77 - (BOOL)canIncrementallyDecodeFromData:(NSData *)data {
78 switch ([NSData sd_imageFormatForImageData:data]) { 78 switch ([NSData sd_imageFormatForImageData:data]) {
79 - // Support static GIF progressive decoding  
80 case SDImageFormatWebP: 79 case SDImageFormatWebP:
  80 + // Do not support WebP progressive decoding
81 return NO; 81 return NO;
82 default: 82 default:
83 return YES; 83 return YES;
@@ -394,9 +394,12 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -394,9 +394,12 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
394 #pragma mark - Encode 394 #pragma mark - Encode
395 - (BOOL)canEncodeToFormat:(SDImageFormat)format { 395 - (BOOL)canEncodeToFormat:(SDImageFormat)format {
396 switch (format) { 396 switch (format) {
397 - // Do not support WebP encoding  
398 case SDImageFormatWebP: 397 case SDImageFormatWebP:
  398 + // Do not support WebP encoding
399 return NO; 399 return NO;
  400 + case SDImageFormatHEIC:
  401 + // Check HEIC encoding compatibility
  402 + return [[self class] canEncodeToHEICFormat];
400 default: 403 default:
401 return YES; 404 return YES;
402 } 405 }
@@ -469,6 +472,28 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over @@ -469,6 +472,28 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
469 return YES; 472 return YES;
470 } 473 }
471 474
  475 ++ (BOOL)canEncodeToHEICFormat
  476 +{
  477 + static BOOL canEncode = NO;
  478 + static dispatch_once_t onceToken;
  479 + dispatch_once(&onceToken, ^{
  480 + NSMutableData *imageData = [NSMutableData data];
  481 + CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatHEIC];
  482 +
  483 + // Create an image destination.
  484 + CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
  485 + if (!imageDestination) {
  486 + // Can't encode to HEIC
  487 + canEncode = NO;
  488 + } else {
  489 + // Can encode to HEIC
  490 + CFRelease(imageDestination);
  491 + canEncode = YES;
  492 + }
  493 + });
  494 + return canEncode;
  495 +}
  496 +
472 #if SD_UIKIT || SD_WATCH 497 #if SD_UIKIT || SD_WATCH
473 #pragma mark EXIF orientation tag converter 498 #pragma mark EXIF orientation tag converter
474 + (UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData { 499 + (UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData {