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
|
[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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Binary file not shown.
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue