Skip to content

Commit

Permalink
Support for 16-bit color and 8 bit grayscale thumbnails
Browse files Browse the repository at this point in the history
  • Loading branch information
Mallory Paine committed Oct 23, 2013
1 parent 346e91b commit 9902af7
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 36 deletions.
20 changes: 15 additions & 5 deletions FastImageCache/FICImageFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) {
All images associated with a particular format must have the same image dimentions and opacity preference. You can define the maximum number of entries that an image format can accommodate to
prevent the image cache from consuming too much disk space. Each `<FICImageTable>` managed by the image cache is associated with a single image format.
*/


typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
FICImageFormatStyle32BitBGRA,
FICImageFormatStyle16BitBGR,
FICImageFormatStyle8BitGrayscale,
};

@interface FICImageFormat : NSObject <NSCopying>

///------------------------------
Expand Down Expand Up @@ -56,10 +64,12 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) {
*/
@property (nonatomic, assign, readonly) CGSize pixelSize;

/**
Whether or not the bitmap of the image table created by this format needs to include an alpha channel.
*/
@property (nonatomic, assign, getter = isOpaque) BOOL opaque;
@property (nonatomic, assign) FICImageFormatStyle style;

@property (nonatomic, readonly) CGBitmapInfo bitmapInfo;
@property (nonatomic, readonly) NSInteger bytesPerPixel;
@property (nonatomic, readonly) NSInteger bitsPerComponent;
@property (nonatomic, readonly) BOOL isGrayscale;

/**
The maximum number of entries that an image table can contain for this image format.
Expand Down Expand Up @@ -103,6 +113,6 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) {
@return An autoreleased instance of `<FICImageFormat>` or one of its subclasses, if any exist.
*/
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize isOpaque:(BOOL)isOpaque maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices;
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices;

@end
74 changes: 67 additions & 7 deletions FastImageCache/FICImageFormat.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
static NSString *const FICImageFormatFamilyKey = @"family";
static NSString *const FICImageFormatWidthKey = @"width";
static NSString *const FICImageFormatHeightKey = @"height";
static NSString *const FICImageFormatIsOpaqueKey = @"isOpaque";
static NSString *const FICImageFormatStyleKey = @"style";
static NSString *const FICImageFormatMaximumCountKey = @"maximumCount";
static NSString *const FICImageFormatDevicesKey = @"devices";

Expand All @@ -27,7 +27,7 @@ @interface FICImageFormat () {
NSString *_family;
CGSize _imageSize;
CGSize _pixelSize;
BOOL _isOpaque;
FICImageFormatStyle _style;
NSInteger _maximumCount;
FICImageFormatDevices _devices;
}
Expand All @@ -42,7 +42,7 @@ @implementation FICImageFormat
@synthesize family = _family;
@synthesize imageSize = _imageSize;
@synthesize pixelSize = _pixelSize;
@synthesize opaque = _isOpaque;
@synthesize style = _style;
@synthesize maximumCount = _maximumCount;
@synthesize devices = _devices;

Expand All @@ -58,15 +58,75 @@ - (void)setImageSize:(CGSize)imageSize {
}
}

- (CGBitmapInfo)bitmapInfo {
CGBitmapInfo info;
switch (_style) {
case FICImageFormatStyle32BitBGRA:
info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
break;
case FICImageFormatStyle16BitBGR:
info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Host;
break;
case FICImageFormatStyle8BitGrayscale:
info = (CGBitmapInfo)kCGImageAlphaNone;
break;
}
return info;
}

- (NSInteger)bytesPerPixel {
NSInteger bytesPerPixel;
switch (_style) {
case FICImageFormatStyle32BitBGRA:
bytesPerPixel = 4;
break;
case FICImageFormatStyle16BitBGR:
bytesPerPixel = 2;
break;
case FICImageFormatStyle8BitGrayscale:
bytesPerPixel = 1;
break;
}
return bytesPerPixel;
}

- (NSInteger)bitsPerComponent {
NSInteger bitsPerComponent;
switch (_style) {
case FICImageFormatStyle32BitBGRA:
case FICImageFormatStyle8BitGrayscale:
bitsPerComponent = 8;
break;
case FICImageFormatStyle16BitBGR:
bitsPerComponent = 5;
break;
}
return bitsPerComponent;
}

- (BOOL)isGrayscale {
BOOL isGrayscale;
switch (_style) {
case FICImageFormatStyle32BitBGRA:
case FICImageFormatStyle16BitBGR:
isGrayscale = NO;
break;
case FICImageFormatStyle8BitGrayscale:
isGrayscale = YES;
break;
}
return isGrayscale;
}

#pragma mark - Object Lifecycle

+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize isOpaque:(BOOL)isOpaque maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices {
+ (instancetype)formatWithName:(NSString *)name family:(NSString *)family imageSize:(CGSize)imageSize style:(FICImageFormatStyle)style maximumCount:(NSInteger)maximumCount devices:(FICImageFormatDevices)devices {
FICImageFormat *imageFormat = [[FICImageFormat alloc] init];

[imageFormat setName:name];
[imageFormat setFamily:family];
[imageFormat setImageSize:imageSize];
[imageFormat setOpaque:isOpaque];
[imageFormat setStyle:style];
[imageFormat setMaximumCount:maximumCount];
[imageFormat setDevices:devices];

Expand All @@ -82,7 +142,7 @@ - (NSDictionary *)dictionaryRepresentation {
[dictionaryRepresentation setValue:_family forKey:FICImageFormatFamilyKey];
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:_imageSize.width] forKey:FICImageFormatWidthKey];
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:_imageSize.height] forKey:FICImageFormatHeightKey];
[dictionaryRepresentation setValue:[NSNumber numberWithBool:_isOpaque] forKey:FICImageFormatIsOpaqueKey];
[dictionaryRepresentation setValue:[NSNumber numberWithInt:_style] forKey:FICImageFormatStyleKey];
[dictionaryRepresentation setValue:[NSNumber numberWithUnsignedInteger:_maximumCount] forKey:FICImageFormatMaximumCountKey];
[dictionaryRepresentation setValue:[NSNumber numberWithInt:_devices] forKey:FICImageFormatDevicesKey];
[dictionaryRepresentation setValue:[NSNumber numberWithFloat:[[UIScreen mainScreen] scale]] forKey:FICImageTableScreenScaleKey];
Expand All @@ -101,7 +161,7 @@ - (id)copyWithZone:(NSZone *)zone {
[imageFormatCopy setName:[self name]];
[imageFormatCopy setFamily:[self family]];
[imageFormatCopy setImageSize:[self imageSize]];
[imageFormatCopy setOpaque:[self isOpaque]];
[imageFormatCopy setStyle:[self style]];
[imageFormatCopy setMaximumCount:[self maximumCount]];
[imageFormatCopy setDevices:[self devices]];

Expand Down
37 changes: 15 additions & 22 deletions FastImageCache/FICImageTable.m
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ - (instancetype)initWithFormat:(FICImageFormat *)imageFormat {

_screenScale = [[UIScreen mainScreen] scale];

int bytesPerPixel = 4;
_imageRowLength = FICByteAlignForCoreAnimation([_imageFormat pixelSize].width * bytesPerPixel);
_imageLength = _imageRowLength * (NSInteger)[_imageFormat pixelSize].height;
CGSize pixelSize = [_imageFormat pixelSize];
NSInteger bytesPerPixel = [_imageFormat bytesPerPixel];
_imageRowLength = FICByteAlignForCoreAnimation(pixelSize.width * bytesPerPixel);
_imageLength = _imageRowLength * (NSInteger)pixelSize.height;

_chunkMapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];

Expand Down Expand Up @@ -259,21 +260,16 @@ - (void)setEntryForEntityUUID:(NSString *)entityUUID sourceImageUUID:(NSString *

if (newEntryIndex < _entryCount) {
CGSize pixelSize = [_imageFormat pixelSize];
CGBitmapInfo bitmapInfo;
if ([_imageFormat isOpaque]) {
bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
} else {
bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
}

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = [_imageFormat bitmapInfo];
CGColorSpaceRef colorSpace = [_imageFormat isGrayscale] ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();
NSInteger bitsPerComponent = [_imageFormat bitsPerComponent];

// Create context whose backing store *is* the mapped file data
FICImageTableEntry *entryData = [self _entryDataAtIndex:newEntryIndex];
CGContextRef context = CGBitmapContextCreate([entryData bytes], pixelSize.width, pixelSize.height, 8, _imageRowLength, colorSpace, bitmapInfo);
CGContextRef context = CGBitmapContextCreate([entryData bytes], pixelSize.width, pixelSize.height, bitsPerComponent, _imageRowLength, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);

CGContextTranslateCTM(context, 0, [_imageFormat pixelSize].height);
CGContextTranslateCTM(context, 0, pixelSize.height);
CGContextScaleCTM(context, _screenScale, -_screenScale);

// Call drawing block to allow client to draw into the context
Expand Down Expand Up @@ -321,18 +317,15 @@ - (UIImage *)newImageForEntityUUID:(NSString *)entityUUID sourceImageUUID:(NSStr
[self _entryWasAccessedWithEntityUUID:entityUUID];

// Create CGImageRef whose backing store *is* the mapped image table entry. We avoid a memcpy this way.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef dataProvider = CGDataProviderCreateWithData((__bridge_retained void *)entryData, [entryData bytes], [entryData imageLength], _FICReleaseImageData);

CGSize pixelSize = [_imageFormat pixelSize];
CGBitmapInfo bitmapInfo;
if ([_imageFormat isOpaque]) {
bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
} else {
bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
}

CGImageRef imageRef = CGImageCreate(pixelSize.width, pixelSize.height, 8, 32, _imageRowLength, colorSpace, bitmapInfo, dataProvider, NULL, false, (CGColorRenderingIntent)0);
CGBitmapInfo bitmapInfo = [_imageFormat bitmapInfo];
NSInteger bitsPerComponent = [_imageFormat bitsPerComponent];
NSInteger bitsPerPixel = [_imageFormat bytesPerPixel] * 8;
CGColorSpaceRef colorSpace = [_imageFormat isGrayscale] ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB();

CGImageRef imageRef = CGImageCreate(pixelSize.width, pixelSize.height, bitsPerComponent, bitsPerPixel, _imageRowLength, colorSpace, bitmapInfo, dataProvider, NULL, false, (CGColorRenderingIntent)0);
CGDataProviderRelease(dataProvider);
CGColorSpaceRelease(colorSpace);

Expand Down
4 changes: 2 additions & 2 deletions FastImageCacheDemo/Classes/FICDAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSInteger squareImageFormatMaximumCount = 400;
FICImageFormatDevices squareImageFormatDevices = FICImageFormatDevicePhone | FICImageFormatDevicePad;

FICImageFormat *squareImageFormat = [FICImageFormat formatWithName:FICDPhotoSquareImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize isOpaque:NO
FICImageFormat *squareImageFormat = [FICImageFormat formatWithName:FICDPhotoSquareImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGRA
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];

[mutableImageFormats addObject:squareImageFormat];
Expand All @@ -40,7 +40,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSInteger pixelImageFormatMaximumCount = 1000;
FICImageFormatDevices pixelImageFormatDevices = FICImageFormatDevicePhone | FICImageFormatDevicePad;

FICImageFormat *pixelImageFormat = [FICImageFormat formatWithName:FICDPhotoPixelImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoPixelImageSize isOpaque:YES
FICImageFormat *pixelImageFormat = [FICImageFormat formatWithName:FICDPhotoPixelImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoPixelImageSize style:FICImageFormatStyle32BitBGRA
maximumCount:pixelImageFormatMaximumCount devices:pixelImageFormatDevices];

[mutableImageFormats addObject:pixelImageFormat];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = Icon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Launch Image";
"CODE_SIGN_IDENTITY[sdk=iphonesimulator*]" = "";
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Supporting Files/FastImageCacheDemo-Prefix.pch";
INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/FastImageCacheDemo-Info.plist";
Expand Down

0 comments on commit 9902af7

Please sign in to comment.