fix the core render of SDAnimatedImagePlayer and fix assignment animationRepeatCount does not work

Signed-off-by: zxiou <zhangxu6@xiaomi.com>
This commit is contained in:
zxiou 2019-11-21 19:17:44 +08:00
parent 7adc385b7d
commit fe0e42afc2
8 changed files with 90 additions and 52 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
Examples/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -24,6 +24,8 @@
[self.imageView sd_setImageWithURL:self.imageURL [self.imageView sd_setImageWithURL:self.imageURL
placeholderImage:nil placeholderImage:nil
options:SDWebImageProgressiveLoad]; options:SDWebImageProgressiveLoad];
self.imageView.shouldCustomLoopCount = YES;
self.imageView.animationRepeatCount = NSIntegerMax;
} }
- (void)viewDidLoad { - (void)viewDidLoad {

View File

@ -63,6 +63,7 @@
[SDWebImageDownloader sharedDownloader].config.executionOrder = SDWebImageDownloaderLIFOExecutionOrder; [SDWebImageDownloader sharedDownloader].config.executionOrder = SDWebImageDownloaderLIFOExecutionOrder;
self.objects = [NSMutableArray arrayWithObjects: 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://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", @"http://assets.sbnation.com/assets/2512203/dogflops.gif",
@"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif", @"https://raw.githubusercontent.com/liyong03/YLGIFImage/master/YLGIFImageDemo/YLGIFImageDemo/joy.gif",

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

BIN
SDWebImage/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -23,6 +23,7 @@
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer; @property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
@property (nonatomic, assign) NSTimeInterval currentTime; @property (nonatomic, assign) NSTimeInterval currentTime;
@property (nonatomic, assign) BOOL bufferMiss; @property (nonatomic, assign) BOOL bufferMiss;
@property (nonatomic, assign) BOOL needsDisplayWhenImageBecomesAvailable;
@property (nonatomic, assign) NSUInteger maxBufferCount; @property (nonatomic, assign) NSUInteger maxBufferCount;
@property (nonatomic, strong) NSOperationQueue *fetchQueue; @property (nonatomic, strong) NSOperationQueue *fetchQueue;
@property (nonatomic, strong) dispatch_semaphore_t lock; @property (nonatomic, strong) dispatch_semaphore_t lock;
@ -165,6 +166,7 @@
self.currentLoopCount = 0; self.currentLoopCount = 0;
self.currentTime = 0; self.currentTime = 0;
self.bufferMiss = NO; self.bufferMiss = NO;
self.needsDisplayWhenImageBecomesAvailable = NO;
[self handleFrameChange]; [self handleFrameChange];
} }
@ -217,8 +219,6 @@
if (!self.isPlaying) { if (!self.isPlaying) {
return; return;
} }
// Calculate refresh duration
NSTimeInterval duration = self.displayLink.duration;
NSUInteger totalFrameCount = self.totalFrameCount; NSUInteger totalFrameCount = self.totalFrameCount;
if (totalFrameCount <= 1) { if (totalFrameCount <= 1) {
@ -226,8 +226,6 @@
[self stopPlaying]; [self stopPlaying];
return; return;
} }
NSUInteger currentFrameIndex = self.currentFrameIndex;
NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount;
NSTimeInterval playbackRate = self.playbackRate; NSTimeInterval playbackRate = self.playbackRate;
if (playbackRate <= 0) { if (playbackRate <= 0) {
@ -236,7 +234,46 @@
return; 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) { if (!self.bufferMiss) {
// Then check if timestamp is reached // Then check if timestamp is reached
self.currentTime += duration; self.currentTime += duration;
@ -246,6 +283,10 @@
// Current frame timestamp not reached, return // Current frame timestamp not reached, return
return; return;
} }
// Otherwise, we shoudle be ready to display next frame
self.needsDisplayWhenImageBecomesAvailable = YES;
self.currentFrameIndex = nextFrameIndex;
self.currentTime -= currentDuration; self.currentTime -= currentDuration;
NSTimeInterval nextDuration = [self.animatedProvider animatedImageDurationAtIndex:nextFrameIndex]; NSTimeInterval nextDuration = [self.animatedProvider animatedImageDurationAtIndex:nextFrameIndex];
nextDuration = nextDuration / playbackRate; nextDuration = nextDuration / playbackRate;
@ -253,45 +294,19 @@
// Do not skip frame // Do not skip frame
self.currentTime = nextDuration; self.currentTime = nextDuration;
} }
}
// Update the loop count when last frame rendered
// Update the current frame if (nextFrameIndex == 0) {
UIImage *currentFrame; // Update the loop count
UIImage *fetchFrame; self.currentLoopCount++;
SD_LOCK(self.lock); [self handleLoopChnage];
currentFrame = self.frameBuffer[@(currentFrameIndex)];
fetchFrame = currentFrame ? self.frameBuffer[@(nextFrameIndex)] : nil; // if reached the max loop count, stop animating, 0 means loop indefinitely
SD_UNLOCK(self.lock); NSUInteger maxLoopCount = self.totalLoopCount;
BOOL bufferFull = NO; if (maxLoopCount != 0 && (self.currentLoopCount >= maxLoopCount)) {
if (currentFrame) { [self stopPlaying];
SD_LOCK(self.lock); return;
// 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;
} }
} }
@ -301,14 +316,13 @@
} }
// Check if we should prefetch next frame or current frame // Check if we should prefetch next frame or current frame
NSUInteger fetchFrameIndex; // When buffer miss, means the decode speed is slower than render speed, we fetch current miss frame
if (self.bufferMiss) { // Or, most cases, the decode speed is faster than render speed, we fetch next frame
// When buffer miss, means the decode speed is slower than render speed, we fetch current miss frame NSUInteger fetchFrameIndex = self.bufferMiss? currentFrameIndex : nextFrameIndex;
fetchFrameIndex = currentFrameIndex; UIImage *fetchFrame;
} else { SD_LOCK(self.lock);
// Or, most cases, the decode speed is faster than render speed, we fetch next frame fetchFrame = self.bufferMiss? nil : self.frameBuffer[@(nextFrameIndex)];
fetchFrameIndex = nextFrameIndex; SD_UNLOCK(self.lock);
}
if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) { if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) {
// Prefetch next frame in background queue // Prefetch next frame in background queue

View File

@ -306,6 +306,19 @@
#pragma mark - UIImageView Method Overrides #pragma mark - UIImageView Method Overrides
#pragma mark Image Data #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 - (void)startAnimating
{ {
if (self.player) { if (self.player) {