mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 09:29:55 +01:00
554 lines
16 KiB
Objective-C
554 lines
16 KiB
Objective-C
#import "TGRemoteImageView.h"
|
|
|
|
#import <QuartzCore/QuartzCore.h>
|
|
|
|
#import "TGCache.h"
|
|
|
|
#import "SGraphObjectNode.h"
|
|
|
|
#import "TGImageManager.h"
|
|
|
|
static TGCache *sharedCache = nil;
|
|
|
|
@interface TGRemoteImageView ()
|
|
|
|
@property (atomic, strong) NSString *path;
|
|
|
|
@property (nonatomic, strong) UIImageView *placeholderView;
|
|
|
|
@end
|
|
|
|
@implementation TGRemoteImageView
|
|
|
|
+ (NSMutableDictionary *)imageProcessors
|
|
{
|
|
static NSMutableDictionary *dictionary = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
dictionary = [[NSMutableDictionary alloc] init];
|
|
});
|
|
return dictionary;
|
|
}
|
|
|
|
+ (NSMutableDictionary *)universalImageProcessors
|
|
{
|
|
static NSMutableDictionary *dictionary = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
dictionary = [[NSMutableDictionary alloc] init];
|
|
});
|
|
return dictionary;
|
|
}
|
|
|
|
+ (void)throttleDownProcessing
|
|
{
|
|
|
|
}
|
|
|
|
+ (void)registerImageUniversalProcessor:(TGImageUniversalProcessor)universalProcessor withBaseName:(NSString *)baseName
|
|
{
|
|
[[TGRemoteImageView universalImageProcessors] setObject:[universalProcessor copy] forKey:baseName];
|
|
}
|
|
|
|
+ (void)registerImageProcessor:(TGImageProcessor)imageProcessor withName:(NSString *)name
|
|
{
|
|
[[TGRemoteImageView imageProcessors] setObject:[imageProcessor copy] forKey:name];
|
|
}
|
|
|
|
+ (TGImageProcessor)imageProcessorForName:(NSString *)name
|
|
{
|
|
TGImageProcessor processor = [[TGRemoteImageView imageProcessors] objectForKey:name];
|
|
if (processor != nil)
|
|
return processor;
|
|
|
|
NSRange range = [name rangeOfString:@":"];
|
|
if (range.location != NSNotFound)
|
|
{
|
|
NSString *baseName = [name substringToIndex:range.location];
|
|
TGImageUniversalProcessor universalProcessor = [[TGRemoteImageView universalImageProcessors] objectForKey:baseName];
|
|
if (universalProcessor != nil)
|
|
{
|
|
return ^UIImage *(UIImage *source)
|
|
{
|
|
return universalProcessor(name, source);
|
|
};
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
+ (void)setSharedCache:(TGCache *)cache
|
|
{
|
|
sharedCache = cache;
|
|
}
|
|
|
|
+ (TGCache *)sharedCache
|
|
{
|
|
return sharedCache;
|
|
}
|
|
|
|
#pragma mark - Implementation
|
|
|
|
- (id)initWithFrame:(CGRect)frame
|
|
{
|
|
self = [super initWithFrame:frame];
|
|
if (self)
|
|
{
|
|
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
|
|
_fadeTransitionDuration = 0.14;
|
|
_useCache = true;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_actionHandle reset];
|
|
[self cancelLoading];
|
|
}
|
|
|
|
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
|
{
|
|
self.userInteractionEnabled = true;
|
|
|
|
[super addGestureRecognizer:gestureRecognizer];
|
|
}
|
|
|
|
- (void)setFadeTransition:(bool)fadeTransition
|
|
{
|
|
if (fadeTransition != _fadeTransition)
|
|
{
|
|
if (fadeTransition && _placeholderView == nil)
|
|
{
|
|
_placeholderView = [[UIImageView alloc] init];
|
|
_placeholderView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
|
|
_placeholderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
[_placeholderView setContentMode:self.contentMode];
|
|
[self addSubview:_placeholderView];
|
|
}
|
|
else if (!fadeTransition && _placeholderView != nil)
|
|
{
|
|
[_placeholderView removeFromSuperview];
|
|
_placeholderView = nil;
|
|
}
|
|
|
|
_fadeTransition = fadeTransition;
|
|
}
|
|
}
|
|
|
|
- (void)setContentMode:(UIViewContentMode)contentMode
|
|
{
|
|
if (_placeholderView != nil)
|
|
[_placeholderView setContentMode:contentMode];
|
|
|
|
[super setContentMode:contentMode];
|
|
}
|
|
|
|
- (void)setPlaceholderOverlay:(UIView *)placeholderOverlay
|
|
{
|
|
if (_placeholderOverlay != nil)
|
|
{
|
|
[_placeholderOverlay removeFromSuperview];
|
|
_placeholderOverlay = nil;
|
|
}
|
|
|
|
_placeholderOverlay = placeholderOverlay;
|
|
[_placeholderView addSubview:placeholderOverlay];
|
|
}
|
|
|
|
- (void)prepareForRecycle
|
|
{
|
|
[self cancelLoading];
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = nil;
|
|
#else
|
|
self.image = nil;
|
|
#endif
|
|
|
|
if (_placeholderOverlay != nil)
|
|
{
|
|
[_placeholderOverlay removeFromSuperview];
|
|
_placeholderOverlay = nil;
|
|
}
|
|
}
|
|
|
|
- (void)prepareForReuse
|
|
{
|
|
[self cancelLoading];
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = nil;
|
|
#else
|
|
self.image = nil;
|
|
#endif
|
|
}
|
|
|
|
- (UIImage *)currentImage
|
|
{
|
|
#if TGRemoteImageUseContents
|
|
if (self.layer.contents != nil)
|
|
return [UIImage imageWithCGImage:(CGImageRef)self.layer.contents];
|
|
#else
|
|
return self.image;
|
|
#endif
|
|
return nil;
|
|
}
|
|
|
|
- (UIImage *)currentPlaceholderImage
|
|
{
|
|
return _placeholderView.image;
|
|
}
|
|
|
|
- (void)tryFillCache:(NSMutableDictionary *)dict
|
|
{
|
|
if (_currentUrl == nil)
|
|
return;
|
|
|
|
UIImage *currentImage = [self currentImage];
|
|
if (currentImage != nil)
|
|
{
|
|
NSString *key = _currentFilter == nil ? _currentUrl : [[NSString alloc] initWithFormat:@"{filter:%@}%@", _currentFilter, _currentUrl];
|
|
|
|
if (key != nil)
|
|
[dict setObject:currentImage forKey:key];
|
|
}
|
|
}
|
|
|
|
- (void)loadImage:(UIImage *)image
|
|
{
|
|
[self cancelLoading];
|
|
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = (id)(image.CGImage);
|
|
#else
|
|
self.image = image;
|
|
#endif
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
[_placeholderView.layer removeAllAnimations];
|
|
_placeholderView.image = nil;
|
|
_placeholderView.hidden = true;
|
|
_placeholderView.alpha = 0.0f;
|
|
}
|
|
}
|
|
|
|
- (void)loadImage:(NSString *)url filter:(NSString *)filter placeholder:(UIImage *)placeholder
|
|
{
|
|
[self loadImage:url filter:filter placeholder:placeholder forceFade:false];
|
|
}
|
|
|
|
- (void)loadImage:(NSString *)url filter:(NSString *)filter placeholder:(UIImage *)placeholder forceFade:(bool)forceFade
|
|
{
|
|
[self cancelLoading];
|
|
|
|
self.currentUrl = url;
|
|
self.currentFilter = filter;
|
|
|
|
TGCache *cache = _cache != nil ? _cache : [TGRemoteImageView sharedCache];
|
|
|
|
NSString *cacheUrl = filter == nil ? url : [[NSString alloc] initWithFormat:@"{filter:%@}%@", filter, url];
|
|
|
|
UIImage *image = [cache cachedImage:cacheUrl availability:TGCacheMemory];
|
|
|
|
if (image == nil)
|
|
image = [[TGImageManager instance] loadImageSyncWithUri:url canWait:false decode:true acceptPartialData:false asyncTaskId:NULL progress:nil partialCompletion:nil completion:nil];
|
|
|
|
if (image == nil && (_contentHints & TGRemoteImageContentHintLoadFromDiskSynchronously))
|
|
{
|
|
UIImage *managerImage = [[TGImageManager instance] loadImageSyncWithUri:url canWait:true decode:filter == nil acceptPartialData:false asyncTaskId:NULL progress:nil partialCompletion:nil completion:nil];
|
|
if (managerImage == nil)
|
|
managerImage = [cache cachedImage:url availability:TGCacheDisk];
|
|
|
|
if (managerImage != nil)
|
|
{
|
|
if (filter != nil)
|
|
{
|
|
TGImageProcessor procesor = [TGRemoteImageView imageProcessorForName:filter];
|
|
if (procesor != nil)
|
|
image = procesor(managerImage);
|
|
}
|
|
else
|
|
image = managerImage;
|
|
}
|
|
}
|
|
|
|
if (image != nil)
|
|
{
|
|
if (_contentHints & TGRemoteImageContentHintSaveToGallery)
|
|
{
|
|
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/tg/checkImageStored/(%lu)", (unsigned long)[url hash]] options:[[NSDictionary alloc] initWithObjectsAndKeys:url, @"url", nil] watcher:self];
|
|
}
|
|
|
|
if (forceFade)
|
|
{
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = (id)(image.CGImage);
|
|
#else
|
|
self.image = image;
|
|
#endif
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
[_placeholderView.layer removeAllAnimations];
|
|
UIView *placeholderView = _placeholderView;
|
|
_placeholderView.alpha = 1.0f;
|
|
_placeholderView.hidden = false;
|
|
if (placeholder != nil)
|
|
_placeholderView.image = placeholder;
|
|
[UIView animateWithDuration:_fadeTransitionDuration animations:^{
|
|
placeholderView.alpha = 0.0f;
|
|
} completion:^(BOOL finished)
|
|
{
|
|
if (finished)
|
|
placeholderView.hidden = true;
|
|
}];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = (id)(image.CGImage);
|
|
#else
|
|
self.image = image;
|
|
#endif
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
[_placeholderView.layer removeAllAnimations];
|
|
_placeholderView.image = nil;
|
|
_placeholderView.hidden = true;
|
|
_placeholderView.alpha = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (_progressHandler)
|
|
_progressHandler(self, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
if (_allowThumbnailCache)
|
|
{
|
|
UIImage *thumbnail = [cache cachedThumbnail:cacheUrl];
|
|
if (thumbnail != nil)
|
|
placeholder = thumbnail;
|
|
}
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = nil;
|
|
#else
|
|
self.image = nil;
|
|
#endif
|
|
[_placeholderView.layer removeAllAnimations];
|
|
_placeholderView.image = placeholder;
|
|
_placeholderView.hidden = false;
|
|
_placeholderView.alpha = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = (id)(placeholder.CGImage);
|
|
#else
|
|
self.image = placeholder;
|
|
#endif
|
|
}
|
|
|
|
if (filter != nil)
|
|
self.path = [NSString stringWithFormat:@"/img/({filter:%@}%@)", filter, url];
|
|
else
|
|
self.path = [NSString stringWithFormat:@"/img/(%@)", url];
|
|
|
|
NSMutableDictionary *options = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:_cancelTimeout], @"cancelTimeout", cache, @"cache", [NSNumber numberWithBool:_useCache], @"useCache", [NSNumber numberWithBool:_allowThumbnailCache], @"allowThumbnailCache", [[NSNumber alloc] initWithInt:_contentHints], @"contentHints", nil];
|
|
if (_userProperties != nil)
|
|
[options setObject:_userProperties forKey:@"userProperties"];
|
|
if (_contentHints & TGRemoteImageContentHintBlurRemote)
|
|
options[@"blurIfRemote"] = @(true);
|
|
[ActionStageInstance() requestActor:self.path options:options watcher:self];
|
|
}
|
|
}
|
|
|
|
- (void)loadPlaceholder:(UIImage *)placeholder
|
|
{
|
|
if (!_placeholderView.hidden)
|
|
_placeholderView.image = placeholder;
|
|
}
|
|
|
|
- (void)cancelLoading
|
|
{
|
|
if (self.path != nil)
|
|
{
|
|
ASHandle *actionHandle = _actionHandle;
|
|
NSString *path = self.path;
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
[ActionStageInstance() removeWatcherByHandle:actionHandle fromPath:path];
|
|
}];
|
|
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = nil;
|
|
#else
|
|
self.image = nil;
|
|
#endif
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
[_placeholderView.layer removeAllAnimations];
|
|
}
|
|
|
|
self.path = nil;
|
|
}
|
|
|
|
self.currentUrl = nil;
|
|
self.currentFilter = nil;
|
|
}
|
|
|
|
+ (UIImage *)imageFromCache:(NSString *)url filter:(NSString *)filter cache:(TGCache *)cache
|
|
{
|
|
TGCache *usingCache = cache != nil ? cache : [TGRemoteImageView sharedCache];
|
|
|
|
UIImage *image = nil;
|
|
if (filter == nil)
|
|
image = [usingCache cachedImage:url availability:TGCacheMemory];
|
|
else
|
|
image = [usingCache cachedImage:[[NSString alloc] initWithFormat:@"{filter:%@}%@", filter, url] availability:TGCacheMemory];
|
|
|
|
return image;
|
|
}
|
|
|
|
+ (NSString *)preloadImage:(NSString *)url filter:(NSString *)filter blurIfRemote:(bool)blurIfRemote cache:(TGCache *)cache allowThumbnailCache:(bool)allowThumbnailCache watcher:(id<ASWatcher>)watcher
|
|
{
|
|
TGCache *usingCache = cache != nil ? cache : [TGRemoteImageView sharedCache];
|
|
|
|
UIImage *image = nil;
|
|
if (filter == nil)
|
|
image = [usingCache cachedImage:url availability:TGCacheMemory];
|
|
else
|
|
image = [usingCache cachedImage:[[NSString alloc] initWithFormat:@"{filter:%@}%@", filter, url] availability:TGCacheMemory];
|
|
|
|
if (image == nil)
|
|
{
|
|
NSString *path = nil;
|
|
if (filter != nil)
|
|
path = [NSString stringWithFormat:@"/img/({filter:%@}%@)", filter, url];
|
|
else
|
|
path = [NSString stringWithFormat:@"/img/(%@)", url];
|
|
|
|
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], @"cancelTimeout", usingCache, @"cache", [NSNumber numberWithBool:allowThumbnailCache], @"forceMemoryCache", @(TG_CACHE_INPLACE), @"allowThumbnailCache", @(blurIfRemote), @"blurIfRemote", nil];
|
|
[ActionStageInstance() requestActor:path options:options watcher:watcher];
|
|
|
|
return path;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (void)actorMessageReceived:(NSString *)path messageType:(NSString *)messageType message:(id)message
|
|
{
|
|
if ([messageType isEqualToString:@"progress"])
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
if (_progressHandler == nil)
|
|
return;
|
|
|
|
if (self.path != nil && [path isEqualToString:self.path])
|
|
{
|
|
if (_progressHandler)
|
|
_progressHandler(self, [message floatValue]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)actorReportedProgress:(NSString *)path progress:(float)progress
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
if (_progressHandler == nil)
|
|
return;
|
|
|
|
if (self.path != nil && [path isEqualToString:self.path])
|
|
{
|
|
if (_progressHandler)
|
|
_progressHandler(self, progress);
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)actorCompleted:(int)resultCode path:(NSString *)path result:(id)result
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
{
|
|
if (self.path != nil && [path isEqualToString:self.path])
|
|
{
|
|
if (resultCode == ASStatusSuccess && result != nil)
|
|
{
|
|
if (_contentHints & TGRemoteImageContentHintSaveToGallery)
|
|
{
|
|
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/tg/checkImageStored/(%lu)", (unsigned long)[self.currentUrl hash]] options:[[NSDictionary alloc] initWithObjectsAndKeys:self.currentUrl, @"url", nil] watcher:self];
|
|
}
|
|
|
|
UIImage *image = ((SGraphObjectNode *)result).object;
|
|
if (image != nil)
|
|
{
|
|
#if TG_CACHE_INPLACE
|
|
if (_useCache)
|
|
{
|
|
TGCache *cache = _cache != nil ? _cache : [TGRemoteImageView sharedCache];
|
|
[cache cacheImage:image withData:nil url:self.currentUrl availability:TGCacheMemory];
|
|
}
|
|
#endif
|
|
|
|
#if TGRemoteImageUseContents
|
|
self.layer.contents = (id)(image.CGImage);
|
|
#else
|
|
self.image = image;
|
|
#endif
|
|
|
|
if (_placeholderView != nil)
|
|
{
|
|
//[_placeholderView.layer removeAllAnimations];
|
|
if (_fadeTransitionDuration < FLT_EPSILON)
|
|
{
|
|
_placeholderView.alpha = 0.0f;
|
|
_placeholderView.hidden = true;
|
|
}
|
|
else
|
|
{
|
|
UIView *placeholderView = _placeholderView;
|
|
[UIView animateWithDuration:_fadeTransitionDuration animations:^
|
|
{
|
|
placeholderView.alpha = 0.0f;
|
|
} completion:^(BOOL finished)
|
|
{
|
|
if (finished)
|
|
placeholderView.hidden = true;
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.currentUrl = nil;
|
|
self.currentFilter = nil;
|
|
}
|
|
|
|
if (_progressHandler)
|
|
_progressHandler(self, 1.0f);
|
|
|
|
self.path = nil;
|
|
}
|
|
/*else if (self.path != nil && ![path isEqualToString:self.path])
|
|
{
|
|
TGLog(@"Received wrong path: <<<%@>>> vs <<<%@>>>", self.path, path);
|
|
}*/
|
|
});
|
|
}
|
|
|
|
@end
|