Introduce frame pool for SDAnimatedImage playback. Solve when multiple image view references the same URL image cause un-wanted decode which waste RAM/CPU
This commit is contained in:
parent
3289629ef6
commit
a206229905
|
@ -45,6 +45,9 @@
|
||||||
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 321E60BC1F38E91700405457 /* UIImage+ForceDecode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 321E60BC1F38E91700405457 /* UIImage+ForceDecode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */; };
|
321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */; };
|
||||||
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */; };
|
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */ = {isa = PBXBuildFile; fileRef = 321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */; };
|
||||||
|
3237321429F8D0D600D1DA41 /* SDImageFramePool.h in Headers */ = {isa = PBXBuildFile; fileRef = 3237321229F8D0D600D1DA41 /* SDImageFramePool.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||||
|
3237321529F8D0D600D1DA41 /* SDImageFramePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 3237321329F8D0D600D1DA41 /* SDImageFramePool.m */; };
|
||||||
|
3237321629F8D0E200D1DA41 /* SDImageFramePool.m in Sources */ = {isa = PBXBuildFile; fileRef = 3237321329F8D0D600D1DA41 /* SDImageFramePool.m */; };
|
||||||
3237F9E820161AE000A88143 /* NSImage+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4397D2F51D0DE2DF00BB2784 /* NSImage+Compatibility.m */; };
|
3237F9E820161AE000A88143 /* NSImage+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4397D2F51D0DE2DF00BB2784 /* NSImage+Compatibility.m */; };
|
||||||
3237F9EB20161AE000A88143 /* NSImage+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4397D2F51D0DE2DF00BB2784 /* NSImage+Compatibility.m */; };
|
3237F9EB20161AE000A88143 /* NSImage+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 4397D2F51D0DE2DF00BB2784 /* NSImage+Compatibility.m */; };
|
||||||
3240BB6523968FA1003BA07D /* SDFileAttributeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 325F7CC523893B2E00AEDFCC /* SDFileAttributeHelper.m */; };
|
3240BB6523968FA1003BA07D /* SDFileAttributeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 325F7CC523893B2E00AEDFCC /* SDFileAttributeHelper.m */; };
|
||||||
|
@ -407,6 +410,8 @@
|
||||||
321E60A11F38E8F600405457 /* SDImageGIFCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageGIFCoder.m; path = Core/SDImageGIFCoder.m; sourceTree = "<group>"; };
|
321E60A11F38E8F600405457 /* SDImageGIFCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageGIFCoder.m; path = Core/SDImageGIFCoder.m; sourceTree = "<group>"; };
|
||||||
321E60BC1F38E91700405457 /* UIImage+ForceDecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+ForceDecode.h"; path = "Core/UIImage+ForceDecode.h"; sourceTree = "<group>"; };
|
321E60BC1F38E91700405457 /* UIImage+ForceDecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+ForceDecode.h"; path = "Core/UIImage+ForceDecode.h"; sourceTree = "<group>"; };
|
||||||
321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+ForceDecode.m"; path = "Core/UIImage+ForceDecode.m"; sourceTree = "<group>"; };
|
321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+ForceDecode.m"; path = "Core/UIImage+ForceDecode.m"; sourceTree = "<group>"; };
|
||||||
|
3237321229F8D0D600D1DA41 /* SDImageFramePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDImageFramePool.h; sourceTree = "<group>"; };
|
||||||
|
3237321329F8D0D600D1DA41 /* SDImageFramePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDImageFramePool.m; sourceTree = "<group>"; };
|
||||||
3240BB6623968FE6003BA07D /* SDAssociatedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAssociatedObject.h; sourceTree = "<group>"; };
|
3240BB6623968FE6003BA07D /* SDAssociatedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAssociatedObject.h; sourceTree = "<group>"; };
|
||||||
3240BB6723968FE6003BA07D /* SDAssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAssociatedObject.m; sourceTree = "<group>"; };
|
3240BB6723968FE6003BA07D /* SDAssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAssociatedObject.m; sourceTree = "<group>"; };
|
||||||
324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageOptionsProcessor.h; path = Core/SDWebImageOptionsProcessor.h; sourceTree = "<group>"; };
|
324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageOptionsProcessor.h; path = Core/SDWebImageOptionsProcessor.h; sourceTree = "<group>"; };
|
||||||
|
@ -691,6 +696,8 @@
|
||||||
325C460122339330004CAE11 /* SDImageAssetManager.m */,
|
325C460122339330004CAE11 /* SDImageAssetManager.m */,
|
||||||
325C460C223394D8004CAE11 /* SDImageCachesManagerOperation.h */,
|
325C460C223394D8004CAE11 /* SDImageCachesManagerOperation.h */,
|
||||||
325C460D223394D8004CAE11 /* SDImageCachesManagerOperation.m */,
|
325C460D223394D8004CAE11 /* SDImageCachesManagerOperation.m */,
|
||||||
|
3237321229F8D0D600D1DA41 /* SDImageFramePool.h */,
|
||||||
|
3237321329F8D0D600D1DA41 /* SDImageFramePool.m */,
|
||||||
32C78E39233371AD00C6B7F8 /* SDImageIOAnimatedCoderInternal.h */,
|
32C78E39233371AD00C6B7F8 /* SDImageIOAnimatedCoderInternal.h */,
|
||||||
3253F235244982D3006C2BE8 /* SDWebImageTransitionInternal.h */,
|
3253F235244982D3006C2BE8 /* SDWebImageTransitionInternal.h */,
|
||||||
325C461E2233A02E004CAE11 /* UIColor+SDHexString.h */,
|
325C461E2233A02E004CAE11 /* UIColor+SDHexString.h */,
|
||||||
|
@ -909,6 +916,7 @@
|
||||||
32D3CDD121DDE87300C4DB49 /* UIImage+MemoryCacheCost.h in Headers */,
|
32D3CDD121DDE87300C4DB49 /* UIImage+MemoryCacheCost.h in Headers */,
|
||||||
328BB6AC2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */,
|
328BB6AC2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */,
|
||||||
325F7CCA238942AB00AEDFCC /* UIImage+ExtendedCacheData.h in Headers */,
|
325F7CCA238942AB00AEDFCC /* UIImage+ExtendedCacheData.h in Headers */,
|
||||||
|
3237321429F8D0D600D1DA41 /* SDImageFramePool.h in Headers */,
|
||||||
325C46272233A0A8004CAE11 /* NSBezierPath+SDRoundedCorners.h in Headers */,
|
325C46272233A0A8004CAE11 /* NSBezierPath+SDRoundedCorners.h in Headers */,
|
||||||
3253F236244982D3006C2BE8 /* SDWebImageTransitionInternal.h in Headers */,
|
3253F236244982D3006C2BE8 /* SDWebImageTransitionInternal.h in Headers */,
|
||||||
321B378F2083290E00C0EA77 /* SDImageLoadersManager.h in Headers */,
|
321B378F2083290E00C0EA77 /* SDImageLoadersManager.h in Headers */,
|
||||||
|
@ -1188,6 +1196,7 @@
|
||||||
32F21B5920788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
|
32F21B5920788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
|
||||||
321B37952083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
|
321B37952083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
|
||||||
4A2CAE361AB4BB7500B6BC39 /* UIImageView+WebCache.m in Sources */,
|
4A2CAE361AB4BB7500B6BC39 /* UIImageView+WebCache.m in Sources */,
|
||||||
|
3237321529F8D0D600D1DA41 /* SDImageFramePool.m in Sources */,
|
||||||
4A2CAE1E1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.m in Sources */,
|
4A2CAE1E1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.m in Sources */,
|
||||||
3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */,
|
3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */,
|
||||||
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */,
|
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */,
|
||||||
|
@ -1262,6 +1271,7 @@
|
||||||
32C0FDE72013426C001B8F2D /* SDWebImageIndicator.m in Sources */,
|
32C0FDE72013426C001B8F2D /* SDWebImageIndicator.m in Sources */,
|
||||||
32B5CC63222F8B70005EB74E /* SDAsyncBlockOperation.m in Sources */,
|
32B5CC63222F8B70005EB74E /* SDAsyncBlockOperation.m in Sources */,
|
||||||
32F21B5720788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
|
32F21B5720788D8C0036B1D5 /* SDWebImageDownloaderRequestModifier.m in Sources */,
|
||||||
|
3237321629F8D0E200D1DA41 /* SDImageFramePool.m in Sources */,
|
||||||
5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */,
|
5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */,
|
||||||
321B37932083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
|
321B37932083290E00C0EA77 /* SDImageLoadersManager.m in Sources */,
|
||||||
32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */,
|
32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */,
|
||||||
|
|
|
@ -10,24 +10,24 @@
|
||||||
#import "NSImage+Compatibility.h"
|
#import "NSImage+Compatibility.h"
|
||||||
#import "SDDisplayLink.h"
|
#import "SDDisplayLink.h"
|
||||||
#import "SDDeviceHelper.h"
|
#import "SDDeviceHelper.h"
|
||||||
|
#import "SDImageFramePool.h"
|
||||||
#import "SDInternalMacros.h"
|
#import "SDInternalMacros.h"
|
||||||
|
|
||||||
@interface SDAnimatedImagePlayer () {
|
@interface SDAnimatedImagePlayer () {
|
||||||
SD_LOCK_DECLARE(_lock);
|
// SD_LOCK_DECLARE(_lock);
|
||||||
NSRunLoopMode _runLoopMode;
|
NSRunLoopMode _runLoopMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property (atomic, strong) SDImageFramePool *framePool;
|
||||||
|
|
||||||
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
|
@property (nonatomic, strong, readwrite) UIImage *currentFrame;
|
||||||
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
|
@property (nonatomic, assign, readwrite) NSUInteger currentFrameIndex;
|
||||||
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
|
@property (nonatomic, assign, readwrite) NSUInteger currentLoopCount;
|
||||||
@property (nonatomic, strong) id<SDAnimatedImageProvider> animatedProvider;
|
@property (nonatomic, strong) id<SDAnimatedImageProvider> animatedProvider;
|
||||||
@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) BOOL needsDisplayWhenImageBecomesAvailable;
|
||||||
@property (nonatomic, assign) BOOL shouldReverse;
|
@property (nonatomic, assign) BOOL shouldReverse;
|
||||||
@property (nonatomic, assign) NSUInteger maxBufferCount;
|
|
||||||
@property (nonatomic, strong) NSOperationQueue *fetchQueue;
|
|
||||||
@property (nonatomic, strong) SDDisplayLink *displayLink;
|
@property (nonatomic, strong) SDDisplayLink *displayLink;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -47,7 +47,8 @@
|
||||||
self.totalLoopCount = provider.animatedImageLoopCount;
|
self.totalLoopCount = provider.animatedImageLoopCount;
|
||||||
self.animatedProvider = provider;
|
self.animatedProvider = provider;
|
||||||
self.playbackRate = 1.0;
|
self.playbackRate = 1.0;
|
||||||
SD_LOCK_INIT(_lock);
|
self.framePool = [SDImageFramePool registerProvider:provider];
|
||||||
|
// SD_LOCK_INIT(_lock);
|
||||||
#if SD_UIKIT
|
#if SD_UIKIT
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||||
#endif
|
#endif
|
||||||
|
@ -69,38 +70,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
|
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
|
||||||
[_fetchQueue cancelAllOperations];
|
[self clearFrameBuffer];
|
||||||
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
// [_fetchQueue cancelAllOperations];
|
||||||
NSNumber *currentFrameIndex = @(self.currentFrameIndex);
|
// NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
||||||
SD_LOCK(self->_lock);
|
// NSNumber *currentFrameIndex = @(self.currentFrameIndex);
|
||||||
NSArray *keys = self.frameBuffer.allKeys;
|
// SD_LOCK(self->_lock);
|
||||||
// only keep the next frame for later rendering
|
// NSArray *keys = self.frameBuffer.allKeys;
|
||||||
for (NSNumber * key in keys) {
|
// // only keep the next frame for later rendering
|
||||||
if (![key isEqualToNumber:currentFrameIndex]) {
|
// for (NSNumber * key in keys) {
|
||||||
[self.frameBuffer removeObjectForKey:key];
|
// if (![key isEqualToNumber:currentFrameIndex]) {
|
||||||
}
|
// [self.frameBuffer removeObjectForKey:key];
|
||||||
}
|
// }
|
||||||
SD_UNLOCK(self->_lock);
|
// }
|
||||||
}];
|
// SD_UNLOCK(self->_lock);
|
||||||
[_fetchQueue addOperation:operation];
|
// }];
|
||||||
|
// [_fetchQueue addOperation:operation];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
- (NSOperationQueue *)fetchQueue {
|
|
||||||
if (!_fetchQueue) {
|
|
||||||
_fetchQueue = [[NSOperationQueue alloc] init];
|
|
||||||
_fetchQueue.maxConcurrentOperationCount = 1;
|
|
||||||
_fetchQueue.name = @"com.hackemist.SDAnimatedImagePlayer.fetchQueue";
|
|
||||||
}
|
|
||||||
return _fetchQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMutableDictionary<NSNumber *,UIImage *> *)frameBuffer {
|
|
||||||
if (!_frameBuffer) {
|
|
||||||
_frameBuffer = [NSMutableDictionary dictionary];
|
|
||||||
}
|
|
||||||
return _frameBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (SDDisplayLink *)displayLink {
|
- (SDDisplayLink *)displayLink {
|
||||||
if (!_displayLink) {
|
if (!_displayLink) {
|
||||||
|
@ -155,9 +142,10 @@
|
||||||
if (posterFrame) {
|
if (posterFrame) {
|
||||||
// HACK: The first frame should not check duration and immediately display
|
// HACK: The first frame should not check duration and immediately display
|
||||||
self.needsDisplayWhenImageBecomesAvailable = YES;
|
self.needsDisplayWhenImageBecomesAvailable = YES;
|
||||||
SD_LOCK(self->_lock);
|
[self.framePool setFrame:posterFrame atIndex:self.currentFrameIndex];
|
||||||
self.frameBuffer[@(self.currentFrameIndex)] = posterFrame;
|
// SD_LOCK(self->_lock);
|
||||||
SD_UNLOCK(self->_lock);
|
// self.frameBuffer[@(self.currentFrameIndex)] = posterFrame;
|
||||||
|
// SD_UNLOCK(self->_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,9 +162,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)clearFrameBuffer {
|
- (void)clearFrameBuffer {
|
||||||
SD_LOCK(_lock);
|
[self.framePool removeAllFrames];
|
||||||
[_frameBuffer removeAllObjects];
|
// SD_LOCK(_lock);
|
||||||
SD_UNLOCK(_lock);
|
// [_frameBuffer removeAllObjects];
|
||||||
|
// SD_UNLOCK(_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Animation Control
|
#pragma mark - Animation Control
|
||||||
|
@ -189,7 +178,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopPlaying {
|
- (void)stopPlaying {
|
||||||
[_fetchQueue cancelAllOperations];
|
// [_fetchQueue cancelAllOperations];
|
||||||
// Using `_displayLink` here because when UIImageView dealloc, it may trigger `[self stopAnimating]`, we already release the display link in SDAnimatedImageView's dealloc method.
|
// Using `_displayLink` here because when UIImageView dealloc, it may trigger `[self stopAnimating]`, we already release the display link in SDAnimatedImageView's dealloc method.
|
||||||
[_displayLink stop];
|
[_displayLink stop];
|
||||||
// We need to reset the frame status, but not trigger any handle. This can ensure next time's playing status correct.
|
// We need to reset the frame status, but not trigger any handle. This can ensure next time's playing status correct.
|
||||||
|
@ -197,7 +186,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pausePlaying {
|
- (void)pausePlaying {
|
||||||
[_fetchQueue cancelAllOperations];
|
// [_fetchQueue cancelAllOperations];
|
||||||
[_displayLink stop];
|
[_displayLink stop];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,23 +250,26 @@
|
||||||
// Check if we need to display new frame firstly
|
// Check if we need to display new frame firstly
|
||||||
BOOL bufferFull = NO;
|
BOOL bufferFull = NO;
|
||||||
if (self.needsDisplayWhenImageBecomesAvailable) {
|
if (self.needsDisplayWhenImageBecomesAvailable) {
|
||||||
UIImage *currentFrame;
|
UIImage *currentFrame = [self.framePool frameAtIndex:currentFrameIndex];
|
||||||
SD_LOCK(_lock);
|
// SD_LOCK(_lock);
|
||||||
currentFrame = self.frameBuffer[@(currentFrameIndex)];
|
// currentFrame = self.frameBuffer[@(currentFrameIndex)];
|
||||||
SD_UNLOCK(_lock);
|
// SD_UNLOCK(_lock);
|
||||||
|
|
||||||
// Update the current frame
|
// Update the current frame
|
||||||
if (currentFrame) {
|
if (currentFrame) {
|
||||||
SD_LOCK(_lock);
|
if (self.framePool.currentFrameCount == totalFrameCount) {
|
||||||
// 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;
|
bufferFull = YES;
|
||||||
}
|
}
|
||||||
SD_UNLOCK(_lock);
|
// SD_LOCK(_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(_lock);
|
||||||
|
|
||||||
// Update the current frame immediately
|
// Update the current frame immediately
|
||||||
self.currentFrame = currentFrame;
|
self.currentFrame = currentFrame;
|
||||||
|
@ -351,31 +343,35 @@
|
||||||
UIImage *fetchFrame = nil;
|
UIImage *fetchFrame = nil;
|
||||||
if (!self.bufferMiss) {
|
if (!self.bufferMiss) {
|
||||||
fetchFrameIndex = nextIndex;
|
fetchFrameIndex = nextIndex;
|
||||||
SD_LOCK(_lock);
|
fetchFrame = [self.framePool frameAtIndex:nextIndex];
|
||||||
fetchFrame = self.frameBuffer[@(nextIndex)];
|
// SD_LOCK(_lock);
|
||||||
SD_UNLOCK(_lock);
|
// fetchFrame = self.frameBuffer[@(nextIndex)];
|
||||||
|
// SD_UNLOCK(_lock);
|
||||||
}
|
}
|
||||||
if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) {
|
if (!fetchFrame && !bufferFull) {
|
||||||
// Prefetch next frame in background queue
|
[self.framePool prefetchFrameAtIndex:fetchFrameIndex];
|
||||||
id<SDAnimatedImageProvider> animatedProvider = self.animatedProvider;
|
|
||||||
@weakify(self);
|
|
||||||
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
|
||||||
@strongify(self);
|
|
||||||
if (!self) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UIImage *frame = [animatedProvider animatedImageFrameAtIndex:fetchFrameIndex];
|
|
||||||
|
|
||||||
BOOL isAnimating = self.displayLink.isRunning;
|
|
||||||
if (isAnimating) {
|
|
||||||
SD_LOCK(self->_lock);
|
|
||||||
self.frameBuffer[@(fetchFrameIndex)] = frame;
|
|
||||||
SD_UNLOCK(self->_lock);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
[self.fetchQueue addOperation:operation];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// if (!fetchFrame && !bufferFull && self.fetchQueue.operationCount == 0) {
|
||||||
|
// // Prefetch next frame in background queue
|
||||||
|
// id<SDAnimatedImageProvider> animatedProvider = self.animatedProvider;
|
||||||
|
// @weakify(self);
|
||||||
|
// NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
||||||
|
// @strongify(self);
|
||||||
|
// if (!self) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// UIImage *frame = [animatedProvider animatedImageFrameAtIndex:fetchFrameIndex];
|
||||||
|
//
|
||||||
|
// BOOL isAnimating = self.displayLink.isRunning;
|
||||||
|
// if (isAnimating) {
|
||||||
|
// SD_LOCK(self->_lock);
|
||||||
|
// self.frameBuffer[@(fetchFrameIndex)] = frame;
|
||||||
|
// SD_UNLOCK(self->_lock);
|
||||||
|
// }
|
||||||
|
// }];
|
||||||
|
// [self.fetchQueue addOperation:operation];
|
||||||
|
// }
|
||||||
|
|
||||||
- (void)handleFrameChange {
|
- (void)handleFrameChange {
|
||||||
if (self.animationFrameHandler) {
|
if (self.animationFrameHandler) {
|
||||||
|
@ -410,7 +406,7 @@
|
||||||
maxBufferCount = 1;
|
maxBufferCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.maxBufferCount = maxBufferCount;
|
self.framePool.maxBufferCount = maxBufferCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSString *)defaultRunLoopMode {
|
+ (NSString *)defaultRunLoopMode {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the SDWebImage package.
|
||||||
|
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "SDWebImageCompat.h"
|
||||||
|
#import "SDImageCoder.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// A per-provider (provider means, AnimatedImage object) based frame pool, each player who use the same provider share the same frame buffer
|
||||||
|
@interface SDImageFramePool : NSObject
|
||||||
|
|
||||||
|
/// Register and return back a frame pool, also increase reference count
|
||||||
|
+ (instancetype)registerProvider:(id<SDAnimatedImageProvider>)provider;
|
||||||
|
/// Unregister a frame pool, also decrease reference count, if zero dealloc the frame pool
|
||||||
|
+ (void)unregisterProvider:(id<SDAnimatedImageProvider>)provider;
|
||||||
|
|
||||||
|
/// Prefetch the current frame, query using `frameAtIndex:` by caller to check whether finished.
|
||||||
|
- (void)prefetchFrameAtIndex:(NSUInteger)index;
|
||||||
|
|
||||||
|
/// Control the max buffer count for current frame pool, used for RAM/CPU balance, default unlimited
|
||||||
|
@property (nonatomic, assign) NSUInteger maxBufferCount;
|
||||||
|
/// Control the max concurrent fetch queue operation count, used for CPU balance, default 1
|
||||||
|
@property (nonatomic, assign) NSUInteger maxConcurrentCount;
|
||||||
|
|
||||||
|
// Frame Operations
|
||||||
|
@property (nonatomic, readonly) NSUInteger currentFrameCount;
|
||||||
|
- (nullable UIImage *)frameAtIndex:(NSUInteger)index;
|
||||||
|
- (void)setFrame:(nullable UIImage *)frame atIndex:(NSUInteger)index;
|
||||||
|
- (void)removeFrameAtIndex:(NSUInteger)index;
|
||||||
|
- (void)removeAllFrames;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the SDWebImage package.
|
||||||
|
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "SDImageFramePool.h"
|
||||||
|
#import "SDInternalMacros.h"
|
||||||
|
#import "objc/runtime.h"
|
||||||
|
|
||||||
|
@interface SDImageFramePool ()
|
||||||
|
|
||||||
|
@property (class, readonly) NSMapTable *providerFramePoolMap;
|
||||||
|
|
||||||
|
@property (weak) id<SDAnimatedImageProvider> provider;
|
||||||
|
@property (atomic) NSUInteger registerCount;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIImage *> *frameBuffer;
|
||||||
|
@property (nonatomic, strong) NSOperationQueue *fetchQueue;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDImageFramePool
|
||||||
|
|
||||||
|
+ (NSMapTable *)providerFramePoolMap {
|
||||||
|
static NSMapTable *providerFramePoolMap;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
providerFramePoolMap = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality valueOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality];
|
||||||
|
});
|
||||||
|
return providerFramePoolMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_frameBuffer = [NSMutableDictionary dictionary];
|
||||||
|
_fetchQueue = [[NSOperationQueue alloc] init];
|
||||||
|
_fetchQueue.maxConcurrentOperationCount = 1;
|
||||||
|
_fetchQueue.name = @"com.hackemist.SDImageFramePool.fetchQueue";
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (instancetype)registerProvider:(id<SDAnimatedImageProvider>)provider {
|
||||||
|
SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
|
||||||
|
if (!framePool) {
|
||||||
|
framePool = [[SDImageFramePool alloc] init];
|
||||||
|
framePool.provider = provider;
|
||||||
|
[self.providerFramePoolMap setObject:framePool forKey:provider];
|
||||||
|
}
|
||||||
|
framePool.registerCount += 1;
|
||||||
|
return framePool;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)unregisterProvider:(id<SDAnimatedImageProvider>)provider {
|
||||||
|
SDImageFramePool *framePool = [self.providerFramePoolMap objectForKey:provider];
|
||||||
|
if (!framePool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
framePool.registerCount -= 1;
|
||||||
|
if (framePool.registerCount == 0) {
|
||||||
|
[self.providerFramePoolMap removeObjectForKey:provider];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prefetchFrameAtIndex:(NSUInteger)index {
|
||||||
|
@synchronized (self) {
|
||||||
|
NSUInteger frameCount = self.frameBuffer.count;
|
||||||
|
if (frameCount > self.maxBufferCount) {
|
||||||
|
// Remove the frame buffer if need
|
||||||
|
self.frameBuffer[@(index)] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.fetchQueue.operationCount == 0) {
|
||||||
|
// Prefetch next frame in background queue
|
||||||
|
id<SDAnimatedImageProvider> animatedProvider = self.provider;
|
||||||
|
@weakify(self);
|
||||||
|
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
|
||||||
|
@strongify(self);
|
||||||
|
if (!self) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UIImage *frame = [animatedProvider animatedImageFrameAtIndex:index];
|
||||||
|
|
||||||
|
[self setFrame:frame atIndex:index];
|
||||||
|
}];
|
||||||
|
[self.fetchQueue addOperation:operation];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMaxConcurrentCount:(NSUInteger)maxConcurrentCount {
|
||||||
|
self.fetchQueue.maxConcurrentOperationCount = maxConcurrentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)currentFrameCount {
|
||||||
|
NSUInteger frameCount = 0;
|
||||||
|
@synchronized (self) {
|
||||||
|
frameCount = self.frameBuffer.count;
|
||||||
|
}
|
||||||
|
return frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFrame:(UIImage *)frame atIndex:(NSUInteger)index {
|
||||||
|
@synchronized (self) {
|
||||||
|
self.frameBuffer[@(index)] = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)frameAtIndex:(NSUInteger)index {
|
||||||
|
UIImage *frame;
|
||||||
|
@synchronized (self) {
|
||||||
|
frame = self.frameBuffer[@(index)];
|
||||||
|
}
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeFrameAtIndex:(NSUInteger)index {
|
||||||
|
@synchronized (self) {
|
||||||
|
self.frameBuffer[@(index)] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeAllFrames {
|
||||||
|
@synchronized (self) {
|
||||||
|
[self.frameBuffer removeAllObjects];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMutableDictionary<NSNumber *,UIImage *> *)frameBuffer {
|
||||||
|
if (!_frameBuffer) {
|
||||||
|
_frameBuffer = [NSMutableDictionary dictionary];
|
||||||
|
}
|
||||||
|
return _frameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Loading…
Reference in New Issue