diff --git a/FastImageCache/FICImageFormat.h b/FastImageCache/FICImageFormat.h index c4e422d..480529c 100644 --- a/FastImageCache/FICImageFormat.h +++ b/FastImageCache/FICImageFormat.h @@ -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 `` managed by the image cache is associated with a single image format. */ + + +typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) { + FICImageFormatStyle32BitBGRA, + FICImageFormatStyle16BitBGR, + FICImageFormatStyle8BitGrayscale, +}; + @interface FICImageFormat : NSObject ///------------------------------ @@ -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. @@ -103,6 +113,6 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) { @return An autoreleased instance of `` 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 diff --git a/FastImageCache/FICImageFormat.m b/FastImageCache/FICImageFormat.m index 20519bd..11ac189 100644 --- a/FastImageCache/FICImageFormat.m +++ b/FastImageCache/FICImageFormat.m @@ -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"; @@ -27,7 +27,7 @@ @interface FICImageFormat () { NSString *_family; CGSize _imageSize; CGSize _pixelSize; - BOOL _isOpaque; + FICImageFormatStyle _style; NSInteger _maximumCount; FICImageFormatDevices _devices; } @@ -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; @@ -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]; @@ -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]; @@ -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]]; diff --git a/FastImageCache/FICImageTable.m b/FastImageCache/FICImageTable.m index ee08bc1..177592d 100644 --- a/FastImageCache/FICImageTable.m +++ b/FastImageCache/FICImageTable.m @@ -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]; @@ -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 @@ -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); diff --git a/FastImageCacheDemo/Classes/FICDAppDelegate.m b/FastImageCacheDemo/Classes/FICDAppDelegate.m index 31c8d12..b1c8dce 100644 --- a/FastImageCacheDemo/Classes/FICDAppDelegate.m +++ b/FastImageCacheDemo/Classes/FICDAppDelegate.m @@ -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]; @@ -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]; diff --git a/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj b/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj index 6134fbe..73c8a00 100644 --- a/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj +++ b/FastImageCacheDemo/FastImageCacheDemo.xcodeproj/project.pbxproj @@ -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";