Skip to content

Commit

Permalink
Demonstrate all image format styles in demo application
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Potter committed Oct 29, 2013
1 parent 91c9e38 commit 4c7560f
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 59 deletions.
29 changes: 28 additions & 1 deletion FastImageCache/FICImageCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ typedef void (^FICImageRequestCompletionBlock)(UIImage *sourceImage);
*/
- (void)deleteImageForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName;

///-------------------------------
/// @name Canceling Image Requests
///-------------------------------

/**
Cancels an active request for an image from the image cache.
@param entity The entity that uniquely identifies the source image.
@param formatName The format name that uniquely identifies which image table to look in for the cached image.
@discussion After this method is called, the completion block of the <[FICImageCacheDelegate imageCache:wantsSourceImageForEntity:withFormatName:completionBlock:]> delegate
method for the corresponding entity, if called, does nothing.
*/
- (void)cancelImageRetrievalForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName;

///-----------------------------------
Expand Down Expand Up @@ -229,9 +243,22 @@ typedef void (^FICImageRequestCompletionBlock)(UIImage *sourceImage);

@optional

/**
This method is called on the delegate when the image cache has received an image retrieval cancellation request.
@param imageCache The image cache that has received the image retrieval cancellation request.
@param entity The entity that uniquely identifies the source image.
@param formatName The format name that uniquely identifies which image table to look in for the cached image.
@discussion When an image retrieval cancellation request is made to the image cache, it removes all of its internal bookkeeping for requests. However, it is still the
delegate's responsibility to cancel whatever logic is it performing to provide a source image to the cache (e.g., a network request).
@see [FICImageCache cancelImageRetrievalForEntity:withFormatName:]
*/
- (void)imageCache:(FICImageCache *)imageCache cancelImageLoadingForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName;


/**
This method is called on the delegate to determine whether or not all formats in a family should be processed right now.
Expand Down
60 changes: 44 additions & 16 deletions FastImageCache/FICImageFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) {
FICImageFormatDevicePad = 1 << UIUserInterfaceIdiomPad,
};

/**
`FICImageFormat` acts as a definition for the types of images that are stored in the image cache. Each image format must have a unique name, but multiple formats can belong to the same family.
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,
FICImageFormatStyle32BitBGR,
FICImageFormatStyle16BitBGR,
FICImageFormatStyle8BitGrayscale,
};

/**
`FICImageFormat` acts as a definition for the types of images that are stored in the image cache. Each image format must have a unique name, but multiple formats can belong to the same family.
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.
*/

@interface FICImageFormat : NSObject <NSCopying>

///------------------------------
Expand Down Expand Up @@ -61,17 +60,21 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
@property (nonatomic, assign) CGSize imageSize;

/**
The size, in pixels, of the images stored in the image table created by this format. This takes into account the screen scale.
A bitmask of type `<FICImageFormatStyle>` that defines the style of the image format.
`FICImageFormatStyle` has the following values:
- `FICImageFormatStyle32BitBGRA`: Full-color image format with alpha channel. 8 bits per color component, and 8 bits for the alpha channel.
- `FICImageFormatStyle32BitBGR`: Full-color image format with no alpha channel. 8 bits per color component. The remaining 8 bits are unused.
- `FICImageFormatStyle16BitBGR`: Reduced-color image format with no alpha channel. 5 bits per color component. The remaining bit is unused.
- `FICImageFormatStyle8BitGrayscale`: Grayscale-only image format with no alpha channel.
If you are storing images without an alpha component (e.g., JPEG images), then you should use the `FICImageFormatStyle32BitBGR` style for performance reasons. If you are storing very small images or images
without a great deal of color complexity, the `FICImageFormatStyle16BitBGR` style may be sufficient and uses less disk space than the 32-bit styles use. For grayscale-only image formats, the
`FICImageFormatStyle8BitGrayscale` style is sufficient and further reduces disk space usage.
*/
@property (nonatomic, assign, readonly) CGSize pixelSize;

@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 All @@ -86,6 +89,31 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
*/
@property (nonatomic, assign) FICImageFormatDevices devices;

/**
The size, in pixels, of the images stored in the image table created by this format. This takes into account the screen scale.
*/
@property (nonatomic, assign, readonly) CGSize pixelSize;

/**
The bitmap info associated with the images created with this image format.
*/
@property (nonatomic, assign, readonly) CGBitmapInfo bitmapInfo;

/**
The number of bytes each pixel of an image created with this image format occupies.
*/
@property (nonatomic, assign, readonly) NSInteger bytesPerPixel;

/**
The number of bits each pixel component (e.g., blue, green, red color channels) uses for images created with this image format.
*/
@property (nonatomic, assign, readonly) NSInteger bitsPerComponent;

/**
Whether or not the the images represented by this image format are grayscale.
*/
@property (nonatomic, assign, readonly) BOOL isGrayscale;

/**
The dictionary representation of this image format.
Expand All @@ -106,7 +134,7 @@ typedef NS_OPTIONS(NSUInteger, FICImageFormatStyle) {
@param imageSize The size, in points, of the images stored in the image table created by this format.
@param isOpaque Whether or not the image table's backing bitmap data provider is opaque.
@param style The style of the image format. See the `<style>` property description for more information.
@param maximumCount The maximum number of entries that an image table can contain for this image format.
Expand Down
27 changes: 23 additions & 4 deletions FastImageCacheDemo/Classes/FICDAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,40 @@ @implementation FICDAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSMutableArray *mutableImageFormats = [NSMutableArray array];

// Square image format
// Square image formats...
NSInteger squareImageFormatMaximumCount = 400;
FICImageFormatDevices squareImageFormatDevices = FICImageFormatDevicePhone | FICImageFormatDevicePad;

FICImageFormat *squareImageFormat = [FICImageFormat formatWithName:FICDPhotoSquareImageFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGRA
// ...32-bit BGR
FICImageFormat *squareImageFormat32BitBGRA = [FICImageFormat formatWithName:FICDPhotoSquareImage32BitBGRAFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGRA
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];

[mutableImageFormats addObject:squareImageFormat];
[mutableImageFormats addObject:squareImageFormat32BitBGRA];

// ...32-bit BGR
FICImageFormat *squareImageFormat32BitBGR = [FICImageFormat formatWithName:FICDPhotoSquareImage32BitBGRFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle32BitBGR
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];

[mutableImageFormats addObject:squareImageFormat32BitBGR];

// ...16-bit BGR
FICImageFormat *squareImageFormat16BitBGR = [FICImageFormat formatWithName:FICDPhotoSquareImage16BitBGRFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle16BitBGR
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];

[mutableImageFormats addObject:squareImageFormat16BitBGR];

// ...8-bit Grayscale
FICImageFormat *squareImageFormat8BitGrayscale = [FICImageFormat formatWithName:FICDPhotoSquareImage8BitGrayscaleFormatName family:FICDPhotoImageFormatFamily imageSize:FICDPhotoSquareImageSize style:FICImageFormatStyle8BitGrayscale
maximumCount:squareImageFormatMaximumCount devices:squareImageFormatDevices];

[mutableImageFormats addObject:squareImageFormat8BitGrayscale];

if ([UIViewController instancesRespondToSelector:@selector(preferredStatusBarStyle)]) {
// Pixel image format
NSInteger pixelImageFormatMaximumCount = 1000;
FICImageFormatDevices pixelImageFormatDevices = FICImageFormatDevicePhone | FICImageFormatDevicePad;

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

[mutableImageFormats addObject:pixelImageFormat];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

@property (nonatomic, assign, readonly, getter = isDisplayingPhoto) BOOL displayingPhoto;

- (void)showFullscreenPhoto:(FICDPhoto *)photo withThumbnailImageView:(UIImageView *)thumbnailImageView;
- (void)showFullscreenPhoto:(FICDPhoto *)photo forImageFormatName:(NSString *)imageFormatName withThumbnailImageView:(UIImageView *)thumbnailImageView;
- (void)hideFullscreenPhoto;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#import "FICDFullscreenPhotoDisplayController.h"
#import "FICDPhoto.h"

#import <CoreImage/CoreImage.h>

#pragma mark Class Extension

@interface FICDFullscreenPhotoDisplayController () <UIGestureRecognizerDelegate> {
Expand All @@ -30,6 +32,8 @@ @interface FICDFullscreenPhotoDisplayController () <UIGestureRecognizerDelegate>
FICDPhoto *_photo;

UITapGestureRecognizer *_tapGestureRecognizer;

CIContext* _CoreImageContext;
}

@end
Expand Down Expand Up @@ -90,6 +94,8 @@ - (id)init {

_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_tapGestureRecognizerStateDidChange)];
[_fullscreenView addGestureRecognizer:_tapGestureRecognizer];

_CoreImageContext = [CIContext contextWithOptions:nil];
}

return self;
Expand All @@ -101,7 +107,7 @@ - (void)dealloc {

#pragma mark - Showing and Hiding a Fullscreen Photo

- (void)showFullscreenPhoto:(FICDPhoto *)photo withThumbnailImageView:(UIImageView *)thumbnailImageView {
- (void)showFullscreenPhoto:(FICDPhoto *)photo forImageFormatName:(NSString *)imageFormatName withThumbnailImageView:(UIImageView *)thumbnailImageView {
// Stash away the photo
_photo = photo;

Expand All @@ -128,6 +134,20 @@ - (void)showFullscreenPhoto:(FICDPhoto *)photo withThumbnailImageView:(UIImageVi

// Configure the source image view
UIImage *sourceImage = [photo sourceImage];

// Desaturate the source image with Core Image if the image format name is FICDPhotoSquareImage8BitGrayscaleFormatName
if ([imageFormatName isEqualToString:FICDPhotoSquareImage8BitGrayscaleFormatName]) {
CIFilter *colorControlsFilter = [CIFilter filterWithName:@"CIColorControls"];
[colorControlsFilter setValue:[CIImage imageWithCGImage:[sourceImage CGImage]] forKey:kCIInputImageKey];
[colorControlsFilter setValue:[NSNumber numberWithFloat:0] forKey:@"inputSaturation"];

CIImage *outputCIImage = [colorControlsFilter outputImage];
CGImageRef outputImageRef = [_CoreImageContext createCGImage:outputCIImage fromRect:[outputCIImage extent]];
sourceImage = [UIImage imageWithCGImage:outputImageRef];

CGImageRelease(outputImageRef);
}

[_sourceImageView setImage:sourceImage];
[_sourceImageView setFrame:convertedThumbnailImageViewFrame];
[_sourceImageView setAlpha:0];
Expand Down
5 changes: 4 additions & 1 deletion FastImageCacheDemo/Classes/FICDPhoto.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@

extern NSString *const FICDPhotoImageFormatFamily;

extern NSString *const FICDPhotoSquareImageFormatName;
extern NSString *const FICDPhotoSquareImage32BitBGRAFormatName;
extern NSString *const FICDPhotoSquareImage32BitBGRFormatName;
extern NSString *const FICDPhotoSquareImage16BitBGRFormatName;
extern NSString *const FICDPhotoSquareImage8BitGrayscaleFormatName;
extern NSString *const FICDPhotoPixelImageFormatName;

extern CGSize const FICDPhotoSquareImageSize;
Expand Down
29 changes: 19 additions & 10 deletions FastImageCacheDemo/Classes/FICDPhoto.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@

NSString *const FICDPhotoImageFormatFamily = @"FICDPhotoImageFormatFamily";

NSString *const FICDPhotoSquareImageFormatName = @"FICDPhotoSquareImageFormatName";
NSString *const FICDPhotoSquareImage32BitBGRAFormatName = @"FICDPhotoSquareImage32BitBGRAFormatName";
NSString *const FICDPhotoSquareImage32BitBGRFormatName = @"FICDPhotoSquareImage32BitBGRFormatName";
NSString *const FICDPhotoSquareImage16BitBGRFormatName = @"FICDPhotoSquareImage16BitBGRFormatName";
NSString *const FICDPhotoSquareImage8BitGrayscaleFormatName = @"FICDPhotoSquareImage8BitGrayscaleFormatName";
NSString *const FICDPhotoPixelImageFormatName = @"FICDPhotoPixelImageFormatName";

CGSize const FICDPhotoSquareImageSize = {75, 75};
Expand Down Expand Up @@ -188,9 +191,22 @@ - (FICEntityImageDrawingBlock)drawingBlockForImage:(UIImage *)image withFormatNa
contextBounds.size = contextSize;
CGContextClearRect(contextRef, contextBounds);

if ([formatName isEqualToString:FICDPhotoSquareImageFormatName]) {
if ([formatName isEqualToString:FICDPhotoPixelImageFormatName]) {
UIImage *statusBarImage = _FICDStatusBarImageFromImage(image);
CGContextSetInterpolationQuality(contextRef, kCGInterpolationMedium);

UIGraphicsPushContext(contextRef);
[statusBarImage drawInRect:contextBounds];
UIGraphicsPopContext();
} else {
if ([formatName isEqualToString:FICDPhotoSquareImage32BitBGRAFormatName] == NO) {
// Fill with white for image formats that are opaque
CGContextSetFillColorWithColor(contextRef, [[UIColor whiteColor] CGColor]);
CGContextFillRect(contextRef, contextBounds);
}

UIImage *squareImage = _FICDSquareImageFromImage(image);

// Clip to a rounded rect
CGPathRef path = _FICDCreateRoundedRectPath(contextBounds, 12);
CGContextAddPath(contextRef, path);
Expand All @@ -200,13 +216,6 @@ - (FICEntityImageDrawingBlock)drawingBlockForImage:(UIImage *)image withFormatNa
UIGraphicsPushContext(contextRef);
[squareImage drawInRect:contextBounds];
UIGraphicsPopContext();
} else if ([formatName isEqualToString:FICDPhotoPixelImageFormatName]) {
UIImage *statusBarImage = _FICDStatusBarImageFromImage(image);
CGContextSetInterpolationQuality(contextRef, kCGInterpolationMedium);

UIGraphicsPushContext(contextRef);
[statusBarImage drawInRect:contextBounds];
UIGraphicsPopContext();
}
};

Expand Down
1 change: 1 addition & 0 deletions FastImageCacheDemo/Classes/FICDPhotosTableViewCell.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@property (nonatomic, weak) id <FICDPhotosTableViewCellDelegate> delegate;
@property (nonatomic, assign) BOOL usesImageTable;
@property (nonatomic, copy) NSArray *photos;
@property (nonatomic, copy) NSString *imageFormatName;

+ (NSString *)reuseIdentifier;
+ (NSInteger)photosPerRow;
Expand Down
10 changes: 6 additions & 4 deletions FastImageCacheDemo/Classes/FICDPhotosTableViewCell.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@

@interface FICDPhotosTableViewCell () <UIGestureRecognizerDelegate> {
__weak id <FICDPhotosTableViewCellDelegate> _delegate;

BOOL _usesImageTable;
NSArray *_photos;
NSString *_imageFormatName;

NSMutableArray *_imageViews;
BOOL _usesImageTable;
UITapGestureRecognizer *_tapGestureRecognizer;
}

Expand All @@ -29,8 +30,9 @@ @interface FICDPhotosTableViewCell () <UIGestureRecognizerDelegate> {
@implementation FICDPhotosTableViewCell

@synthesize delegate = _delegate;
@synthesize photos = _photos;
@synthesize usesImageTable = _usesImageTable;
@synthesize photos = _photos;
@synthesize imageFormatName = _imageFormatName;

#pragma mark - Property Accessors

Expand Down Expand Up @@ -61,7 +63,7 @@ - (void)setPhotos:(NSArray *)photos {
UIImageView *imageView = [_imageViews objectAtIndex:i];

if (_usesImageTable) {
[[FICImageCache sharedImageCache] retrieveImageForEntity:photo withFormatName:FICDPhotoSquareImageFormatName completionBlock:^(id<FICEntity> entity, NSString *formatName, UIImage *image) {
[[FICImageCache sharedImageCache] retrieveImageForEntity:photo withFormatName:_imageFormatName completionBlock:^(id<FICEntity> entity, NSString *formatName, UIImage *image) {
// This completion block may be called much later. We should check to make sure this cell hasn't been reused for different photos before displaying the image that has loaded.
if (photos == [self photos]) {
[imageView setImage:image];
Expand Down
Loading

0 comments on commit 4c7560f

Please sign in to comment.