mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 09:29:55 +01:00
389 lines
14 KiB
Objective-C
389 lines
14 KiB
Objective-C
#import "TGStickerImageDataSource.h"
|
|
|
|
#import "TGStringUtils.h"
|
|
|
|
#import "TGWorkerPool.h"
|
|
#import "TGWorkerTask.h"
|
|
#import "TGMediaPreviewTask.h"
|
|
|
|
#import "TGMemoryImageCache.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
#import "TGStringUtils.h"
|
|
#import "TGRemoteImageView.h"
|
|
|
|
#import "TGImageBlur.h"
|
|
#import "UIImage+TG.h"
|
|
#import "NSObject+TGLock.h"
|
|
|
|
#import "TGMediaStoreContext.h"
|
|
|
|
#import "UIImage+WebP.h"
|
|
|
|
#import "TGDocumentMediaAttachment.h"
|
|
|
|
#import "TGAppDelegate.h"
|
|
|
|
static TGWorkerPool *workerPool()
|
|
{
|
|
static TGWorkerPool *instance = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
instance = [[TGWorkerPool alloc] init];
|
|
});
|
|
|
|
return instance;
|
|
}
|
|
|
|
static ASQueue *taskManagementQueue()
|
|
{
|
|
static ASQueue *queue = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
queue = [[ASQueue alloc] initWithName:"org.telegram.stickerImageTaskManagementQueue"];
|
|
});
|
|
|
|
return queue;
|
|
}
|
|
|
|
@implementation TGStickerImageDataSource
|
|
|
|
+ (void)load
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
[TGImageDataSource registerDataSource:[[self alloc] init]];
|
|
}
|
|
}
|
|
|
|
+ (NSString *)uriPrefix
|
|
{
|
|
return @"sticker://?";
|
|
}
|
|
|
|
- (bool)canHandleUri:(NSString *)uri
|
|
{
|
|
return [uri hasPrefix:@"sticker://"];
|
|
}
|
|
|
|
- (bool)canHandleAttributeUri:(NSString *)uri
|
|
{
|
|
return [uri hasPrefix:@"sticker://"];
|
|
}
|
|
|
|
- (id)loadDataAsyncWithUri:(NSString *)uri progress:(void (^)(float))progress partialCompletion:(void (^)(TGDataResource *resource))__unused partialCompletion completion:(void (^)(TGDataResource *))completion
|
|
{
|
|
TGMediaPreviewTask *previewTask = [[TGMediaPreviewTask alloc] init];
|
|
|
|
[taskManagementQueue() dispatchOnQueue:^
|
|
{
|
|
TGWorkerTask *workerTask = [[TGWorkerTask alloc] initWithBlock:^(bool (^isCancelled)())
|
|
{
|
|
TGDataResource *result = [TGStickerImageDataSource _performLoad:uri isCancelled:isCancelled];
|
|
|
|
if (result != nil && progress != nil)
|
|
progress(1.0f);
|
|
|
|
if (isCancelled != nil && isCancelled())
|
|
return;
|
|
|
|
if (completion != nil)
|
|
completion(result != nil ? result : [TGStickerImageDataSource resultForUnavailableImage]);
|
|
}];
|
|
|
|
if ([TGStickerImageDataSource _isDataLocallyAvailableForUri:uri])
|
|
{
|
|
[previewTask executeWithWorkerTask:workerTask workerPool:workerPool()];
|
|
}
|
|
else
|
|
{
|
|
NSDictionary *args = [TGStringUtils argumentDictionaryInUrlString:[uri substringFromIndex:[TGStickerImageDataSource uriPrefix].length]];
|
|
|
|
if ((![args[@"documentId"] respondsToSelector:@selector(longLongValue)] && ![args[@"localDocumentId"] respondsToSelector:@selector(longLongValue)]) || (![args[@"fileName"] respondsToSelector:@selector(characterAtIndex:)]) || (![args[@"datacenterId"] respondsToSelector:@selector(intValue)]))
|
|
{
|
|
if (completion != nil)
|
|
completion([TGStickerImageDataSource resultForUnavailableImage]);
|
|
}
|
|
else
|
|
{
|
|
static NSString *filesDirectory = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
filesDirectory = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"files"];
|
|
});
|
|
|
|
NSString *fileDirectoryName = nil;
|
|
if (args[@"documentId"] != nil)
|
|
{
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"%" PRIx64 "", (int64_t)[args[@"documentId"] longLongValue]];
|
|
}
|
|
else if (args[@"localDocumentId"] != nil)
|
|
{
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"local%" PRIx64 "", (int64_t)[args[@"localDocumentId"] longLongValue]];
|
|
}
|
|
|
|
NSString *fileDirectory = [filesDirectory stringByAppendingPathComponent:fileDirectoryName];
|
|
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:fileDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
|
|
|
NSString *filePath = [fileDirectory stringByAppendingPathComponent:args[@"fileName"]];
|
|
|
|
NSString *thumbnailPath = [fileDirectory stringByAppendingPathComponent:@"thumbnail"];
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:thumbnailPath])
|
|
{
|
|
TGDataResource *partialResult = [TGStickerImageDataSource _performLoad:uri isCancelled:nil];
|
|
if (partialResult != nil)
|
|
{
|
|
if (partialCompletion)
|
|
partialCompletion(partialResult);
|
|
}
|
|
}
|
|
|
|
NSString *thumbnailHighPath = [fileDirectory stringByAppendingPathComponent:@"thumbnail-high"];
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:thumbnailHighPath])
|
|
{
|
|
TGDataResource *partialResult = [TGStickerImageDataSource _performLoad:uri isCancelled:nil];
|
|
if (partialResult != nil)
|
|
{
|
|
if (partialCompletion)
|
|
partialCompletion(partialResult);
|
|
}
|
|
}
|
|
|
|
NSMutableArray *attributes = [[NSMutableArray alloc] init];
|
|
[attributes addObject:[[TGDocumentAttributeFilename alloc] initWithFilename:args[@"fileName"]]];
|
|
|
|
TGDocumentMediaAttachment *documentAttachment = [[TGDocumentMediaAttachment alloc] init];
|
|
documentAttachment.documentId = [args[@"documentId"] longLongValue];
|
|
documentAttachment.accessHash = [args[@"accessHash"] longLongValue];
|
|
documentAttachment.datacenterId = [args[@"datacenterId"] intValue];
|
|
documentAttachment.attributes = attributes;
|
|
documentAttachment.size = [args[@"size"] intValue];
|
|
|
|
[previewTask executeWithTargetFilePath:filePath document:documentAttachment progress:^(float value)
|
|
{
|
|
if (progress)
|
|
progress(value);
|
|
} completion:^(bool success)
|
|
{
|
|
if (success)
|
|
{
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
[previewTask executeWithWorkerTask:workerTask workerPool:workerPool()];
|
|
}];
|
|
}
|
|
else
|
|
{
|
|
if (completion != nil)
|
|
completion([TGStickerImageDataSource resultForUnavailableImage]);
|
|
}
|
|
} workerTask:nil];
|
|
}
|
|
}
|
|
}];
|
|
|
|
return previewTask;
|
|
}
|
|
|
|
+ (bool)_isDataLocallyAvailableForUri:(NSString *)uri
|
|
{
|
|
NSDictionary *args = [TGStringUtils argumentDictionaryInUrlString:[uri substringFromIndex:[TGStickerImageDataSource uriPrefix].length]];
|
|
|
|
if ((![args[@"documentId"] respondsToSelector:@selector(longLongValue)] && ![args[@"localDocumentId"] respondsToSelector:@selector(longLongValue)]) || (![args[@"fileName"] respondsToSelector:@selector(characterAtIndex:)]))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static NSString *filesDirectory = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
filesDirectory = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"files"];
|
|
});
|
|
|
|
NSString *fileDirectoryName = nil;
|
|
if ([args[@"documentId"] longLongValue] != 0)
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"%" PRIx64 "", (int64_t)[args[@"documentId"] longLongValue]];
|
|
else
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"local%" PRIx64 "", (int64_t)[args[@"localDocumentId"] longLongValue]];
|
|
NSString *fileDirectory = [filesDirectory stringByAppendingPathComponent:fileDirectoryName];
|
|
|
|
NSString *filePath = [fileDirectory stringByAppendingPathComponent:args[@"fileName"]];
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:NULL])
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
- (void)cancelTaskById:(id)taskId
|
|
{
|
|
[taskManagementQueue() dispatchOnQueue:^
|
|
{
|
|
if ([taskId isKindOfClass:[TGMediaPreviewTask class]])
|
|
{
|
|
TGMediaPreviewTask *previewTask = taskId;
|
|
[previewTask cancel];
|
|
}
|
|
}];
|
|
}
|
|
|
|
+ (TGDataResource *)resultForUnavailableImage
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
- (id)loadAttributeSyncForUri:(NSString *)__unused uri attribute:(NSString *)attribute
|
|
{
|
|
if ([attribute isEqualToString:@"placeholder"])
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (TGDataResource *)loadDataSyncWithUri:(NSString *)uri canWait:(bool)canWait acceptPartialData:(bool)__unused acceptPartialData asyncTaskId:(__autoreleasing id *)__unused asyncTaskId progress:(void (^)(float))__unused progress partialCompletion:(void (^)(TGDataResource *))__unused partialCompletion completion:(void (^)(TGDataResource *))__unused completion
|
|
{
|
|
if (uri == nil)
|
|
return nil;
|
|
|
|
UIImage *cachedImage = [[TGMediaStoreContext instance] mediaImage:uri attributes:nil];
|
|
if (cachedImage != nil)
|
|
return [[TGDataResource alloc] initWithImage:cachedImage decoded:true];
|
|
|
|
if (!canWait)
|
|
return nil;
|
|
|
|
return [TGStickerImageDataSource _performLoad:uri isCancelled:nil];
|
|
}
|
|
|
|
+ (TGDataResource *)_performLoad:(NSString *)uri isCancelled:(bool (^)())isCancelled
|
|
{
|
|
if (isCancelled && isCancelled())
|
|
{
|
|
TGLog(@"[TGPhotoMediaPreviewImageDataSource cancelled while loading %@]", uri);
|
|
return nil;
|
|
}
|
|
|
|
NSDictionary *args = [TGStringUtils argumentDictionaryInUrlString:[uri substringFromIndex:[TGStickerImageDataSource uriPrefix].length]];
|
|
|
|
if ((![args[@"documentId"] respondsToSelector:@selector(longLongValue)] && ![args[@"localDocumentId"] respondsToSelector:@selector(longLongValue)]) || (![args[@"fileName"] respondsToSelector:@selector(characterAtIndex:)]))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static NSString *filesDirectory = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
filesDirectory = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"files"];
|
|
});
|
|
|
|
NSString *fileDirectoryName = nil;
|
|
if ([args[@"documentId"] longLongValue] != 0)
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"%" PRIx64 "", (int64_t)[args[@"documentId"] longLongValue]];
|
|
else
|
|
fileDirectoryName = [[NSString alloc] initWithFormat:@"local%" PRIx64 "", (int64_t)[args[@"localDocumentId"] longLongValue]];
|
|
NSString *fileDirectory = [filesDirectory stringByAppendingPathComponent:fileDirectoryName];
|
|
|
|
CGSize size = CGSizeMake([args[@"width"] intValue], [args[@"height"] intValue]);
|
|
|
|
UIImage *thumbnailSourceImage = nil;
|
|
bool lowQualityThumbnail = false;
|
|
|
|
NSString *filePath = [fileDirectory stringByAppendingPathComponent:args[@"fileName"]];
|
|
NSString *thumbnailPath = [fileDirectory stringByAppendingPathComponent:@"thumbnail"];
|
|
NSString *thumbnailHighPath = [fileDirectory stringByAppendingPathComponent:@"thumbnail-high"];
|
|
|
|
UIImage *image = nil;
|
|
|
|
{
|
|
NSString *cachedFilePath = [fileDirectory stringByAppendingPathComponent:@"cached.bin"];
|
|
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath isDirectory:NULL])
|
|
{
|
|
image = [UIImage convertFromGZippedData:cachedFilePath size:size];
|
|
}
|
|
|
|
if (image != nil)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:NULL])
|
|
{
|
|
__autoreleasing NSData *compressedData = nil;
|
|
image = [UIImage convertFromWebP:filePath compressedData:&compressedData error:nil];
|
|
|
|
if (compressedData != nil)
|
|
[compressedData writeToFile:cachedFilePath atomically:true];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (image == nil)
|
|
{
|
|
image = [[UIImage alloc] initWithContentsOfFile:filePath];
|
|
if (image != nil)
|
|
image = TGScaleImageToPixelSize(image, size);
|
|
}
|
|
|
|
if (image == nil)
|
|
{
|
|
lowQualityThumbnail = true;
|
|
|
|
NSString *cachedFilePath = [fileDirectory stringByAppendingPathComponent:@"thumbnail.cached.bin"];
|
|
if ([[NSFileManager defaultManager] fileExistsAtPath:cachedFilePath isDirectory:NULL])
|
|
{
|
|
image = [UIImage convertFromGZippedData:cachedFilePath size:size];
|
|
}
|
|
|
|
image = [[UIImage alloc] initWithContentsOfFile:thumbnailPath];
|
|
if (image != nil)
|
|
{
|
|
image = TGScaleImageToPixelSize(image, TGFitSize(image.size, size));
|
|
}
|
|
else
|
|
{
|
|
__autoreleasing NSData *compressedData = nil;
|
|
image = [UIImage convertFromWebP:thumbnailPath compressedData:&compressedData error:nil];
|
|
if (compressedData != nil)
|
|
[compressedData writeToFile:cachedFilePath atomically:true];
|
|
|
|
if (image == nil)
|
|
{
|
|
image = [UIImage convertFromWebP:thumbnailHighPath compressedData:&compressedData error:nil];
|
|
if (compressedData != nil)
|
|
[compressedData writeToFile:cachedFilePath atomically:true];
|
|
}
|
|
}
|
|
|
|
image = TGBlurredAlphaImage(image, CGSizeMake(size.width / 2.0f, size.height / 2.0f));
|
|
}
|
|
|
|
thumbnailSourceImage = image;
|
|
|
|
if (thumbnailSourceImage != nil)
|
|
{
|
|
UIImage *thumbnailImage = nil;
|
|
|
|
thumbnailImage = thumbnailSourceImage;
|
|
|
|
if (thumbnailImage != nil)
|
|
{
|
|
if (!lowQualityThumbnail)
|
|
[[TGMediaStoreContext instance] setMediaImageForKey:uri image:thumbnailImage attributes:nil];
|
|
|
|
return [[TGDataResource alloc] initWithImage:thumbnailImage decoded:true];
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
@end
|