Răsfoiți Sursa

Merge pull request #2895 from ZXIOU/fixAnimatedImageBugs

Fix animated image playback bugs
DreamPiggy 6 ani în urmă
părinte
comite
2e4107e21e

BIN
.DS_Store


+ 2 - 0
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 {

+ 1 - 0
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",

BIN
SDWebImage/.DS_Store


+ 66 - 52
SDWebImage/Core/SDAnimatedImagePlayer.m

@@ -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,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

+ 13 - 0
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) {