This repository has been archived by the owner on Jul 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
296 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#import "FTMaskRenderer.h" | ||
|
||
@interface FTImageMaskRenderer : FTMaskRenderer | ||
|
||
@property (readonly, nonatomic) UIImage *sourceImage; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#import "FTImageMaskRenderer.h" | ||
|
||
@implementation FTImageMaskRenderer | ||
|
||
@synthesize sourceImage = _sourceImage; | ||
|
||
- (UIImage *)sourceImage; | ||
{ | ||
if (!_sourceImage) { | ||
_sourceImage = [[UIImage alloc] initWithContentsOfFile:self.URL.path]; | ||
} | ||
return _sourceImage; | ||
} | ||
|
||
- (CGSize)targetSize; | ||
{ | ||
return self.sourceImage.size; | ||
} | ||
|
||
- (UIImage *)imageWithCacheIdentifier:(NSString *)identifier; | ||
{ | ||
if (self.sourceImage == nil) { | ||
[NSException raise:@"FTPDFIconRendererCacheError" | ||
format:@"Can’t render an image without a source image."]; | ||
} | ||
return [super imageWithCacheIdentifier:identifier]; | ||
} | ||
|
||
- (void)drawImageInContext:(CGContextRef)context; | ||
{ | ||
UIImage *source = self.sourceImage; | ||
CGRect rect = CGRectMake(0, 0, source.size.width, source.size.height); | ||
CGContextClipToMask(context, rect, [source CGImage]); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#import <UIKit/UIKit.h> | ||
|
||
@interface FTMaskRenderer : NSObject | ||
|
||
@property (readonly, nonatomic) NSURL *URL; | ||
@property (assign, nonatomic) UIColor *targetColor; | ||
@property (assign, nonatomic) BOOL cache; | ||
|
||
+ (NSString *)cacheDirectory; | ||
|
||
- (id)initWithURL:(NSURL *)URL; | ||
|
||
- (CGSize)targetSize; | ||
|
||
// When caching is enabled and the image is used as a mask then an identifier | ||
// *has* to be specified. | ||
// | ||
// For example, when generating button icons, you could use an identifier like | ||
// `normal` or `highlighted`, depending on the state. | ||
- (UIImage *)image; | ||
- (UIImage *)imageWithCacheIdentifier:(NSString *)identifier; | ||
|
||
// PRIVATE | ||
|
||
- (void)drawImageInContext:(CGContextRef)context; | ||
- (void)drawTargetColorInContext:(CGContextRef)context; | ||
- (void)canCacheWithIdentifier:(NSString *)identifier; | ||
- (NSString *)cacheRawFilenameWithIdentifier:(NSString *)identifier; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
#import "FTMaskRenderer.h" | ||
#import "TargetConditionals.h" | ||
|
||
// From: https://gist.github.com/1209911 | ||
#import <CommonCrypto/CommonDigest.h> | ||
static NSString * | ||
FTPDFMD5String(NSString *input) { | ||
const char *cStr = [input UTF8String]; | ||
unsigned char result[16]; | ||
CC_MD5(cStr, strlen(cStr), result); | ||
return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", | ||
result[0], result[1], result[2], result[3], | ||
result[4], result[5], result[6], result[7], | ||
result[8], result[9], result[10], result[11], | ||
result[12], result[13], result[14], result[15]]; | ||
} | ||
|
||
@interface FTMaskRenderer () | ||
|
||
|
||
- (NSString *)cachePathWithIdentifier:(NSString *)identifier; | ||
- (UIImage *)cachedImageAtPath:(NSString *)cachePath; | ||
@end | ||
|
||
@implementation FTMaskRenderer | ||
|
||
- (id)initWithURL:(NSURL *)URL; | ||
{ | ||
if ((self = [super init])) { | ||
_URL = URL; | ||
_cache = YES; | ||
} | ||
return self; | ||
} | ||
|
||
- (CGSize)targetSize; | ||
{ | ||
[NSException raise:@"AbstractClassError" | ||
format:@"This class is supposed to be subclassed."]; | ||
return CGSizeZero; | ||
} | ||
|
||
#pragma mark - drawing | ||
|
||
- (UIImage *)image; | ||
{ | ||
return [self imageWithCacheIdentifier:nil]; | ||
} | ||
|
||
- (UIImage *)imageWithCacheIdentifier:(NSString *)identifier; | ||
{ | ||
if (self.cache) [self canCacheWithIdentifier:identifier]; | ||
|
||
UIImage *image = nil; | ||
NSString *cachePath = nil; | ||
|
||
if (self.cache) { | ||
cachePath = [self cachePathWithIdentifier:identifier]; | ||
image = [self cachedImageAtPath:cachePath]; | ||
if (image) return image; | ||
} | ||
|
||
// Setup context for target size and with main screen scale factor. | ||
CGSize targetSize = self.targetSize; | ||
UIGraphicsBeginImageContextWithOptions(targetSize, false, 0); | ||
CGContextRef context = UIGraphicsGetCurrentContext(); | ||
// Flip context, making bottom-left the origin. | ||
CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, targetSize.height)); | ||
|
||
[self drawImageInContext:context]; | ||
[self drawTargetColorInContext:context]; | ||
|
||
image = UIGraphicsGetImageFromCurrentImageContext(); | ||
UIGraphicsEndImageContext(); | ||
|
||
if (self.cache) { | ||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ | ||
[UIImagePNGRepresentation(image) writeToFile:cachePath atomically:NO]; | ||
}); | ||
} | ||
|
||
return image; | ||
} | ||
|
||
- (void)drawImageInContext:(CGContextRef)context; | ||
{ | ||
[NSException raise:@"AbstractClassError" | ||
format:@"This class is supposed to be subclassed."]; | ||
} | ||
|
||
- (void)drawTargetColorInContext:(CGContextRef)context; | ||
{ | ||
CGContextSetFillColorWithColor(context, self.targetColor.CGColor); | ||
CGContextSetBlendMode(context, kCGBlendModeSourceAtop); | ||
CGSize targetSize = self.targetSize; | ||
CGContextFillRect(context, CGRectMake(0, 0, targetSize.width, targetSize.height)); | ||
} | ||
|
||
#pragma mark - caching | ||
|
||
+ (NSString *)cacheDirectory; | ||
{ | ||
static NSString *cacheDirectory; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
cacheDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; | ||
cacheDirectory = [cacheDirectory stringByAppendingPathComponent:@"__FTPDFIconRenderer_CACHE__"]; | ||
[[NSFileManager new] createDirectoryAtPath:cacheDirectory | ||
withIntermediateDirectories:YES | ||
attributes:nil | ||
error:NULL]; | ||
}); | ||
return cacheDirectory; | ||
} | ||
|
||
- (void)canCacheWithIdentifier:(NSString *)identifier; | ||
{ | ||
if (identifier == nil) { | ||
[NSException raise:@"FTPDFIconRendererCacheError" | ||
format:@"A masked result can’t be cached without a cache identifier."]; | ||
} else if (self.targetColor == nil) { | ||
[NSException raise:@"FTPDFIconRendererCacheError" | ||
format:@"Can’t produce an image from a mask without a target color."]; | ||
} | ||
} | ||
|
||
- (UIImage *)cachedImageAtPath:(NSString *)cachePath; | ||
{ | ||
UIImage *image = nil; | ||
|
||
NSData *data = [NSData dataWithContentsOfFile:cachePath]; | ||
if (data) { | ||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); | ||
CGImageRef imageRef = CGImageCreateWithPNGDataProvider(provider, NULL, NO, kCGRenderingIntentDefault); | ||
CGDataProviderRelease(provider); | ||
image = [UIImage imageWithCGImage:imageRef | ||
scale:[[UIScreen mainScreen] scale] | ||
orientation:UIImageOrientationUp]; | ||
CGImageRelease(imageRef); | ||
} | ||
|
||
return image; | ||
} | ||
|
||
- (NSString *)cacheRawFilenameWithIdentifier:(NSString *)identifier; | ||
{ | ||
NSDictionary *attributes = [[NSFileManager new] attributesOfItemAtPath:self.URL.path | ||
error:NULL]; | ||
NSString *filename = [NSString stringWithFormat:@"%@-%@-%@-%@-%@", | ||
[self.URL lastPathComponent], | ||
attributes[NSFileSize], | ||
attributes[NSFileModificationDate], | ||
NSStringFromCGSize(self.targetSize), | ||
identifier ?: @""]; | ||
return filename; | ||
} | ||
|
||
- (NSString *)cachePathWithIdentifier:(NSString *)identifier; | ||
{ | ||
NSString *cachePath = [self cacheRawFilenameWithIdentifier:identifier]; | ||
#if TARGET_IPHONE_SIMULATOR | ||
// On the simulator, the cache dir is shared between retina and non-retina | ||
// devices, so include the device's main screen scale factor to ensure the | ||
// right dimensions are used per device. | ||
cachePath = [NSString stringWithFormat:@"%@-%f", cachePath, [[UIScreen mainScreen] scale]]; | ||
#endif | ||
cachePath = FTPDFMD5String(cachePath); | ||
cachePath = [[[self class] cacheDirectory] stringByAppendingPathComponent:cachePath]; | ||
cachePath = [cachePath stringByAppendingPathExtension:@"png"]; | ||
return cachePath; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.