Add animated WebP loop count and fix duration zero issue
Animated WebP image support loop count. Set this value to UIImage decoded with WebP data. Frame duration set to 100ms if is zero
This commit is contained in:
parent
f3cffd0aca
commit
e3ed82fdda
|
@ -8,6 +8,8 @@
|
|||
|
||||
#import "SDWebImageCompat.h"
|
||||
|
||||
#import "objc/runtime.h"
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
|
||||
#endif
|
||||
|
@ -26,8 +28,18 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl
|
|||
for (UIImage *tempImage in image.images) {
|
||||
[scaledImages addObject:SDScaledImageForKey(key, tempImage)];
|
||||
}
|
||||
|
||||
return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
|
||||
UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration];
|
||||
#ifdef SD_WEBP
|
||||
if (animatedImage) {
|
||||
SEL sd_webpLoopCount = NSSelectorFromString(@"sd_webpLoopCount");
|
||||
NSNumber *value = objc_getAssociatedObject(image, sd_webpLoopCount);
|
||||
NSInteger loopCount = value.integerValue;
|
||||
if (loopCount) {
|
||||
objc_setAssociatedObject(animatedImage, sd_webpLoopCount, @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return animatedImage;
|
||||
}
|
||||
else {
|
||||
#if SD_WATCH
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
|
||||
@interface UIImage (WebP)
|
||||
|
||||
/**
|
||||
* Get the current WebP image loop count, the default value is 0.
|
||||
* For static WebP image, the value is 0.
|
||||
* For animated WebP image, 0 means repeat the animation indefinitely.
|
||||
* 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.
|
||||
* @return WebP image loop count
|
||||
*/
|
||||
- (NSInteger)sd_webpLoopCount;
|
||||
|
||||
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data;
|
||||
|
||||
@end
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#import "webp/demux.h"
|
||||
#import "NSImage+WebCache.h"
|
||||
|
||||
#import "objc/runtime.h"
|
||||
|
||||
// Callback for CGDataProviderRelease
|
||||
static void FreeImageData(void *info, const void *data, size_t size) {
|
||||
free((void *)data);
|
||||
|
@ -21,6 +23,12 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
|
||||
@implementation UIImage (WebP)
|
||||
|
||||
- (NSInteger)sd_webpLoopCount
|
||||
{
|
||||
NSNumber *value = objc_getAssociatedObject(self, @selector(sd_webpLoopCount));
|
||||
return value.integerValue;
|
||||
}
|
||||
|
||||
+ (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data {
|
||||
if (!data) {
|
||||
return nil;
|
||||
|
@ -51,6 +59,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
}
|
||||
|
||||
int frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
|
||||
int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
|
||||
int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
|
||||
int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
|
||||
CGBitmapInfo bitmapInfo;
|
||||
|
@ -62,7 +71,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
CGContextRef canvas = CGBitmapContextCreate(NULL, canvasWidth, canvasHeight, 8, 0, SDCGColorSpaceGetDeviceRGB(), bitmapInfo);
|
||||
|
||||
NSMutableArray<UIImage *> *images = [NSMutableArray array];
|
||||
NSTimeInterval duration = 0;
|
||||
NSTimeInterval totalDuration = 0;
|
||||
int durations[frameCount];
|
||||
|
||||
do {
|
||||
|
@ -78,10 +87,16 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
}
|
||||
|
||||
[images addObject:image];
|
||||
duration += iter.duration;
|
||||
int duration = iter.duration;
|
||||
if (!duration) {
|
||||
// WebP standard says duration for 0 is used for canvas updating but not showing image, but actually Chrome set this to the default 100ms duration.
|
||||
// Some animated WebP images also create without duration, we should keep compatibility
|
||||
duration = 100;
|
||||
}
|
||||
totalDuration += duration;
|
||||
size_t count = images.count;
|
||||
if (count) {
|
||||
durations[count - 1] = iter.duration;
|
||||
durations[count - 1] = duration;
|
||||
}
|
||||
|
||||
} while (WebPDemuxNextFrame(&iter));
|
||||
|
@ -92,8 +107,11 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
|
||||
UIImage *finalImage = nil;
|
||||
#if SD_UIKIT || SD_WATCH
|
||||
NSArray<UIImage *> *animatedImages = [self sd_animatedImagesWithImages:images durations:durations totalDuration:duration];
|
||||
finalImage = [UIImage animatedImageWithImages:animatedImages duration:duration / 1000.0];
|
||||
NSArray<UIImage *> *animatedImages = [self sd_animatedImagesWithImages:images durations:durations totalDuration:totalDuration];
|
||||
finalImage = [UIImage animatedImageWithImages:animatedImages duration:totalDuration / 1000.0];
|
||||
if (finalImage) {
|
||||
objc_setAssociatedObject(finalImage, @selector(sd_webpLoopCount), @(loopCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
#elif SD_MAC
|
||||
finalImage = images.firstObject;
|
||||
#endif
|
||||
|
@ -230,7 +248,12 @@ static void FreeImageData(void *info, const void *data, size_t size) {
|
|||
NSMutableArray<UIImage *> *animatedImages = [NSMutableArray arrayWithCapacity:count];
|
||||
[images enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
int duration = durations[idx];
|
||||
int repeatCount = duration / gcd;
|
||||
int repeatCount;
|
||||
if (gcd) {
|
||||
repeatCount = duration / gcd;
|
||||
} else {
|
||||
repeatCount = 1;
|
||||
}
|
||||
for (int i = 0; i < repeatCount; ++i) {
|
||||
[animatedImages addObject:image];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue