diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..eeded2ff Binary files /dev/null and b/.DS_Store differ diff --git a/Examples/SDWebImage Demo/DetailViewController.m b/Examples/SDWebImage Demo/DetailViewController.m index 4327c9dd..ee9be9df 100644 --- a/Examples/SDWebImage Demo/DetailViewController.m +++ b/Examples/SDWebImage Demo/DetailViewController.m @@ -24,6 +24,8 @@ [self.imageView sd_setImageWithURL:self.imageURL placeholderImage:nil options:SDWebImageProgressiveLoad]; + self.imageView.shouldCustomLoopCount = YES; + self.imageView.animationRepeatCount = NSIntegerMax; } - (void)viewDidLoad { diff --git a/Examples/SDWebImage Demo/MasterViewController.m b/Examples/SDWebImage Demo/MasterViewController.m index 14aea5ca..bb86ba17 100644 --- a/Examples/SDWebImage Demo/MasterViewController.m +++ b/Examples/SDWebImage Demo/MasterViewController.m @@ -63,6 +63,7 @@ [SDWebImageDownloader sharedDownloader].config.executionOrder = SDWebImageDownloaderLIFOExecutionOrder; self.objects = [NSMutableArray arrayWithObjects: + @"https://s2.ax1x.com/2019/11/01/KHYIgJ.gif", @"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", diff --git a/SDWebImage/.DS_Store b/SDWebImage/.DS_Store new file mode 100644 index 00000000..3ed219f2 Binary files /dev/null and b/SDWebImage/.DS_Store differ diff --git a/SDWebImage/Core/SDAnimatedImagePlayer.m b/SDWebImage/Core/SDAnimatedImagePlayer.m index 2efd8805..39658b43 100644 --- a/SDWebImage/Core/SDAnimatedImagePlayer.m +++ b/SDWebImage/Core/SDAnimatedImagePlayer.m @@ -23,6 +23,7 @@ @property (nonatomic, strong) NSMutableDictionary *frameBuffer; @property (nonatomic, assign) NSTimeInterval currentTime; @property (nonatomic, assign) BOOL bufferMiss; +@property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable; @property (nonatomic, assign) NSUInteger maxBufferCount; @property (nonatomic, strong) NSOperationQueue *fetchQueue; @property (nonatomic, strong) dispatch_semaphore_t lock; @@ -165,6 +166,7 @@ self.currentLoopCount = 0; self.currentTime = 0; self.bufferMiss = NO; + self.needsDisplayWhenImageBecomesAvailable = NO; [self handleFrameChange]; } @@ -217,8 +219,6 @@ if (!self.isPlaying) { return; } - // Calculate refresh duration - NSTimeInterval duration = self.displayLink.duration; NSUInteger totalFrameCount = self.totalFrameCount; if (totalFrameCount <= 1) { @@ -226,8 +226,6 @@ [self stopPlaying]; return; } - NSUInteger currentFrameIndex = self.currentFrameIndex; - NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; NSTimeInterval playbackRate = self.playbackRate; if (playbackRate <= 0) { @@ -236,7 +234,46 @@ return; } - // Check if we have the frame buffer firstly to improve performance + // Calculate refresh duration + NSTimeInterval duration = self.displayLink.duration; + + NSUInteger currentFrameIndex = self.currentFrameIndex; + NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; + + // Check if we need to display new frame firstly + BOOL bufferFull = NO; + if (self.needsDisplayWhenImageBecomesAvailable) { + UIImage *currentFrame; + SD_LOCK(self.lock); + currentFrame = self.frameBuffer[@(currentFrameIndex)]; + SD_UNLOCK(self.lock); + + // Update the current frame + if (currentFrame) { + SD_LOCK(self.lock); + // Remove the frame buffer if need + if (self.frameBuffer.count > self.maxBufferCount) { + self.frameBuffer[@(currentFrameIndex)] = nil; + } + // Check whether we can stop fetch + if (self.frameBuffer.count == totalFrameCount) { + bufferFull = YES; + } + SD_UNLOCK(self.lock); + + // Update the current frame immediately + self.currentFrame = currentFrame; + [self handleFrameChange]; + + self.bufferMiss = NO; + self.needsDisplayWhenImageBecomesAvailable = NO; + } + else { + self.bufferMiss = YES; + } + } + + // Check if we have the frame buffer if (!self.bufferMiss) { // Then check if timestamp is reached self.currentTime += duration; @@ -246,6 +283,10 @@ // Current frame timestamp not reached, return return; } + + // Otherwise, we shoudle be ready to display next frame + self.needsDisplayWhenImageBecomesAvailable = YES; + self.currentFrameIndex = nextFrameIndex; self.currentTime -= currentDuration; NSTimeInterval nextDuration = [self.animatedProvider animatedImageDurationAtIndex:nextFrameIndex]; nextDuration = nextDuration / playbackRate; @@ -253,45 +294,19 @@ // Do not skip frame self.currentTime = nextDuration; } - } - - // Update the current frame - UIImage *currentFrame; - UIImage *fetchFrame; - SD_LOCK(self.lock); - currentFrame = self.frameBuffer[@(currentFrameIndex)]; - fetchFrame = currentFrame ? self.frameBuffer[@(nextFrameIndex)] : nil; - SD_UNLOCK(self.lock); - BOOL bufferFull = NO; - if (currentFrame) { - SD_LOCK(self.lock); - // Remove the frame buffer if need - if (self.frameBuffer.count > self.maxBufferCount) { - self.frameBuffer[@(currentFrameIndex)] = nil; - } - // Check whether we can stop fetch - if (self.frameBuffer.count == totalFrameCount) { - bufferFull = YES; - } - SD_UNLOCK(self.lock); - self.currentFrame = currentFrame; - [self handleFrameChange]; - self.currentFrameIndex = nextFrameIndex; - self.bufferMiss = NO; - } else { - self.bufferMiss = YES; - } - - // Update the loop count when last frame rendered - if (nextFrameIndex == 0 && !self.bufferMiss) { - // Update the loop count - self.currentLoopCount++; - [self handleLoopChnage]; - // if reached the max loop count, stop animating, 0 means loop indefinitely - NSUInteger maxLoopCount = self.totalLoopCount; - if (maxLoopCount != 0 && (self.currentLoopCount >= maxLoopCount)) { - [self stopPlaying]; - return; + + // Update the loop count when last frame rendered + if (nextFrameIndex == 0) { + // Update the loop count + self.currentLoopCount++; + [self handleLoopChnage]; + + // if reached the max loop count, stop animating, 0 means loop indefinitely + NSUInteger maxLoopCount = self.totalLoopCount; + if (maxLoopCount != 0 && (self.currentLoopCount >= maxLoopCount)) { + [self stopPlaying]; + return; + } } } @@ -301,14 +316,13 @@ } // Check if we should prefetch next frame or current frame - NSUInteger fetchFrameIndex; - if (self.bufferMiss) { - // When buffer miss, means the decode speed is slower than render speed, we fetch current miss frame - fetchFrameIndex = currentFrameIndex; - } else { - // Or, most cases, the decode speed is faster than render speed, we fetch next frame - fetchFrameIndex = nextFrameIndex; - } + // When buffer miss, means the decode speed is slower than render speed, we fetch current miss frame + // Or, most cases, the decode speed is faster than render speed, we fetch next frame + NSUInteger fetchFrameIndex = self.bufferMiss? currentFrameIndex : nextFrameIndex; + UIImage *fetchFrame; + SD_LOCK(self.lock); + fetchFrame = self.bufferMiss? nil : self.frameBuffer[@(nextFrameIndex)]; + SD_UNLOCK(self.lock); if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) { // Prefetch next frame in background queue diff --git a/SDWebImage/Core/SDAnimatedImageView.m b/SDWebImage/Core/SDAnimatedImageView.m index 181f2db9..78f1cb8b 100644 --- a/SDWebImage/Core/SDAnimatedImageView.m +++ b/SDWebImage/Core/SDAnimatedImageView.m @@ -306,6 +306,19 @@ #pragma mark - UIImageView Method Overrides #pragma mark Image Data +- (void)setAnimationRepeatCount:(NSInteger)animationRepeatCount +{ +#if SD_UIKIT + [super setAnimationRepeatCount:animationRepeatCount]; +#else + _animationRepeatCount = animationRepeatCount; +#endif + + if (self.shouldCustomLoopCount) { + self.player.totalLoopCount = animationRepeatCount; + } +} + - (void)startAnimating { if (self.player) {