From a36e27f8339eb83b0cd660f196c8f72e11eec344 Mon Sep 17 00:00:00 2001 From: zhongwuzw Date: Wed, 29 May 2024 08:16:42 -0700 Subject: [PATCH] Fixes animated frame timing (#44622) Summary: Fixes https://github.com/facebook/react-native/issues/44608 . ## Changelog: [IOS] [FIXED] - Fixes animated frame timing Pull Request resolved: https://github.com/facebook/react-native/pull/44622 Test Plan: Example in https://github.com/facebook/react-native/issues/44608 . Reviewed By: sammy-SC Differential Revision: D57559884 Pulled By: cipolleschi fbshipit-source-id: 33eaf71c24eb83715403a67b3471dd1ac2dd9d2f --- .../Libraries/Image/RCTUIImageViewAnimated.mm | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm b/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm index b2247c58d0593c..62131f50acb6f1 100644 --- a/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm +++ b/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm @@ -121,6 +121,8 @@ - (void)setImage:(UIImage *)image // Calculate max buffer size [self calculateMaxBufferCount]; + [self prefetchNextFrame:nil fetchFrameIndex:1]; + if ([self paused]) { [self start]; } @@ -167,6 +169,21 @@ - (CADisplayLink *)displayLink return _displayLink; } +- (void)prefetchNextFrame:(UIImage *)fetchFrame fetchFrameIndex:(NSInteger)fetchFrameIndex +{ + if (!fetchFrame && !(self.frameBuffer.count == self.totalFrameCount) && self.fetchQueue.operationCount == 0) { + // Prefetch next frame in background queue + UIImage *animatedImage = self.animatedImage; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + UIImage *frame = [animatedImage animatedImageFrameAtIndex:fetchFrameIndex]; + dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); + self.frameBuffer[@(fetchFrameIndex)] = frame; + dispatch_semaphore_signal(self.lock); + }]; + [self.fetchQueue addOperation:operation]; + } +} + #pragma mark - Animation - (void)start @@ -210,6 +227,9 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink // Do not skip frame self.currentTime = nextDuration; } + currentFrameIndex = nextFrameIndex; + self.currentFrameIndex = nextFrameIndex; + nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; } // Update the current frame @@ -219,20 +239,14 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink currentFrame = self.frameBuffer[@(currentFrameIndex)]; fetchFrame = currentFrame ? self.frameBuffer[@(nextFrameIndex)] : nil; dispatch_semaphore_signal(self.lock); - BOOL bufferFull = NO; if (currentFrame) { dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); // 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; - } dispatch_semaphore_signal(self.lock); self.currentFrame = currentFrame; - self.currentFrameIndex = nextFrameIndex; self.bufferMiss = NO; [self.layer setNeedsDisplay]; } else { @@ -261,17 +275,7 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink fetchFrameIndex = nextFrameIndex; } - if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) { - // Prefetch next frame in background queue - UIImage *animatedImage = self.animatedImage; - NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ - UIImage *frame = [animatedImage animatedImageFrameAtIndex:fetchFrameIndex]; - dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER); - self.frameBuffer[@(fetchFrameIndex)] = frame; - dispatch_semaphore_signal(self.lock); - }]; - [self.fetchQueue addOperation:operation]; - } + [self prefetchNextFrame:fetchFrame fetchFrameIndex:fetchFrameIndex]; } #pragma mark - CALayerDelegate