1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-12 09:29:55 +01:00
Telegram/legacy/TelegraphKit/TGRemoteImageView.m
2015-10-01 19:19:52 +03:00

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