mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-18 11:30:04 +00:00
278 lines
7.6 KiB
Objective-C
278 lines
7.6 KiB
Objective-C
#import "PGPhotoFilterThumbnailManager.h"
|
|
|
|
#import <LegacyComponents/TGMemoryImageCache.h>
|
|
#import <pthread.h>
|
|
|
|
#import <SSignalKit/SSignalKit.h>
|
|
|
|
#import "PGPhotoEditor.h"
|
|
#import "PGPhotoFilter.h"
|
|
#import "PGPhotoFilterDefinition.h"
|
|
#import "PGPhotoProcessPass.h"
|
|
#import "PGPhotoEditorPicture.h"
|
|
|
|
const NSUInteger TGFilterThumbnailCacheSoftMemoryLimit = 2 * 1024 * 1024;
|
|
const NSUInteger TGFilterThumbnailCacheHardMemoryLimit = 2 * 1024 * 1024;
|
|
|
|
@interface PGPhotoFilterThumbnailManager ()
|
|
{
|
|
TGMemoryImageCache *_filterThumbnailCache;
|
|
SQueue *_cachingQueue;
|
|
|
|
UIImage *_thumbnailImage;
|
|
PGPhotoEditorPicture *_thumbnailPicture;
|
|
|
|
SQueue *_filteringQueue;
|
|
dispatch_queue_t _prepQueue;
|
|
|
|
pthread_rwlock_t _callbackLock;
|
|
NSMutableDictionary *_callbacksForId;
|
|
|
|
NSInteger _version;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation PGPhotoFilterThumbnailManager
|
|
|
|
- (instancetype)init
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
[self invalidateThumbnailImages];
|
|
|
|
_prepQueue = dispatch_queue_create("ph.pictogra.Pictograph.FilterThumbnailQueue", DISPATCH_QUEUE_CONCURRENT);
|
|
|
|
_cachingQueue = [[SQueue alloc] init];
|
|
_callbacksForId = [[NSMutableDictionary alloc] init];
|
|
|
|
_filteringQueue = [[SQueue alloc] init];
|
|
pthread_rwlock_init(&_callbackLock, NULL);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)setThumbnailImage:(UIImage *)image
|
|
{
|
|
[self invalidateThumbnailImages];
|
|
_thumbnailImage = image;
|
|
|
|
//_thumbnailPicture = [[PGPhotoEditorPicture alloc] initWithImage:_thumbnailImage];
|
|
}
|
|
|
|
- (void)requestThumbnailImageForFilter:(PGPhotoFilter *)filter completion:(void (^)(UIImage *image, bool cached, bool finished))completion
|
|
{
|
|
if (filter.definition.type == PGPhotoFilterTypePassThrough)
|
|
{
|
|
if (completion != nil)
|
|
{
|
|
if (_thumbnailImage != nil)
|
|
completion(_thumbnailImage, true, true);
|
|
else
|
|
completion(nil, true, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
UIImage *cachedImage = [_filterThumbnailCache imageForKey:filter.identifier attributes:nil];
|
|
if (cachedImage != nil)
|
|
{
|
|
if (completion != nil)
|
|
completion(cachedImage, true, true);
|
|
return;
|
|
}
|
|
|
|
if (_thumbnailImage == nil)
|
|
{
|
|
completion(nil, true, true);
|
|
return;
|
|
}
|
|
|
|
if (completion != nil)
|
|
completion(_thumbnailImage, true, false);
|
|
|
|
NSInteger version = _version;
|
|
|
|
__weak PGPhotoFilterThumbnailManager *weakSelf = self;
|
|
[self _addCallback:completion forId:filter.identifier createCallback:^
|
|
{
|
|
__strong PGPhotoFilterThumbnailManager *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
if (version != strongSelf->_version)
|
|
return;
|
|
|
|
[strongSelf renderFilterThumbnailWithPicture:strongSelf->_thumbnailPicture filter:filter completion:^(UIImage *result)
|
|
{
|
|
[strongSelf _processCompletionForId:filter.identifier withResult:result];
|
|
}];
|
|
}];
|
|
}
|
|
|
|
- (void)startCachingThumbnailImagesForFilters:(NSArray *)filters
|
|
{
|
|
if (_thumbnailImage == nil)
|
|
return;
|
|
|
|
NSMutableArray *filtersToStartCaching = [[NSMutableArray alloc] init];
|
|
|
|
for (PGPhotoFilter *filter in filters)
|
|
{
|
|
if (filter.definition.type != PGPhotoFilterTypePassThrough && [_filterThumbnailCache imageForKey:filter.identifier attributes:nil] == nil)
|
|
[filtersToStartCaching addObject:filter];
|
|
}
|
|
|
|
NSInteger version = _version;
|
|
|
|
[_cachingQueue dispatch:^
|
|
{
|
|
if (version != _version)
|
|
return;
|
|
|
|
for (PGPhotoFilter *filter in filtersToStartCaching)
|
|
{
|
|
__weak PGPhotoFilterThumbnailManager *weakSelf = self;
|
|
[self _addCallback:nil forId:filter.identifier createCallback:^
|
|
{
|
|
__strong PGPhotoFilterThumbnailManager *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
if (version != strongSelf->_version)
|
|
return;
|
|
|
|
[strongSelf renderFilterThumbnailWithPicture:strongSelf->_thumbnailPicture filter:filter completion:^(UIImage *result)
|
|
{
|
|
[strongSelf _processCompletionForId:filter.identifier withResult:result];
|
|
}];
|
|
}];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)stopCachingThumbnailImagesForFilters:(NSArray *)__unused filters
|
|
{
|
|
|
|
}
|
|
|
|
- (void)stopCachingThumbnailImagesForAllFilters
|
|
{
|
|
|
|
}
|
|
|
|
- (void)_processCompletionForId:(NSString *)filterId withResult:(UIImage *)result
|
|
{
|
|
[_filterThumbnailCache setImage:result forKey:filterId attributes:nil];
|
|
|
|
NSArray *callbacks = [self _callbacksForId:filterId];
|
|
[self _removeCallbacksForId:filterId];
|
|
|
|
for (id callback in callbacks)
|
|
{
|
|
void(^callbackBlock)(UIImage *image, bool cached, bool finished) = callback;
|
|
if (callbackBlock != nil)
|
|
callbackBlock(result, false, true);
|
|
}
|
|
}
|
|
|
|
- (void)renderFilterThumbnailWithPicture:(PGPhotoEditorPicture *)picture filter:(PGPhotoFilter *)filter completion:(void (^)(UIImage *result))completion
|
|
{
|
|
PGPhotoEditor *photoEditor = self.photoEditor;
|
|
if (photoEditor == nil)
|
|
return;
|
|
|
|
NSInteger version = _version;
|
|
dispatch_async(_prepQueue, ^
|
|
{
|
|
GPUImageOutput<GPUImageInput> *gpuFilter = filter.optimizedPass.filter;
|
|
[_filteringQueue dispatch:^
|
|
{
|
|
if (version != _version)
|
|
return;
|
|
|
|
[picture addTarget:gpuFilter];
|
|
[gpuFilter useNextFrameForImageCapture];
|
|
[picture processSynchronous:true completion:^
|
|
{
|
|
UIImage *image = [gpuFilter imageFromCurrentFramebufferWithOrientation:UIImageOrientationUp];
|
|
[picture removeAllTargets];
|
|
|
|
if (completion != nil)
|
|
completion(image);
|
|
}];
|
|
}];
|
|
});
|
|
}
|
|
|
|
- (void)invalidateThumbnailImages
|
|
{
|
|
_version = lrand48();
|
|
|
|
_filterThumbnailCache = [[TGMemoryImageCache alloc] initWithSoftMemoryLimit:TGFilterThumbnailCacheSoftMemoryLimit
|
|
hardMemoryLimit:TGFilterThumbnailCacheHardMemoryLimit];
|
|
}
|
|
|
|
- (void)haltCaching
|
|
{
|
|
_version = lrand48();
|
|
}
|
|
|
|
- (void)_addCallback:(void (^)(UIImage *, bool, bool))callback forId:(NSString *)filterId createCallback:(void (^)(void))createCallback
|
|
{
|
|
if (filterId == nil)
|
|
{
|
|
callback(nil, true, false);
|
|
return;
|
|
}
|
|
|
|
pthread_rwlock_rdlock(&_callbackLock);
|
|
|
|
bool isInitial = false;
|
|
if (_callbacksForId[filterId] == nil)
|
|
{
|
|
isInitial = true;
|
|
_callbacksForId[filterId] = [[NSMutableArray alloc] init];
|
|
}
|
|
|
|
if (callback != nil)
|
|
{
|
|
NSMutableArray *callbacksForId = _callbacksForId[filterId];
|
|
[callbacksForId addObject:callback];
|
|
_callbacksForId[filterId] = callbacksForId;
|
|
}
|
|
|
|
if (isInitial && createCallback != nil)
|
|
createCallback();
|
|
|
|
pthread_rwlock_unlock(&_callbackLock);
|
|
}
|
|
|
|
- (NSArray *)_callbacksForId:(NSString *)filterId
|
|
{
|
|
if (filterId == nil)
|
|
return nil;
|
|
|
|
__block NSArray *callbacksForId;
|
|
|
|
pthread_rwlock_rdlock(&_callbackLock);
|
|
callbacksForId = _callbacksForId[filterId];
|
|
pthread_rwlock_unlock(&_callbackLock);
|
|
|
|
return [callbacksForId copy];
|
|
}
|
|
|
|
- (void)_removeCallbacksForId:(NSString *)filterId
|
|
{
|
|
if (filterId == nil)
|
|
return;
|
|
|
|
pthread_rwlock_rdlock(&_callbackLock);
|
|
[_callbacksForId removeObjectForKey:filterId];
|
|
pthread_rwlock_unlock(&_callbackLock);
|
|
}
|
|
|
|
@end
|