Give back our GIF category animation support since we now use plugin coders. This will not affect FLAnimatedImageView which just create the first frame.
1. Change default coders to [IOCoder, WebPCoder] 2. Add back our previous GIF decoding code, with loop count support 3. Add GIF encoding code for animation 4. Modify IOCoder for GIF format because FLAnimatedImageView need just use the first frame
This commit is contained in:
parent
d12484a762
commit
17386829c1
|
@ -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`, `WebPCoder`
|
||||
- calling `coders` will return `@[WebPCoder, GIFCoder, IOCoder]`
|
||||
- by default we internally set coders = `IOCoder`, `WebPCoder`
|
||||
- calling `coders` will return `@[WebPCoder, IOCoder]`
|
||||
- call `[addCoder:[MyCrazyCoder new]]`
|
||||
- calling `coders` now returns `@[MyCrazyCoder, WebPCoder, GIFCoder, IOCoder]`
|
||||
- calling `coders` now returns `@[MyCrazyCoder, WebPCoder, IOCoder]`
|
||||
|
||||
Coders
|
||||
------
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// initialize with default coders
|
||||
_mutableCoders = [@[[SDWebImageImageIOCoder sharedCoder], [SDWebImageGIFCoder sharedCoder]] mutableCopy];
|
||||
_mutableCoders = [@[[SDWebImageImageIOCoder sharedCoder]] mutableCopy];
|
||||
#ifdef SD_WEBP
|
||||
[_mutableCoders addObject:[SDWebImageWebPCoder sharedCoder]];
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "NSImage+WebCache.h"
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#import "NSData+ImageContentType.h"
|
||||
#import "UIImage+MultiFormat.h"
|
||||
|
||||
@implementation SDWebImageGIFCoder
|
||||
|
||||
|
@ -40,13 +41,23 @@
|
|||
|
||||
size_t count = CGImageSourceGetCount(source);
|
||||
|
||||
UIImage *staticImage;
|
||||
UIImage *animatedImage;
|
||||
|
||||
if (count <= 1) {
|
||||
staticImage = [[UIImage alloc] initWithData:data];
|
||||
} else {
|
||||
// we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
|
||||
// this here is only code to allow drawing animated images as static ones
|
||||
animatedImage = [[UIImage alloc] initWithData:data];
|
||||
}
|
||||
else {
|
||||
NSMutableArray *images = [NSMutableArray array];
|
||||
|
||||
NSTimeInterval duration = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
|
||||
if (!image) {
|
||||
continue;
|
||||
}
|
||||
|
||||
duration += [self sd_frameDurationAtIndex:i source:source];
|
||||
#if SD_WATCH
|
||||
CGFloat scale = 1;
|
||||
scale = [WKInterfaceDevice currentDevice].screenScale;
|
||||
|
@ -54,21 +65,66 @@
|
|||
CGFloat scale = 1;
|
||||
scale = [UIScreen mainScreen].scale;
|
||||
#endif
|
||||
[images addObject:[UIImage imageWithCGImage:image scale:scale orientation:UIImageOrientationUp]];
|
||||
|
||||
CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
|
||||
staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
|
||||
#endif
|
||||
CGImageRelease(CGImage);
|
||||
CGImageRelease(image);
|
||||
}
|
||||
|
||||
if (!duration) {
|
||||
duration = (1.0f / 10.0f) * count;
|
||||
}
|
||||
|
||||
animatedImage = [UIImage animatedImageWithImages:images duration:duration];
|
||||
|
||||
NSUInteger loopCount = 0;
|
||||
NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil);
|
||||
NSDictionary *gifProperties = [imageProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary];
|
||||
if (gifProperties) {
|
||||
NSNumber *gifLoopCount = [gifProperties valueForKey:(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount];
|
||||
if (gifLoopCount) {
|
||||
loopCount = gifLoopCount.unsignedIntegerValue;
|
||||
}
|
||||
}
|
||||
animatedImage.sd_imageLoopCount = loopCount;
|
||||
}
|
||||
|
||||
CFRelease(source);
|
||||
|
||||
return staticImage;
|
||||
return animatedImage;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
|
||||
float frameDuration = 0.1f;
|
||||
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
|
||||
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
|
||||
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];
|
||||
|
||||
NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
|
||||
if (delayTimeUnclampedProp) {
|
||||
frameDuration = [delayTimeUnclampedProp floatValue];
|
||||
}
|
||||
else {
|
||||
|
||||
NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
|
||||
if (delayTimeProp) {
|
||||
frameDuration = [delayTimeProp floatValue];
|
||||
}
|
||||
}
|
||||
|
||||
// Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
|
||||
// We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
|
||||
// a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
|
||||
// for more information.
|
||||
|
||||
if (frameDuration < 0.011f) {
|
||||
frameDuration = 0.100f;
|
||||
}
|
||||
|
||||
CFRelease(cfFrameProperties);
|
||||
return frameDuration;
|
||||
}
|
||||
|
||||
- (UIImage *)decompressedImageWithImage:(UIImage *)image
|
||||
data:(NSData *__autoreleasing _Nullable *)data
|
||||
options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict {
|
||||
|
@ -92,16 +148,36 @@
|
|||
|
||||
NSMutableData *imageData = [NSMutableData data];
|
||||
CFStringRef imageUTType = [NSData sd_UTTypeFromSDImageFormat:format];
|
||||
NSUInteger frameCount = 1;
|
||||
if (image.images) {
|
||||
frameCount = image.images.count;
|
||||
}
|
||||
|
||||
// Create an image destination.
|
||||
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL);
|
||||
// Create an image destination. GIF does not support EXIF image orientation
|
||||
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frameCount, NULL);
|
||||
if (!imageDestination) {
|
||||
// Handle failure.
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Add your image to the destination.
|
||||
if (!image.images) {
|
||||
// for static single GIF images
|
||||
CGImageDestinationAddImage(imageDestination, image.CGImage, nil);
|
||||
} else {
|
||||
// for animated GIF images
|
||||
NSUInteger loopCount = image.sd_imageLoopCount;
|
||||
NSTimeInterval totalDuration = image.duration;
|
||||
NSTimeInterval frameDuration = totalDuration / frameCount;
|
||||
NSDictionary *gifProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary: @{(__bridge_transfer NSString *)kCGImagePropertyGIFLoopCount : @(loopCount)}};
|
||||
CGImageDestinationSetProperties(imageDestination, (__bridge CFDictionaryRef)gifProperties);
|
||||
for (size_t i = 0; i < frameCount; i++) {
|
||||
@autoreleasepool {
|
||||
NSDictionary *frameProperties = @{(__bridge_transfer NSString *)kCGImagePropertyGIFDictionary : @{(__bridge_transfer NSString *)kCGImagePropertyGIFUnclampedDelayTime : @(frameDuration)}};
|
||||
CGImageRef frameImageRef = image.images[i].CGImage;
|
||||
CGImageDestinationAddImage(imageDestination, frameImageRef, (__bridge CFDictionaryRef)frameProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the destination.
|
||||
if (CGImageDestinationFinalize(imageDestination) == NO) {
|
||||
|
|
|
@ -67,7 +67,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
- (BOOL)canDecodeFromData:(nullable NSData *)data {
|
||||
switch ([NSData sd_imageFormatForImageData:data]) {
|
||||
// Do not support GIF and WebP decoding
|
||||
case SDImageFormatGIF:
|
||||
case SDImageFormatWebP:
|
||||
return NO;
|
||||
default:
|
||||
|
@ -91,6 +90,17 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
}
|
||||
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
SDImageFormat format = [NSData sd_imageFormatForImageData:data];
|
||||
if (format == SDImageFormatGIF) {
|
||||
// static single GIF need to be created animated for FLAnimatedImageView logic
|
||||
// GIF does not support EXIF image orientation
|
||||
image = [UIImage animatedImageWithImages:@[image] duration:image.duration];
|
||||
return image;
|
||||
}
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data];
|
||||
if (orientation != UIImageOrientationUp) {
|
||||
|
@ -373,7 +383,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
- (BOOL)canEncodeToFormat:(SDImageFormat)format {
|
||||
switch (format) {
|
||||
// Do not support GIF and WebP encoding
|
||||
case SDImageFormatGIF:
|
||||
case SDImageFormatWebP:
|
||||
return NO;
|
||||
default:
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
@interface UIImage (GIF)
|
||||
|
||||
/**
|
||||
* Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image
|
||||
* Creates an animated UIImage from an NSData.
|
||||
* For static GIF, will create an UIImage with `images` array set to nil. For animated GIF, will create an UIImage with valid `images` array.
|
||||
*/
|
||||
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;
|
||||
|
||||
/**
|
||||
* Checks if an UIImage instance is a GIF. Will use the `images` array
|
||||
* Checks if an UIImage instance is a GIF. Will use the `images` array.
|
||||
*/
|
||||
- (BOOL)isGIF;
|
||||
|
||||
|
|
Loading…
Reference in New Issue