Add APNG coder to support static PNG & APNG rendering. Also update macOS's SDAnimatedImageRep
This commit is contained in:
parent
beb958bd08
commit
082c5af565
|
@ -63,6 +63,7 @@
|
|||
@"http://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.35786508303135633", // requires HTTP auth, used to demo the NTLM auth
|
||||
@"http://assets.sbnation.com/assets/2512203/dogflops.gif",
|
||||
@"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif",
|
||||
@"https://raw.githubusercontent.com/onevcat/APNGKit/master/TestImages/APNG-cube.apng",
|
||||
@"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp",
|
||||
@"http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp",
|
||||
@"http://littlesvr.ca/apng/images/SteamEngine.webp",
|
||||
|
|
|
@ -376,6 +376,18 @@
|
|||
325312D1200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; };
|
||||
325312D2200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; };
|
||||
325312D3200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; };
|
||||
327054D4206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054D5206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054D6206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054D7206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054D8206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054D9206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
327054DA206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DB206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DC206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DD206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DE206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
327054DF206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */; };
|
||||
3290FA041FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3290FA051FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3290FA061FA478AF0047D20C /* SDWebImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDWebImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -1449,6 +1461,8 @@
|
|||
324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDefine.m; sourceTree = "<group>"; };
|
||||
325312C6200F09910046BF1E /* SDWebImageTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTransition.h; sourceTree = "<group>"; };
|
||||
325312C7200F09910046BF1E /* SDWebImageTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTransition.m; sourceTree = "<group>"; };
|
||||
327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageAPNGCoder.h; sourceTree = "<group>"; };
|
||||
327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageAPNGCoder.m; sourceTree = "<group>"; };
|
||||
3290FA021FA478AF0047D20C /* SDWebImageFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageFrame.h; sourceTree = "<group>"; };
|
||||
3290FA031FA478AF0047D20C /* SDWebImageFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageFrame.m; sourceTree = "<group>"; };
|
||||
329A18571FFF5DFD008C9A2F /* UIImage+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+WebCache.h"; path = "SDWebImage/UIImage+WebCache.h"; sourceTree = "<group>"; };
|
||||
|
@ -1703,6 +1717,8 @@
|
|||
321E60A11F38E8F600405457 /* SDWebImageGIFCoder.m */,
|
||||
321E60AE1F38E90100405457 /* SDWebImageWebPCoder.h */,
|
||||
321E60AF1F38E90100405457 /* SDWebImageWebPCoder.m */,
|
||||
327054D2206CD8B3006EA328 /* SDWebImageAPNGCoder.h */,
|
||||
327054D3206CD8B3006EA328 /* SDWebImageAPNGCoder.m */,
|
||||
3290FA021FA478AF0047D20C /* SDWebImageFrame.h */,
|
||||
3290FA031FA478AF0047D20C /* SDWebImageFrame.m */,
|
||||
32CF1C051FA496B000004BD1 /* SDWebImageCoderHelper.h */,
|
||||
|
@ -2161,6 +2177,7 @@
|
|||
80377DCC1F2F66A700F89830 /* lossless_common.h in Headers */,
|
||||
321E60971F38E8ED00405457 /* SDWebImageImageIOCoder.h in Headers */,
|
||||
43A918671D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
|
||||
327054D7206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
431739571CDFC8B70008FEB9 /* encode.h in Headers */,
|
||||
00733A6F1BC4880E00A5A117 /* UIImage+WebP.h in Headers */,
|
||||
323F8B711F38EF770092B609 /* delta_palettization_enc.h in Headers */,
|
||||
|
@ -2241,6 +2258,7 @@
|
|||
80377C1D1F2F666300F89830 /* huffman_encode_utils.h in Headers */,
|
||||
321E60B11F38E90100405457 /* SDWebImageWebPCoder.h in Headers */,
|
||||
80377E9A1F2F66D400F89830 /* common_dec.h in Headers */,
|
||||
327054D5206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
80377C231F2F666300F89830 /* quant_levels_utils.h in Headers */,
|
||||
321E60BF1F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
|
||||
80377EA61F2F66D400F89830 /* webpi_dec.h in Headers */,
|
||||
|
@ -2396,6 +2414,7 @@
|
|||
323F8BDC1F38EF770092B609 /* vp8i_enc.h in Headers */,
|
||||
80377ED21F2F66D500F89830 /* vp8i_dec.h in Headers */,
|
||||
32484779201775F600AF9E5A /* SDAnimatedImage.h in Headers */,
|
||||
327054D8206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
43A918681D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -2419,6 +2438,7 @@
|
|||
80377C8D1F2F666400F89830 /* random_utils.h in Headers */,
|
||||
4397D2C31D0DDD8C00BB2784 /* SDWebImageManager.h in Headers */,
|
||||
323F8B551F38EF770092B609 /* backward_references_enc.h in Headers */,
|
||||
327054D9206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
80377C811F2F666400F89830 /* endian_inl_utils.h in Headers */,
|
||||
321E60991F38E8ED00405457 /* SDWebImageImageIOCoder.h in Headers */,
|
||||
323F8B8B1F38EF770092B609 /* histogram_enc.h in Headers */,
|
||||
|
@ -2508,6 +2528,7 @@
|
|||
80377D871F2F66A700F89830 /* lossless_common.h in Headers */,
|
||||
321E60961F38E8ED00405457 /* SDWebImageImageIOCoder.h in Headers */,
|
||||
4A2CAE041AB4BB5400B6BC39 /* SDWebImage.h in Headers */,
|
||||
327054D6206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
431739511CDFC8B70008FEB9 /* format_constants.h in Headers */,
|
||||
43A918661D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
|
||||
323F8B701F38EF770092B609 /* delta_palettization_enc.h in Headers */,
|
||||
|
@ -2628,6 +2649,7 @@
|
|||
80377C0B1F2F665300F89830 /* random_utils.h in Headers */,
|
||||
80377E921F2F66D000F89830 /* vp8i_dec.h in Headers */,
|
||||
5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */,
|
||||
327054D4206CD8B3006EA328 /* SDWebImageAPNGCoder.h in Headers */,
|
||||
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */,
|
||||
530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */,
|
||||
32484769201775F600AF9E5A /* SDAnimatedImageView.h in Headers */,
|
||||
|
@ -2951,6 +2973,7 @@
|
|||
323F8B991F38EF770092B609 /* near_lossless_enc.c in Sources */,
|
||||
80377DE81F2F66A700F89830 /* yuv_mips_dsp_r2.c in Sources */,
|
||||
80377EC31F2F66D500F89830 /* vp8l_dec.c in Sources */,
|
||||
327054DD206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
325312D1200F09910046BF1E /* SDWebImageTransition.m in Sources */,
|
||||
321E609D1F38E8ED00405457 /* SDWebImageImageIOCoder.m in Sources */,
|
||||
323F8B9F1F38EF770092B609 /* picture_csp_enc.c in Sources */,
|
||||
|
@ -3151,6 +3174,7 @@
|
|||
80377C221F2F666300F89830 /* quant_levels_utils.c in Sources */,
|
||||
80377D2E1F2F66A700F89830 /* dec_mips32.c in Sources */,
|
||||
323F8BD31F38EF770092B609 /* tree_enc.c in Sources */,
|
||||
327054DB206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
80377D5C1F2F66A700F89830 /* upsampling_sse2.c in Sources */,
|
||||
323F8BC71F38EF770092B609 /* syntax_enc.c in Sources */,
|
||||
80377D321F2F66A700F89830 /* dec_sse41.c in Sources */,
|
||||
|
@ -3304,6 +3328,7 @@
|
|||
80377E301F2F66A800F89830 /* yuv.c in Sources */,
|
||||
43A9186F1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
|
||||
323F8BD61F38EF770092B609 /* tree_enc.c in Sources */,
|
||||
327054DE206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
80377DFD1F2F66A800F89830 /* dec_mips32.c in Sources */,
|
||||
323F8BCA1F38EF770092B609 /* syntax_enc.c in Sources */,
|
||||
80377E2B1F2F66A800F89830 /* upsampling_sse2.c in Sources */,
|
||||
|
@ -3451,6 +3476,7 @@
|
|||
4397D2A11D0DDD8C00BB2784 /* SDWebImageManager.m in Sources */,
|
||||
323F8BCB1F38EF770092B609 /* syntax_enc.c in Sources */,
|
||||
321E60AD1F38E8F600405457 /* SDWebImageGIFCoder.m in Sources */,
|
||||
327054DF206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
80377E341F2F66A800F89830 /* alpha_processing_sse2.c in Sources */,
|
||||
4397D2A61D0DDD8C00BB2784 /* SDWebImageCompat.m in Sources */,
|
||||
80377E6F1F2F66A800F89830 /* upsampling_neon.c in Sources */,
|
||||
|
@ -3570,6 +3596,7 @@
|
|||
323F8B981F38EF770092B609 /* near_lossless_enc.c in Sources */,
|
||||
80377D6F1F2F66A700F89830 /* cost.c in Sources */,
|
||||
80377EB31F2F66D400F89830 /* vp8l_dec.c in Sources */,
|
||||
327054DC206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */,
|
||||
321E609C1F38E8ED00405457 /* SDWebImageImageIOCoder.m in Sources */,
|
||||
323F8B9E1F38EF770092B609 /* picture_csp_enc.c in Sources */,
|
||||
|
@ -3727,6 +3754,7 @@
|
|||
80377CE51F2F66A100F89830 /* cost.c in Sources */,
|
||||
80377E931F2F66D000F89830 /* vp8l_dec.c in Sources */,
|
||||
321E609A1F38E8ED00405457 /* SDWebImageImageIOCoder.m in Sources */,
|
||||
327054DA206CD8B3006EA328 /* SDWebImageAPNGCoder.m in Sources */,
|
||||
325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */,
|
||||
323F8B9C1F38EF770092B609 /* picture_csp_enc.c in Sources */,
|
||||
80377D141F2F66A100F89830 /* upsampling_mips_dsp_r2.c in Sources */,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#if SD_MAC
|
||||
|
||||
#import "SDWebImageGIFCoder.h"
|
||||
#import "SDWebImageAPNGCoder.h"
|
||||
|
||||
@interface SDWebImageGIFCoder ()
|
||||
|
||||
|
@ -18,6 +19,13 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface SDWebImageAPNGCoder ()
|
||||
|
||||
- (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source;
|
||||
- (NSUInteger)sd_imageLoopCountWithSource:(CGImageSourceRef)source;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDAnimatedImageRep ()
|
||||
|
||||
@property (nonatomic, assign, readonly, nullable) CGImageSourceRef imageSource;
|
||||
|
@ -26,6 +34,43 @@
|
|||
|
||||
@implementation SDAnimatedImageRep
|
||||
|
||||
// `NSBitmapImageRep`'s `imageRepWithData:` is not designed initlizer
|
||||
+ (instancetype)imageRepWithData:(NSData *)data {
|
||||
SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
|
||||
return imageRep;
|
||||
}
|
||||
|
||||
// We should override init method for `NSBitmapImageRep` to do initlize about animated image format
|
||||
- (instancetype)initWithData:(NSData *)data {
|
||||
self = [super initWithData:data];
|
||||
if (self) {
|
||||
CGImageSourceRef imageSource = self.imageSource;
|
||||
if (!imageSource) {
|
||||
return self;
|
||||
}
|
||||
NSUInteger frameCount = CGImageSourceGetCount(imageSource);
|
||||
if (frameCount <= 1) {
|
||||
return self;
|
||||
}
|
||||
CFStringRef type = CGImageSourceGetType(imageSource);
|
||||
if (!type) {
|
||||
return self;
|
||||
}
|
||||
if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
|
||||
// GIF
|
||||
// Do nothing because NSBitmapImageRep support it
|
||||
} else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
|
||||
// APNG
|
||||
// Do initilize about frame count, current frame/duration and loop count
|
||||
[self setProperty:NSImageFrameCount withValue:@(frameCount)];
|
||||
[self setProperty:NSImageCurrentFrame withValue:@(0)];
|
||||
NSUInteger loopCount = [[SDWebImageAPNGCoder sharedCoder] sd_imageLoopCountWithSource:imageSource];
|
||||
[self setProperty:NSImageLoopCount withValue:@(loopCount)];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// `NSBitmapImageRep` will use `kCGImagePropertyGIFDelayTime` whenever you call `setProperty:withValue:` with `NSImageCurrentFrame` to change the current frame. We override it and use the actual `kCGImagePropertyGIFUnclampedDelayTime` if need.
|
||||
- (void)setProperty:(NSBitmapImageRepPropertyKey)property withValue:(id)value {
|
||||
[super setProperty:property withValue:value];
|
||||
|
@ -45,6 +90,8 @@
|
|||
// Through we currently process GIF only, in the 5.x we support APNG so we keep the extensibility
|
||||
if (CFStringCompare(type, kUTTypeGIF, 0) == kCFCompareEqualTo) {
|
||||
frameDuration = [[SDWebImageGIFCoder sharedCoder] sd_frameDurationAtIndex:index source:imageSource];
|
||||
} else if (CFStringCompare(type, kUTTypePNG, 0) == kCFCompareEqualTo) {
|
||||
frameDuration = [[SDWebImageAPNGCoder sharedCoder] sd_frameDurationAtIndex:index source:imageSource];
|
||||
}
|
||||
if (!frameDuration) {
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 "SDWebImageCoder.h"
|
||||
|
||||
/**
|
||||
Built in coder using ImageIO that supports APNG encoding/decoding
|
||||
*/
|
||||
@interface SDWebImageAPNGCoder : NSObject <SDWebImageProgressiveCoder, SDWebImageAnimatedCoder>
|
||||
|
||||
@property (nonatomic, class, readonly, nonnull) SDWebImageAPNGCoder *sharedCoder;
|
||||
|
||||
@end
|
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
* 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 "SDWebImageAPNGCoder.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import "NSData+ImageContentType.h"
|
||||
#import "UIImage+WebCache.h"
|
||||
#import "NSImage+Additions.h"
|
||||
#import "SDWebImageCoderHelper.h"
|
||||
#import "SDAnimatedImageRep.h"
|
||||
|
||||
// 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
|
||||
|
||||
@interface SDAPNGCoderFrame : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSUInteger index; // Frame index (zero based)
|
||||
@property (nonatomic, assign) NSTimeInterval duration; // Frame duration in seconds
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDAPNGCoderFrame
|
||||
@end
|
||||
|
||||
@implementation SDWebImageAPNGCoder {
|
||||
size_t _width, _height;
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
UIImageOrientation _orientation;
|
||||
#endif
|
||||
CGImageSourceRef _imageSource;
|
||||
NSData *_imageData;
|
||||
NSUInteger _loopCount;
|
||||
NSUInteger _frameCount;
|
||||
NSArray<SDAPNGCoderFrame *> *_frames;
|
||||
BOOL _finished;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_imageSource) {
|
||||
CFRelease(_imageSource);
|
||||
_imageSource = NULL;
|
||||
}
|
||||
#if SD_UIKIT
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning:(NSNotification *)notification
|
||||
{
|
||||
if (_imageSource) {
|
||||
for (size_t i = 0; i < _frameCount; i++) {
|
||||
CGImageSourceRemoveCacheAtIndex(_imageSource, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (instancetype)sharedCoder {
|
||||
static SDWebImageAPNGCoder *coder;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
coder = [[SDWebImageAPNGCoder alloc] init];
|
||||
});
|
||||
return coder;
|
||||
}
|
||||
|
||||
#pragma mark - Decode
|
||||
- (BOOL)canDecodeFromData:(nullable NSData *)data {
|
||||
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatPNG);
|
||||
}
|
||||
|
||||
- (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDWebImageCoderOptions *)options {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if SD_MAC
|
||||
SDAnimatedImageRep *imageRep = [[SDAnimatedImageRep alloc] initWithData:data];
|
||||
NSImage *animatedImage = [[NSImage alloc] initWithSize:imageRep.size];
|
||||
[animatedImage addRepresentation:imageRep];
|
||||
return animatedImage;
|
||||
#else
|
||||
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
|
||||
if (!source) {
|
||||
return nil;
|
||||
}
|
||||
size_t count = CGImageSourceGetCount(source);
|
||||
|
||||
UIImage *animatedImage;
|
||||
|
||||
if (count <= 1) {
|
||||
animatedImage = [[UIImage alloc] initWithData:data];
|
||||
} else {
|
||||
NSMutableArray<SDWebImageFrame *> *frames = [NSMutableArray array];
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL);
|
||||
if (!imageRef) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float duration = [self sd_frameDurationAtIndex:i source:source];
|
||||
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
|
||||
CGImageRelease(imageRef);
|
||||
|
||||
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:image duration:duration];
|
||||
[frames addObject:frame];
|
||||
}
|
||||
|
||||
NSUInteger loopCount = [self sd_imageLoopCountWithSource:source];
|
||||
|
||||
animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames];
|
||||
animatedImage.sd_imageLoopCount = loopCount;
|
||||
}
|
||||
|
||||
CFRelease(source);
|
||||
|
||||
return animatedImage;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSUInteger)sd_imageLoopCountWithSource:(CGImageSourceRef)source {
|
||||
NSUInteger loopCount = 0;
|
||||
NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil);
|
||||
NSDictionary *pngProperties = [imageProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyPNGDictionary];
|
||||
if (pngProperties) {
|
||||
NSNumber *apngLoopCount = [pngProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyAPNGLoopCount];
|
||||
if (apngLoopCount != nil) {
|
||||
loopCount = apngLoopCount.unsignedIntegerValue;
|
||||
}
|
||||
}
|
||||
return loopCount;
|
||||
}
|
||||
|
||||
- (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
|
||||
float frameDuration = 0.1f;
|
||||
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
|
||||
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
|
||||
NSDictionary *pngProperties = frameProperties[(NSString *)kCGImagePropertyPNGDictionary];
|
||||
|
||||
NSNumber *delayTimeUnclampedProp = pngProperties[(__bridge_transfer NSString *)kCGImagePropertyAPNGUnclampedDelayTime];
|
||||
if (delayTimeUnclampedProp != nil) {
|
||||
frameDuration = [delayTimeUnclampedProp floatValue];
|
||||
} else {
|
||||
NSNumber *delayTimeProp = pngProperties[(__bridge_transfer NSString *)kCGImagePropertyAPNGDelayTime];
|
||||
if (delayTimeProp != nil) {
|
||||
frameDuration = [delayTimeProp floatValue];
|
||||
}
|
||||
}
|
||||
|
||||
if (frameDuration < 0.011f) {
|
||||
frameDuration = 0.100f;
|
||||
}
|
||||
|
||||
CFRelease(cfFrameProperties);
|
||||
return frameDuration;
|
||||
}
|
||||
|
||||
#pragma mark - Encode
|
||||
- (BOOL)canEncodeToFormat:(SDImageFormat)format {
|
||||
return (format == SDImageFormatPNG);
|
||||
}
|
||||
|
||||
- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format options:(nullable SDWebImageCoderOptions *)options {
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (format != SDImageFormatPNG) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *imageData = [NSMutableData data];
|
||||
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:SDImageFormatPNG];
|
||||
NSArray<SDWebImageFrame *> *frames = [SDWebImageCoderHelper framesFromAnimatedImage:image];
|
||||
|
||||
// Create an image destination. APNG does not support EXIF image orientation
|
||||
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frames.count, NULL);
|
||||
if (!imageDestination) {
|
||||
// Handle failure.
|
||||
return nil;
|
||||
}
|
||||
if (frames.count == 0) {
|
||||
// for static single PNG images
|
||||
CGImageDestinationAddImage(imageDestination, image.CGImage, nil);
|
||||
} else {
|
||||
// for animated APNG images
|
||||
NSUInteger loopCount = image.sd_imageLoopCount;
|
||||
NSDictionary *pngProperties = @{(__bridge_transfer NSString *)kCGImagePropertyPNGDictionary: @{(__bridge_transfer NSString *)kCGImagePropertyAPNGLoopCount : @(loopCount)}};
|
||||
CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)pngProperties);
|
||||
|
||||
for (size_t i = 0; i < frames.count; i++) {
|
||||
SDWebImageFrame *frame = frames[i];
|
||||
float frameDuration = frame.duration;
|
||||
CGImageRef frameImageRef = frame.image.CGImage;
|
||||
NSDictionary *frameProperties = @{(__bridge_transfer NSString *)kCGImagePropertyPNGDictionary : @{(__bridge_transfer NSString *)kCGImagePropertyAPNGDelayTime : @(frameDuration)}};
|
||||
CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
|
||||
}
|
||||
}
|
||||
// Finalize the destination.
|
||||
if (CGImageDestinationFinalize(imageDestination) == NO) {
|
||||
// Handle failure.
|
||||
imageData = nil;
|
||||
}
|
||||
|
||||
CFRelease(imageDestination);
|
||||
|
||||
return [imageData copy];
|
||||
}
|
||||
|
||||
#pragma mark - Progressive Decode
|
||||
|
||||
- (BOOL)canIncrementalDecodeFromData:(NSData *)data {
|
||||
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatPNG);
|
||||
}
|
||||
|
||||
- (instancetype)initIncremental {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_imageSource = CGImageSourceCreateIncremental((__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceShouldCache : @(YES)});
|
||||
#if SD_UIKIT
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateIncrementalData:(NSData *)data finished:(BOOL)finished {
|
||||
if (_finished) {
|
||||
return;
|
||||
}
|
||||
_imageData = data;
|
||||
_finished = finished;
|
||||
|
||||
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
|
||||
// Thanks to the author @Nyx0uf
|
||||
|
||||
// Update the data source, we must pass ALL the data, not just the new bytes
|
||||
CGImageSourceUpdateData(_imageSource, (__bridge CFDataRef)data, finished);
|
||||
|
||||
if (_width + _height == 0) {
|
||||
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(_imageSource, 0, NULL);
|
||||
if (properties) {
|
||||
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
|
||||
if (val) CFNumberGetValue(val, kCFNumberLongType, &_height);
|
||||
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
|
||||
if (val) CFNumberGetValue(val, kCFNumberLongType, &_width);
|
||||
CFRelease(properties);
|
||||
}
|
||||
}
|
||||
|
||||
// For animated image progressive decoding because the frame count and duration may be changed.
|
||||
[self scanAndCheckFramesValidWithImageSource:_imageSource];
|
||||
}
|
||||
|
||||
- (UIImage *)incrementalDecodedImageWithOptions:(SDWebImageCoderOptions *)options {
|
||||
UIImage *image;
|
||||
|
||||
if (_width + _height > 0) {
|
||||
// Create the image
|
||||
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(_imageSource, 0, NULL);
|
||||
|
||||
if (partialImageRef) {
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
image = [[UIImage alloc] initWithCGImage:partialImageRef];
|
||||
#elif SD_MAC
|
||||
image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize];
|
||||
#endif
|
||||
CGImageRelease(partialImageRef);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#pragma mark - SDWebImageAnimatedCoder
|
||||
- (nullable instancetype)initWithAnimatedImageData:(nullable NSData *)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
}
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// use Image/IO cache because it's already keep a balance between CPU & memory
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, (__bridge CFDictionaryRef)@{(__bridge_transfer NSString *)kCGImageSourceShouldCache : @(YES)});
|
||||
if (!imageSource) {
|
||||
return nil;
|
||||
}
|
||||
BOOL framesValid = [self scanAndCheckFramesValidWithImageSource:imageSource];
|
||||
if (!framesValid) {
|
||||
CFRelease(imageSource);
|
||||
return nil;
|
||||
}
|
||||
_imageSource = imageSource;
|
||||
_imageData = data;
|
||||
#if SD_UIKIT
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)scanAndCheckFramesValidWithImageSource:(CGImageSourceRef)imageSource
|
||||
{
|
||||
if (!imageSource) {
|
||||
return NO;
|
||||
}
|
||||
NSUInteger frameCount = CGImageSourceGetCount(imageSource);
|
||||
NSUInteger loopCount = [self sd_imageLoopCountWithSource:imageSource];
|
||||
NSMutableArray<SDAPNGCoderFrame *> *frames = [NSMutableArray array];
|
||||
|
||||
for (size_t i = 0; i < frameCount; i++) {
|
||||
SDAPNGCoderFrame *frame = [[SDAPNGCoderFrame alloc] init];
|
||||
frame.index = i;
|
||||
frame.duration = [self sd_frameDurationAtIndex:i source:imageSource];
|
||||
[frames addObject:frame];
|
||||
}
|
||||
|
||||
_frameCount = frameCount;
|
||||
_loopCount = loopCount;
|
||||
_frames = [frames copy];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSData *)animatedImageData
|
||||
{
|
||||
return _imageData;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageLoopCount
|
||||
{
|
||||
return _loopCount;
|
||||
}
|
||||
|
||||
- (NSUInteger)animatedImageFrameCount
|
||||
{
|
||||
return _frameCount;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index
|
||||
{
|
||||
if (index >= _frameCount) {
|
||||
return 0;
|
||||
}
|
||||
return _frames[index].duration;
|
||||
}
|
||||
|
||||
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index
|
||||
{
|
||||
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_imageSource, index, NULL);
|
||||
if (!imageRef) {
|
||||
return nil;
|
||||
}
|
||||
// Image/IO create CGImage does not decompressed, so we do this because this is called background queue, this can avoid main queue block when rendering(especially when one more imageViews use the same image instance)
|
||||
CGImageRef newImageRef = [SDWebImageCoderHelper imageRefCreateDecoded:imageRef];
|
||||
if (!newImageRef) {
|
||||
newImageRef = imageRef;
|
||||
} else {
|
||||
CGImageRelease(imageRef);
|
||||
}
|
||||
#if SD_MAC
|
||||
UIImage *image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize];
|
||||
#else
|
||||
UIImage *image = [UIImage imageWithCGImage:newImageRef];
|
||||
#endif
|
||||
CGImageRelease(newImageRef);
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
|
@ -9,6 +9,7 @@
|
|||
#import "SDWebImageCodersManager.h"
|
||||
#import "SDWebImageImageIOCoder.h"
|
||||
#import "SDWebImageGIFCoder.h"
|
||||
#import "SDWebImageAPNGCoder.h"
|
||||
#ifdef SD_WEBP
|
||||
#import "SDWebImageWebPCoder.h"
|
||||
#endif
|
||||
|
@ -36,7 +37,7 @@
|
|||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// initialize with default coders
|
||||
_mutableCoders = [@[[SDWebImageImageIOCoder sharedCoder], [SDWebImageGIFCoder sharedCoder]] mutableCopy];
|
||||
_mutableCoders = [@[[SDWebImageImageIOCoder sharedCoder], [SDWebImageGIFCoder sharedCoder], [SDWebImageAPNGCoder sharedCoder]] mutableCopy];
|
||||
#ifdef SD_WEBP
|
||||
[_mutableCoders addObject:[SDWebImageWebPCoder sharedCoder]];
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
|
|||
#import <SDWebImage/SDAnimatedImageView+WebCache.h>
|
||||
#import <SDWebImage/SDWebImageCodersManager.h>
|
||||
#import <SDWebImage/SDWebImageCoder.h>
|
||||
#import <SDWebImage/SDWebImageAPNGCoder.h>
|
||||
#import <SDWebImage/SDWebImageWebPCoder.h>
|
||||
#import <SDWebImage/SDWebImageGIFCoder.h>
|
||||
#import <SDWebImage/SDWebImageImageIOCoder.h>
|
||||
|
|
Loading…
Reference in New Issue