From a6e515208bf77052b466a2461ff8136350de5554 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 5 Nov 2019 23:11:53 +0800 Subject: [PATCH 1/4] Add animated image playback rate config. Allows user to control the animation speed --- SDWebImage/Core/SDAnimatedImagePlayer.h | 8 ++++++++ SDWebImage/Core/SDAnimatedImagePlayer.m | 9 +++++++++ SDWebImage/Core/SDAnimatedImageView.h | 9 +++++++++ SDWebImage/Core/SDAnimatedImageView.m | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.h b/SDWebImage/Core/SDAnimatedImagePlayer.h index f329eefb..cec6f9f7 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.h +++ b/SDWebImage/Core/SDAnimatedImagePlayer.h @@ -29,6 +29,14 @@ /// Total loop count for animated image rendering. Default is animated image's loop count. @property (nonatomic, assign) NSUInteger totalLoopCount; +/// The animation playback rate. Default is 1.0 +/// `1.0` means the normal speed. +/// `0.0` means stopping the animation. +/// `0.0-1.0` means the slow speed. +/// `> 1.0` means the fast speed. +/// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future) +@property (nonatomic, assign) double playRate; + /// Provide a max buffer size by bytes. This is used to adjust frame buffer count and can be useful when the decoding cost is expensive (such as Animated WebP software decoding). Default is 0. /// `0` means automatically adjust by calculating current memory usage. /// `1` means without any buffer cache, each of frames will be decoded and then be freed after rendering. (Lowest Memory and Highest CPU) diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.m b/SDWebImage/Core/SDAnimatedImagePlayer.m index a1b10ebc..e358f136 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.m +++ b/SDWebImage/Core/SDAnimatedImagePlayer.m @@ -44,6 +44,7 @@ // Get the current frame and loop count. self.totalLoopCount = provider.animatedImageLoopCount; self.animatedProvider = provider; + self.playRate = 1.0; #if SD_UIKIT [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; #endif @@ -232,12 +233,20 @@ // Then check if timestamp is reached self.currentTime += duration; NSTimeInterval currentDuration = [self.animatedProvider animatedImageDurationAtIndex:currentFrameIndex]; + NSTimeInterval playRate = self.playRate; + if (playRate <= 0) { + // Does not support <= 0 play rate + [self stopPlaying]; + return; + } + currentDuration = currentDuration / playRate; if (self.currentTime < currentDuration) { // Current frame timestamp not reached, return return; } self.currentTime -= currentDuration; NSTimeInterval nextDuration = [self.animatedProvider animatedImageDurationAtIndex:nextFrameIndex]; + nextDuration = nextDuration / playRate; if (self.currentTime > nextDuration) { // Do not skip frame self.currentTime = nextDuration; diff --git a/SDWebImage/Core/SDAnimatedImageView.h b/SDWebImage/Core/SDAnimatedImageView.h index 3b7f310f..fc875235 100644 --- a/SDWebImage/Core/SDAnimatedImageView.h +++ b/SDWebImage/Core/SDAnimatedImageView.h @@ -43,6 +43,15 @@ This class override UIImageView's `animationRepeatCount` property on iOS, use this property as well. */ @property (nonatomic, assign) NSInteger animationRepeatCount; +/** + The animation playback rate. Default is 1.0. + `1.0` means the normal speed. + `0.0` means stopping the animation. + `0.0-1.0` means the slow speed. + `> 1.0` means the fast speed. + `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future) + */ +@property (nonatomic, assign) double playRate; /** Provide a max buffer size by bytes. This is used to adjust frame buffer count and can be useful when the decoding cost is expensive (such as Animated WebP software decoding). Default is 0. `0` means automatically adjust by calculating current memory usage. diff --git a/SDWebImage/Core/SDAnimatedImageView.m b/SDWebImage/Core/SDAnimatedImageView.m index 88412aac..c480d004 100644 --- a/SDWebImage/Core/SDAnimatedImageView.m +++ b/SDWebImage/Core/SDAnimatedImageView.m @@ -18,6 +18,7 @@ @interface SDAnimatedImageView () { BOOL _initFinished; // Extra flag to mark the `commonInit` is called + double _playRate; } @property (nonatomic, strong, readwrite) UIImage *currentFrame; @@ -92,6 +93,7 @@ // So the properties which rely on this order, should using lazy-evaluation or do extra check in `setImage:`. self.shouldCustomLoopCount = NO; self.shouldIncrementalLoad = YES; + self.playRate = 1.0; #if SD_MAC self.wantsLayer = YES; #endif @@ -147,6 +149,9 @@ self.player.totalLoopCount = self.animationRepeatCount; } + // Play Rate + self.player.playRate = self.playRate; + // Setup handler @weakify(self); self.player.animationFrameHandler = ^(NSUInteger index, UIImage * frame) { @@ -191,6 +196,20 @@ return self.player.runLoopMode; } +- (void)setPlayRate:(double)playRate +{ + _playRate = playRate; + self.player.playRate = playRate; +} + +- (double)playRate +{ + if (!_initFinished) { + return 1.0; // Defaults to 1.0 + } + return _playRate; +} + - (BOOL)shouldIncrementalLoad { if (!_initFinished) { From 25e96717d60fb7f94a92c2547128e4e14c0df451 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2019 00:48:15 +0800 Subject: [PATCH 2/4] Remove the cocoapods spec source to get benefit from 1.8.0+ CDN source --- Podfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Podfile b/Podfile index 44ea977f..d753b5b9 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,3 @@ -source 'https://github.com/CocoaPods/Specs.git' - use_frameworks! def all_example_pods From 4e157a84f9d299ad422d39f5e2aaaf99f62b352f Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2019 01:00:16 +0800 Subject: [PATCH 3/4] Check playRate in advance to avoid querying to frame duration --- SDWebImage/Core/SDAnimatedImagePlayer.m | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.m b/SDWebImage/Core/SDAnimatedImagePlayer.m index e358f136..228f0087 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.m +++ b/SDWebImage/Core/SDAnimatedImagePlayer.m @@ -228,17 +228,18 @@ NSUInteger currentFrameIndex = self.currentFrameIndex; NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; + NSTimeInterval playRate = self.playRate; + if (playRate <= 0) { + // Does not support <= 0 play rate + [self stopPlaying]; + return; + } + // Check if we have the frame buffer firstly to improve performance if (!self.bufferMiss) { // Then check if timestamp is reached self.currentTime += duration; NSTimeInterval currentDuration = [self.animatedProvider animatedImageDurationAtIndex:currentFrameIndex]; - NSTimeInterval playRate = self.playRate; - if (playRate <= 0) { - // Does not support <= 0 play rate - [self stopPlaying]; - return; - } currentDuration = currentDuration / playRate; if (self.currentTime < currentDuration) { // Current frame timestamp not reached, return From 867da45cafd7982733973e762daed0b58d6a3073 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 6 Nov 2019 01:18:49 +0800 Subject: [PATCH 4/4] Rename playRate to playbackRate, match the Apple API naming --- SDWebImage/Core/SDAnimatedImagePlayer.h | 2 +- SDWebImage/Core/SDAnimatedImagePlayer.m | 10 +++++----- SDWebImage/Core/SDAnimatedImageView.h | 2 +- SDWebImage/Core/SDAnimatedImageView.m | 16 ++++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.h b/SDWebImage/Core/SDAnimatedImagePlayer.h index cec6f9f7..b6fa88d0 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.h +++ b/SDWebImage/Core/SDAnimatedImagePlayer.h @@ -35,7 +35,7 @@ /// `0.0-1.0` means the slow speed. /// `> 1.0` means the fast speed. /// `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future) -@property (nonatomic, assign) double playRate; +@property (nonatomic, assign) double playbackRate; /// Provide a max buffer size by bytes. This is used to adjust frame buffer count and can be useful when the decoding cost is expensive (such as Animated WebP software decoding). Default is 0. /// `0` means automatically adjust by calculating current memory usage. diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.m b/SDWebImage/Core/SDAnimatedImagePlayer.m index 228f0087..c15a99db 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.m +++ b/SDWebImage/Core/SDAnimatedImagePlayer.m @@ -44,7 +44,7 @@ // Get the current frame and loop count. self.totalLoopCount = provider.animatedImageLoopCount; self.animatedProvider = provider; - self.playRate = 1.0; + self.playbackRate = 1.0; #if SD_UIKIT [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; #endif @@ -228,8 +228,8 @@ NSUInteger currentFrameIndex = self.currentFrameIndex; NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; - NSTimeInterval playRate = self.playRate; - if (playRate <= 0) { + NSTimeInterval playbackRate = self.playbackRate; + if (playbackRate <= 0) { // Does not support <= 0 play rate [self stopPlaying]; return; @@ -240,14 +240,14 @@ // Then check if timestamp is reached self.currentTime += duration; NSTimeInterval currentDuration = [self.animatedProvider animatedImageDurationAtIndex:currentFrameIndex]; - currentDuration = currentDuration / playRate; + currentDuration = currentDuration / playbackRate; if (self.currentTime < currentDuration) { // Current frame timestamp not reached, return return; } self.currentTime -= currentDuration; NSTimeInterval nextDuration = [self.animatedProvider animatedImageDurationAtIndex:nextFrameIndex]; - nextDuration = nextDuration / playRate; + nextDuration = nextDuration / playbackRate; if (self.currentTime > nextDuration) { // Do not skip frame self.currentTime = nextDuration; diff --git a/SDWebImage/Core/SDAnimatedImageView.h b/SDWebImage/Core/SDAnimatedImageView.h index fc875235..4f338826 100644 --- a/SDWebImage/Core/SDAnimatedImageView.h +++ b/SDWebImage/Core/SDAnimatedImageView.h @@ -51,7 +51,7 @@ `> 1.0` means the fast speed. `< 0.0` is not supported currently and stop animation. (may support reverse playback in the future) */ -@property (nonatomic, assign) double playRate; +@property (nonatomic, assign) double playbackRate; /** Provide a max buffer size by bytes. This is used to adjust frame buffer count and can be useful when the decoding cost is expensive (such as Animated WebP software decoding). Default is 0. `0` means automatically adjust by calculating current memory usage. diff --git a/SDWebImage/Core/SDAnimatedImageView.m b/SDWebImage/Core/SDAnimatedImageView.m index c480d004..f42a196d 100644 --- a/SDWebImage/Core/SDAnimatedImageView.m +++ b/SDWebImage/Core/SDAnimatedImageView.m @@ -18,7 +18,7 @@ @interface SDAnimatedImageView () { BOOL _initFinished; // Extra flag to mark the `commonInit` is called - double _playRate; + double _playbackRate; } @property (nonatomic, strong, readwrite) UIImage *currentFrame; @@ -93,7 +93,7 @@ // So the properties which rely on this order, should using lazy-evaluation or do extra check in `setImage:`. self.shouldCustomLoopCount = NO; self.shouldIncrementalLoad = YES; - self.playRate = 1.0; + self.playbackRate = 1.0; #if SD_MAC self.wantsLayer = YES; #endif @@ -150,7 +150,7 @@ } // Play Rate - self.player.playRate = self.playRate; + self.player.playbackRate = self.playbackRate; // Setup handler @weakify(self); @@ -196,18 +196,18 @@ return self.player.runLoopMode; } -- (void)setPlayRate:(double)playRate +- (void)setPlaybackRate:(double)playbackRate { - _playRate = playRate; - self.player.playRate = playRate; + _playbackRate = playbackRate; + self.player.playbackRate = playbackRate; } -- (double)playRate +- (double)playbackRate { if (!_initFinished) { return 1.0; // Defaults to 1.0 } - return _playRate; + return _playbackRate; } - (BOOL)shouldIncrementalLoad