mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-03 19:30:09 +00:00
269 lines
6.6 KiB
Plaintext
269 lines
6.6 KiB
Plaintext
//
|
|
// ASImageNode+AnimatedImage.mm
|
|
// AsyncDisplayKit
|
|
//
|
|
// Created by Garrett Moon on 3/22/16.
|
|
//
|
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under the BSD-style license found in the
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
//
|
|
|
|
#import <AsyncDisplayKit/ASImageNode.h>
|
|
|
|
#import <AsyncDisplayKit/ASAssert.h>
|
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
|
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
|
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
|
#import <AsyncDisplayKit/ASEqualityHelpers.h>
|
|
#import <AsyncDisplayKit/ASImageNode+AnimatedImagePrivate.h>
|
|
#import <AsyncDisplayKit/ASImageProtocols.h>
|
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
|
#import <AsyncDisplayKit/ASNetworkImageNode.h>
|
|
#import <AsyncDisplayKit/ASWeakProxy.h>
|
|
|
|
NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes;
|
|
|
|
@implementation ASImageNode (AnimatedImage)
|
|
|
|
#pragma mark - GIF support
|
|
|
|
- (void)setAnimatedImage:(id <ASAnimatedImageProtocol>)animatedImage
|
|
{
|
|
ASDN::MutexLocker l(_animatedImageLock);
|
|
if (ASObjectIsEqual(_animatedImage, animatedImage)) {
|
|
return;
|
|
}
|
|
|
|
_animatedImage = animatedImage;
|
|
|
|
if (animatedImage != nil) {
|
|
__weak ASImageNode *weakSelf = self;
|
|
if ([animatedImage respondsToSelector:@selector(setCoverImageReadyCallback:)]) {
|
|
animatedImage.coverImageReadyCallback = ^(UIImage *coverImage) {
|
|
[weakSelf coverImageCompleted:coverImage];
|
|
};
|
|
}
|
|
|
|
if (animatedImage.playbackReady) {
|
|
[self animatedImageFileReady];
|
|
}
|
|
|
|
animatedImage.playbackReadyCallback = ^{
|
|
[weakSelf animatedImageFileReady];
|
|
};
|
|
}
|
|
}
|
|
|
|
- (id <ASAnimatedImageProtocol>)animatedImage
|
|
{
|
|
ASDN::MutexLocker l(_animatedImageLock);
|
|
return _animatedImage;
|
|
}
|
|
|
|
- (void)setAnimatedImagePaused:(BOOL)animatedImagePaused
|
|
{
|
|
ASDN::MutexLocker l(_animatedImageLock);
|
|
_animatedImagePaused = animatedImagePaused;
|
|
ASPerformBlockOnMainThread(^{
|
|
if (animatedImagePaused) {
|
|
[self stopAnimating];
|
|
} else {
|
|
[self startAnimating];
|
|
}
|
|
});
|
|
}
|
|
|
|
- (BOOL)animatedImagePaused
|
|
{
|
|
ASDN::MutexLocker l(_animatedImageLock);
|
|
return _animatedImagePaused;
|
|
}
|
|
|
|
- (void)coverImageCompleted:(UIImage *)coverImage
|
|
{
|
|
BOOL setCoverImage = YES;
|
|
{
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
if (_displayLink != nil && _displayLink.paused == NO) {
|
|
setCoverImage = NO;
|
|
}
|
|
}
|
|
|
|
if (setCoverImage) {
|
|
[self setCoverImage:coverImage];
|
|
}
|
|
}
|
|
|
|
- (void)setCoverImage:(UIImage *)coverImage
|
|
{
|
|
//If we're a network image node, we want to set the default image so
|
|
//that it will correctly be restored if it exits the range.
|
|
if ([self isKindOfClass:[ASNetworkImageNode class]]) {
|
|
[(ASNetworkImageNode *)self setDefaultImage:coverImage];
|
|
} else {
|
|
self.image = coverImage;
|
|
}
|
|
}
|
|
|
|
- (NSString *)animatedImageRunLoopMode
|
|
{
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
return _animatedImageRunLoopMode;
|
|
}
|
|
|
|
- (void)setAnimatedImageRunLoopMode:(NSString *)runLoopMode
|
|
{
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
|
|
if (runLoopMode == nil) {
|
|
runLoopMode = ASAnimatedImageDefaultRunLoopMode;
|
|
}
|
|
|
|
if (_displayLink != nil) {
|
|
[_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_animatedImageRunLoopMode];
|
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:runLoopMode];
|
|
}
|
|
_animatedImageRunLoopMode = runLoopMode;
|
|
}
|
|
|
|
- (void)animatedImageFileReady
|
|
{
|
|
ASPerformBlockOnMainThread(^{
|
|
[self startAnimating];
|
|
});
|
|
}
|
|
|
|
- (void)startAnimating
|
|
{
|
|
ASDisplayNodeAssertMainThread();
|
|
if (ASInterfaceStateIncludesVisible(self.interfaceState) == NO) {
|
|
return;
|
|
}
|
|
|
|
if (self.animatedImagePaused) {
|
|
return;
|
|
}
|
|
|
|
if (self.animatedImage.playbackReady == NO) {
|
|
return;
|
|
}
|
|
|
|
#if ASAnimatedImageDebug
|
|
NSLog(@"starting animation: %p", self);
|
|
#endif
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
if (_displayLink == nil) {
|
|
_playHead = 0;
|
|
_displayLink = [CADisplayLink displayLinkWithTarget:[ASWeakProxy weakProxyWithTarget:self] selector:@selector(displayLinkFired:)];
|
|
_displayLink.frameInterval = self.animatedImage.frameInterval;
|
|
|
|
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:_animatedImageRunLoopMode];
|
|
} else {
|
|
_displayLink.paused = NO;
|
|
}
|
|
}
|
|
|
|
- (void)stopAnimating
|
|
{
|
|
#if ASAnimatedImageDebug
|
|
NSLog(@"stopping animation: %p", self);
|
|
#endif
|
|
ASDisplayNodeAssertMainThread();
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
_displayLink.paused = YES;
|
|
self.lastDisplayLinkFire = 0;
|
|
|
|
[self.animatedImage clearAnimatedImageCache];
|
|
}
|
|
|
|
- (void)didEnterVisibleState
|
|
{
|
|
ASDisplayNodeAssertMainThread();
|
|
[super didEnterVisibleState];
|
|
|
|
if (self.animatedImage.coverImageReady) {
|
|
[self setCoverImage:self.animatedImage.coverImage];
|
|
}
|
|
[self startAnimating];
|
|
}
|
|
|
|
- (void)didExitVisibleState
|
|
{
|
|
ASDisplayNodeAssertMainThread();
|
|
[super didExitVisibleState];
|
|
|
|
[self stopAnimating];
|
|
}
|
|
|
|
- (void)displayLinkFired:(CADisplayLink *)displayLink
|
|
{
|
|
ASDisplayNodeAssertMainThread();
|
|
|
|
CFTimeInterval timeBetweenLastFire;
|
|
if (self.lastDisplayLinkFire == 0) {
|
|
timeBetweenLastFire = 0;
|
|
} else {
|
|
timeBetweenLastFire = CACurrentMediaTime() - self.lastDisplayLinkFire;
|
|
}
|
|
self.lastDisplayLinkFire = CACurrentMediaTime();
|
|
|
|
_playHead += timeBetweenLastFire;
|
|
|
|
while (_playHead > self.animatedImage.totalDuration) {
|
|
_playHead -= self.animatedImage.totalDuration;
|
|
_playedLoops++;
|
|
}
|
|
|
|
if (self.animatedImage.loopCount > 0 && _playedLoops >= self.animatedImage.loopCount) {
|
|
[self stopAnimating];
|
|
return;
|
|
}
|
|
|
|
NSUInteger frameIndex = [self frameIndexAtPlayHeadPosition:_playHead];
|
|
CGImageRef frameImage = [self.animatedImage imageAtIndex:frameIndex];
|
|
|
|
if (frameImage == nil) {
|
|
_playHead -= timeBetweenLastFire;
|
|
//Pause the display link until we get a file ready notification
|
|
displayLink.paused = YES;
|
|
self.lastDisplayLinkFire = 0;
|
|
} else {
|
|
self.contents = (__bridge id)frameImage;
|
|
}
|
|
}
|
|
|
|
- (NSUInteger)frameIndexAtPlayHeadPosition:(CFTimeInterval)playHead
|
|
{
|
|
ASDisplayNodeAssertMainThread();
|
|
NSUInteger frameIndex = 0;
|
|
for (NSUInteger durationIndex = 0; durationIndex < self.animatedImage.frameCount; durationIndex++) {
|
|
playHead -= [self.animatedImage durationAtIndex:durationIndex];
|
|
if (playHead < 0) {
|
|
return frameIndex;
|
|
}
|
|
frameIndex++;
|
|
}
|
|
|
|
return frameIndex;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation ASImageNode(AnimatedImageInvalidation)
|
|
|
|
- (void)invalidateAnimatedImage
|
|
{
|
|
ASDN::MutexLocker l(_displayLinkLock);
|
|
#if ASAnimatedImageDebug
|
|
if (_displayLink) {
|
|
NSLog(@"invalidating display link");
|
|
}
|
|
#endif
|
|
[_displayLink invalidate];
|
|
_displayLink = nil;
|
|
}
|
|
|
|
@end
|