Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
m
Browse files Browse the repository at this point in the history
  • Loading branch information
alloy committed Oct 11, 2012
1 parent 319fdf9 commit b85c5c2
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 152 deletions.
12 changes: 12 additions & 0 deletions Example/FTPDFIconRenderer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
51C409A51625F62A00AF7C06 /* FTPDFIconRendererTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C409A41625F62A00AF7C06 /* FTPDFIconRendererTests.m */; };
51C409BA1626014200AF7C06 /* FTPDFIconRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C409B91626014200AF7C06 /* FTPDFIconRenderer.m */; };
51C409BD1626038900AF7C06 /* restaurant-icon-mask.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 51C409BC1626038900AF7C06 /* restaurant-icon-mask.pdf */; };
51C409DB1627191600AF7C06 /* FTMaskRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C409DA1627191600AF7C06 /* FTMaskRenderer.m */; };
51C409DF1627240D00AF7C06 /* FTImageMaskRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 51C409DE1627240D00AF7C06 /* FTImageMaskRenderer.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -58,6 +60,10 @@
51C409B81626014200AF7C06 /* FTPDFIconRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTPDFIconRenderer.h; path = ../Source/FTPDFIconRenderer.h; sourceTree = "<group>"; };
51C409B91626014200AF7C06 /* FTPDFIconRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FTPDFIconRenderer.m; path = ../Source/FTPDFIconRenderer.m; sourceTree = "<group>"; };
51C409BC1626038900AF7C06 /* restaurant-icon-mask.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = "restaurant-icon-mask.pdf"; sourceTree = SOURCE_ROOT; };
51C409D91627191600AF7C06 /* FTMaskRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTMaskRenderer.h; path = ../Source/FTMaskRenderer.h; sourceTree = "<group>"; };
51C409DA1627191600AF7C06 /* FTMaskRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FTMaskRenderer.m; path = ../Source/FTMaskRenderer.m; sourceTree = "<group>"; };
51C409DD1627240D00AF7C06 /* FTImageMaskRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTImageMaskRenderer.h; path = ../Source/FTImageMaskRenderer.h; sourceTree = "<group>"; };
51C409DE1627240D00AF7C06 /* FTImageMaskRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FTImageMaskRenderer.m; path = ../Source/FTImageMaskRenderer.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -131,6 +137,10 @@
children = (
51C409B81626014200AF7C06 /* FTPDFIconRenderer.h */,
51C409B91626014200AF7C06 /* FTPDFIconRenderer.m */,
51C409D91627191600AF7C06 /* FTMaskRenderer.h */,
51C409DA1627191600AF7C06 /* FTMaskRenderer.m */,
51C409DD1627240D00AF7C06 /* FTImageMaskRenderer.h */,
51C409DE1627240D00AF7C06 /* FTImageMaskRenderer.m */,
);
name = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -282,6 +292,8 @@
51C409861625F62900AF7C06 /* main.m in Sources */,
51C4098A1625F62900AF7C06 /* AppDelegate.m in Sources */,
51C409BA1626014200AF7C06 /* FTPDFIconRenderer.m in Sources */,
51C409DB1627191600AF7C06 /* FTMaskRenderer.m in Sources */,
51C409DF1627240D00AF7C06 /* FTImageMaskRenderer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 7 additions & 0 deletions Source/FTImageMaskRenderer.h
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
36 changes: 36 additions & 0 deletions Source/FTImageMaskRenderer.m
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
30 changes: 30 additions & 0 deletions Source/FTMaskRenderer.h
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
173 changes: 173 additions & 0 deletions Source/FTMaskRenderer.m
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
21 changes: 3 additions & 18 deletions Source/FTPDFIconRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
// * Jeffrey Sambells - http://jeffreysambells.com/2012/03/02/beating-the-20mb-size-limit-ipad-retina-displays
// * Ole Zorn - https://gist.github.com/1102091

#import <UIKit/UIKit.h>
#import "FTMaskRenderer.h"

// This object is supposed to be short-lived, because an open PDF document can
// consume quite some memory.
@interface FTPDFIconRenderer : NSObject
@interface FTPDFIconRenderer : FTMaskRenderer

@property (readonly, nonatomic) NSURL *URL;
@property (assign, nonatomic) size_t sourcePageIndex;
@property (readonly, nonatomic) CGPDFPageRef sourcePage;
@property (assign, nonatomic) UIColor *targetColor;
@property (readonly, nonatomic) BOOL isMask;
@property (assign, nonatomic) CGSize targetSize;
@property (assign, nonatomic) BOOL cache;

+ (NSString *)cacheDirectory;
@property (readonly, nonatomic) BOOL isMask;

+ (FTPDFIconRenderer *)iconRendererForPDFNamed:(NSString *)pdfName;
+ (FTPDFIconRenderer *)iconRendererForPDFNamed:(NSString *)pdfName
Expand Down Expand Up @@ -48,14 +43,4 @@
- (void)fitWidth:(CGFloat)targetWidth;
- (void)fitHeight:(CGFloat)targetHeight;

- (UIImage *)image;

// When caching is enabled and a PDF 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 *)imageWithCacheIdentifier:(NSString *)identifier;

@end

Loading

0 comments on commit b85c5c2

Please sign in to comment.