Merge pull request #2895 from ZXIOU/fixAnimatedImageBugs
Fix animated image playback bugs
This commit is contained in:
commit
2e4107e21e
|
@ -24,6 +24,8 @@
|
|||
[self.imageView sd_setImageWithURL:self.imageURL
|
||||
placeholderImage:nil
|
||||
options:SDWebImageProgressiveLoad];
|
||||
self.imageView.shouldCustomLoopCount = YES;
|
||||
self.imageView.animationRepeatCount = NSIntegerMax;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
|
|
|
@ -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",
|
||||
|
|
Binary file not shown.
|
@ -23,6 +23,7 @@
|
|||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *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,33 +234,21 @@
|
|||
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];
|
||||
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 / playbackRate;
|
||||
if (self.currentTime > nextDuration) {
|
||||
// Do not skip frame
|
||||
self.currentTime = nextDuration;
|
||||
}
|
||||
}
|
||||
// Calculate refresh duration
|
||||
NSTimeInterval duration = self.displayLink.duration;
|
||||
|
||||
// Update the current frame
|
||||
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;
|
||||
UIImage *fetchFrame;
|
||||
SD_LOCK(self.lock);
|
||||
currentFrame = self.frameBuffer[@(currentFrameIndex)];
|
||||
fetchFrame = currentFrame ? self.frameBuffer[@(nextFrameIndex)] : nil;
|
||||
SD_UNLOCK(self.lock);
|
||||
BOOL bufferFull = NO;
|
||||
|
||||
// Update the current frame
|
||||
if (currentFrame) {
|
||||
SD_LOCK(self.lock);
|
||||
// Remove the frame buffer if need
|
||||
|
@ -274,19 +260,47 @@
|
|||
bufferFull = YES;
|
||||
}
|
||||
SD_UNLOCK(self.lock);
|
||||
|
||||
// Update the current frame immediately
|
||||
self.currentFrame = currentFrame;
|
||||
[self handleFrameChange];
|
||||
self.currentFrameIndex = nextFrameIndex;
|
||||
|
||||
self.bufferMiss = NO;
|
||||
} else {
|
||||
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;
|
||||
NSTimeInterval currentDuration = [self.animatedProvider animatedImageDurationAtIndex:currentFrameIndex];
|
||||
currentDuration = currentDuration / playbackRate;
|
||||
if (self.currentTime < currentDuration) {
|
||||
// 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;
|
||||
if (self.currentTime > nextDuration) {
|
||||
// Do not skip frame
|
||||
self.currentTime = nextDuration;
|
||||
}
|
||||
|
||||
// Update the loop count when last frame rendered
|
||||
if (nextFrameIndex == 0 && !self.bufferMiss) {
|
||||
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)) {
|
||||
|
@ -294,6 +308,7 @@
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since we support handler, check animating state again
|
||||
if (!self.isPlaying) {
|
||||
|
@ -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;
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue