diff --git a/SDWebImage/NSData+ImageContentType.h b/SDWebImage/NSData+ImageContentType.h index 0ca226d8..d7e21428 100644 --- a/SDWebImage/NSData+ImageContentType.h +++ b/SDWebImage/NSData+ImageContentType.h @@ -32,11 +32,19 @@ typedef NS_ENUM(NSInteger, SDImageFormat) { + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data; /** - Convert SDImageFormat to UTType - - @param format Format as SDImageFormat - @return The UTType as CFStringRef + * Convert SDImageFormat to UTType + * + * @param format Format as SDImageFormat + * @return The UTType as CFStringRef */ + (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format; +/** + * Convert UTTyppe to SDImageFormat + * + * @param uttype The UTType as CFStringRef + * @return The Format as SDImageFormat + */ ++ (SDImageFormat)sd_imageFormatFromUTType:(nonnull CFStringRef)uttype; + @end diff --git a/SDWebImage/NSData+ImageContentType.m b/SDWebImage/NSData+ImageContentType.m index 6a590611..64ea0a30 100644 --- a/SDWebImage/NSData+ImageContentType.m +++ b/SDWebImage/NSData+ImageContentType.m @@ -95,4 +95,27 @@ return UTType; } ++ (SDImageFormat)sd_imageFormatFromUTType:(CFStringRef)uttype { + if (!uttype) { + return SDImageFormatUndefined; + } + SDImageFormat imageFormat; + if (CFStringCompare(uttype, kUTTypeJPEG, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatJPEG; + } else if (CFStringCompare(uttype, kUTTypePNG, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatPNG; + } else if (CFStringCompare(uttype, kUTTypeGIF, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatGIF; + } else if (CFStringCompare(uttype, kUTTypeTIFF, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatTIFF; + } else if (CFStringCompare(uttype, kSDUTTypeWebP, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatWebP; + } else if (CFStringCompare(uttype, kSDUTTypeHEIC, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatHEIC; + } else { + imageFormat = SDImageFormatUndefined; + } + return imageFormat; +} + @end diff --git a/SDWebImage/SDWebImageCodersManager.m b/SDWebImage/SDWebImageCodersManager.m index 79c3bff5..883d1fdb 100644 --- a/SDWebImage/SDWebImageCodersManager.m +++ b/SDWebImage/SDWebImageCodersManager.m @@ -12,6 +12,7 @@ #ifdef SD_WEBP #import "SDWebImageWebPCoder.h" #endif +#import "UIImage+MultiFormat.h" #define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); #define UNLOCK(lock) dispatch_semaphore_signal(lock); @@ -121,7 +122,9 @@ UNLOCK(self.codersLock); for (id coder in coders.reverseObjectEnumerator) { if ([coder canDecodeFromData:*data]) { - return [coder decompressedImageWithImage:image data:data options:optionsDict]; + UIImage *decompressedImage = [coder decompressedImageWithImage:image data:data options:optionsDict]; + decompressedImage.sd_imageFormat = image.sd_imageFormat; + return decompressedImage; } } return nil; diff --git a/SDWebImage/SDWebImageCompat.m b/SDWebImage/SDWebImageCompat.m index bb53495e..daee7fc9 100644 --- a/SDWebImage/SDWebImageCompat.m +++ b/SDWebImage/SDWebImageCompat.m @@ -35,6 +35,7 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration]; if (animatedImage) { animatedImage.sd_imageLoopCount = image.sd_imageLoopCount; + animatedImage.sd_imageFormat = image.sd_imageFormat; } return animatedImage; } else { @@ -57,6 +58,7 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl } UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + scaledImage.sd_imageFormat = image.sd_imageFormat; image = scaledImage; } return image; diff --git a/SDWebImage/SDWebImageGIFCoder.m b/SDWebImage/SDWebImageGIFCoder.m index b9ebf1a5..6707329b 100644 --- a/SDWebImage/SDWebImageGIFCoder.m +++ b/SDWebImage/SDWebImageGIFCoder.m @@ -81,6 +81,7 @@ animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames]; animatedImage.sd_imageLoopCount = loopCount; + animatedImage.sd_imageFormat = SDImageFormatGIF; } CFRelease(source); diff --git a/SDWebImage/SDWebImageImageIOCoder.m b/SDWebImage/SDWebImageImageIOCoder.m index 0447ed3c..5f9d1118 100644 --- a/SDWebImage/SDWebImageImageIOCoder.m +++ b/SDWebImage/SDWebImageImageIOCoder.m @@ -11,6 +11,7 @@ #import "NSImage+WebCache.h" #import #import "NSData+ImageContentType.h" +#import "UIImage+MultiFormat.h" #if SD_UIKIT || SD_WATCH static const size_t kBytesPerPixel = 4; @@ -97,6 +98,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over } UIImage *image = [[UIImage alloc] initWithData:data]; + image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; return image; } @@ -146,6 +148,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize]; #endif CGImageRelease(partialImageRef); + image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; } } diff --git a/SDWebImage/SDWebImageWebPCoder.m b/SDWebImage/SDWebImageWebPCoder.m index a57a4bad..7971d231 100644 --- a/SDWebImage/SDWebImageWebPCoder.m +++ b/SDWebImage/SDWebImageWebPCoder.m @@ -106,6 +106,7 @@ } WebPDemuxDelete(demuxer); CGContextRelease(canvas); + staticImage.sd_imageFormat = SDImageFormatWebP; return staticImage; } @@ -145,6 +146,7 @@ UIImage *animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames]; animatedImage.sd_imageLoopCount = loopCount; + animatedImage.sd_imageFormat = SDImageFormatWebP; return animatedImage; } @@ -215,6 +217,7 @@ #else image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize]; #endif + image.sd_imageFormat = SDImageFormatWebP; CGImageRelease(newImageRef); CGContextRelease(canvas); } diff --git a/SDWebImage/UIImage+MultiFormat.h b/SDWebImage/UIImage+MultiFormat.h index c0792d1b..5c6f473c 100644 --- a/SDWebImage/UIImage+MultiFormat.h +++ b/SDWebImage/UIImage+MultiFormat.h @@ -15,7 +15,7 @@ * UIKit: * For static image format, this value is always 0. * For animated image format, 0 means infinite looping. - * 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. + * @note 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. * AppKit: * NSImage currently only support animated via GIF imageRep unlike UIImage. * The getter of this property will get the loop count from GIF imageRep @@ -23,6 +23,13 @@ */ @property (nonatomic, assign) NSUInteger sd_imageLoopCount; +/** + * The image format represent the original compressed image data format. + * If you don't manually specify a format, this information is retrieve from CGImage using `CGImageGetUTType`, which may return nil for non-CG based image. At this time it will return `SDImageFormatUndefined` as default value. + * @note 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. + */ +@property (nonatomic, assign) SDImageFormat sd_imageFormat; + + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data; - (nullable NSData *)sd_imageData; - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat; diff --git a/SDWebImage/UIImage+MultiFormat.m b/SDWebImage/UIImage+MultiFormat.m index 664e0969..d3198d2d 100644 --- a/SDWebImage/UIImage+MultiFormat.m +++ b/SDWebImage/UIImage+MultiFormat.m @@ -7,9 +7,9 @@ */ #import "UIImage+MultiFormat.h" - -#import "objc/runtime.h" +#import "NSImage+WebCache.h" #import "SDWebImageCodersManager.h" +#import "objc/runtime.h" @implementation UIImage (MultiFormat) @@ -53,6 +53,28 @@ } #endif +- (SDImageFormat)sd_imageFormat { + SDImageFormat imageFormat = SDImageFormatUndefined; + NSNumber *value = objc_getAssociatedObject(self, @selector(sd_imageFormat)); + if ([value isKindOfClass:[NSNumber class]]) { + imageFormat = value.integerValue; + return imageFormat; + } + // Check CGImage's UTType, may return nil for non-Image/IO based image +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (&CGImageGetUTType != NULL) { + CFStringRef uttype = CGImageGetUTType(self.CGImage); + imageFormat = [NSData sd_imageFormatFromUTType:uttype]; + } +#pragma clang diagnostic pop + return imageFormat; +} + +- (void)setSd_imageFormat:(SDImageFormat)sd_imageFormat { + objc_setAssociatedObject(self, @selector(sd_imageFormat), @(sd_imageFormat), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data { return [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data]; }