Add SDWebImageOption for converting images to grayscale #284

Closed
wants to merge 5 commits into
from
@@ -67,6 +67,8 @@
5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 57B7420516B75D52004199BE /* UIImage+Grayscale.h in Headers */ = {isa = PBXBuildFile; fileRef = 57B7420316B75D52004199BE /* UIImage+Grayscale.h */; };
+ 57B7420616B75D52004199BE /* UIImage+Grayscale.m in Sources */ = {isa = PBXBuildFile; fileRef = 57B7420416B75D52004199BE /* UIImage+Grayscale.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -106,6 +108,8 @@
53922D96148C56230056699D /* UIImageView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WebCache.m"; path = "SDWebImage/UIImageView+WebCache.m"; sourceTree = SOURCE_ROOT; };
53FB893F14D35D1A0020B787 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
53FB894814D35E9E0020B787 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 57B7420316B75D52004199BE /* UIImage+Grayscale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Grayscale.h"; sourceTree = "<group>"; };
+ 57B7420416B75D52004199BE /* UIImage+Grayscale.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Grayscale.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -184,6 +188,8 @@
53922D94148C56230056699D /* UIButton+WebCache.m */,
53922D95148C56230056699D /* UIImageView+WebCache.h */,
53922D96148C56230056699D /* UIImageView+WebCache.m */,
+ 57B7420316B75D52004199BE /* UIImage+Grayscale.h */,
+ 57B7420416B75D52004199BE /* UIImage+Grayscale.m */,
);
name = Categories;
sourceTree = "<group>";
@@ -256,6 +262,7 @@
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */,
530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */,
530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */,
+ 57B7420516B75D52004199BE /* UIImage+Grayscale.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -386,6 +393,7 @@
5376130F155AD0D5005750A4 /* UIImageView+WebCache.m in Sources */,
530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */,
53406750167780C40042B59E /* SDWebImageCompat.m in Sources */,
+ 57B7420616B75D52004199BE /* UIImage+Grayscale.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -13,7 +13,8 @@
typedef enum
{
SDWebImageDownloaderLowPriority = 1 << 0,
- SDWebImageDownloaderProgressiveDownload = 1 << 1
+ SDWebImageDownloaderProgressiveDownload = 1 << 1,
+ SDWebImageDownloaderGrayscale = 1 << 2
} SDWebImageDownloaderOptions;
extern NSString *const SDWebImageDownloadStartNotification;
@@ -9,6 +9,7 @@
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageDecoder.h"
#import <ImageIO/ImageIO.h>
+#import "UIImage+Grayscale.h"
@interface SDWebImageDownloaderOperation ()
@@ -276,6 +277,15 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection
dispatch_async(self.queue, ^
{
UIImage *image = [UIImage decodedImageWithImage:SDScaledImageForPath(self.request.URL.absoluteString, self.imageData)];
+
+ if (self.options & SDWebImageDownloaderGrayscale)
+ {
+ image = [image grayscaleImage];
+ // invalidate downloaded data so that it's generated again with the grayscale version
+ // this is to make sure that's the version that gets cached
+ self.imageData = nil;
+ }
+
dispatch_async(dispatch_get_main_queue(), ^
{
if (CGSizeEqualToSize(image.size, CGSizeZero))
@@ -31,7 +31,11 @@ typedef enum
* This flag enables progressive download, the image is displayed progressively during download as a browser would do.
* By default, the image is only displayed once completely downloaded.
*/
- SDWebImageProgressiveDownload = 1 << 3
+ SDWebImageProgressiveDownload = 1 << 3,
+ /**
+ * By using this flag, the downloaded image will be converted to Grayscale
+ */
+ SDWebImageGrayscale = 1 << 4
} SDWebImageOptions;
typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType);
@@ -71,13 +75,13 @@ typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *err
* The following example sets a filter in the application delegate that will remove any query-string from the
* URL before to use it as a cache key:
*
- * [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url)
+ * [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url, BOOL grayscale)
* {
* url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
* return [url absoluteString];
* }];
*/
-@property (strong) NSString *(^cacheKeyFilter)(NSURL *url);
+@property (strong) NSString *(^cacheKeyFilter)(NSURL *url, BOOL grayscale);
/**
* Returns global SDWebImageManager instance.
@@ -48,15 +48,15 @@ - (id)init
}
-- (NSString *)cacheKeyForURL:(NSURL *)url
+- (NSString *)cacheKeyForURL:(NSURL *)url grayscale:(BOOL)grayscale
{
if (self.cacheKeyFilter)
{
- return self.cacheKeyFilter(url);
+ return self.cacheKeyFilter(url, grayscale);
}
else
{
- return [url absoluteString];
+ return [[url absoluteString] stringByAppendingString:(grayscale) ? @"_grayscale" : @""];
}
}
@@ -82,10 +82,12 @@ - (NSString *)cacheKeyForURL:(NSURL *)url
if (completedBlock) completedBlock(nil, nil, SDImageCacheTypeNone, NO);
return operation;
}
-
+
+ const BOOL isGrayscale = (options & SDWebImageGrayscale);
+
[self.runningOperations addObject:operation];
- NSString *key = [self cacheKeyForURL:url];
-
+ NSString *key = [self cacheKeyForURL:url grayscale:isGrayscale];
+
[self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType)
{
if (operation.isCancelled) return;
@@ -100,6 +102,7 @@ - (NSString *)cacheKeyForURL:(NSURL *)url
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
+ if (isGrayscale) downloaderOptions |= SDWebImageDownloaderGrayscale;
__block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)
{
completedBlock(downloadedImage, error, SDImageCacheTypeNone, finished);
@@ -0,0 +1,15 @@
+//
+// UIImage+Grayscale.h
+// SDWebImage
+//
+// Created by Nacho Soto on 1/28/13.
+// Copyright (c) 2013 Dailymotion. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIImage (Grayscale)
+
+- (instancetype)grayscaleImage;
+
+@end
@@ -0,0 +1,76 @@
+//
+// UIImage+Grayscale.m
+// SDWebImage
+//
+// Created by Nacho Soto on 1/28/13.
+// Copyright (c) 2013 Dailymotion. All rights reserved.
+//
+
+#import "UIImage+Grayscale.h"
+
+typedef enum {
+ ALPHA = 0,
+ BLUE = 1,
+ GREEN = 2,
+ RED = 3
+} PIXELS;
+
+@implementation UIImage (Grayscale)
+
+- (instancetype)grayscaleImage
+{
+ /**
+ * reference: http://stackoverflow.com/questions/1298867/convert-image-to-grayscale
+ */
+ CGSize size = self.size;
+ const int scale = self.scale;
+ const int width = size.width * scale,
+ height = size.height * scale;
+
+ // the pixels will be painted to this array
+ uint32_t *const pixels = (uint32_t *)malloc(width * height * sizeof(uint32_t));
+
+ // clear the pixels so any transparency is preserved
+ memset(pixels, 0, width * height * sizeof(uint32_t));
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+
+ // create a context with RGBA pixels
+ CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace,
+ kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
+
+ // paint the bitmap to our context which will fill in the pixels array
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
+
+ for(int y = 0; y < height; y++) {
+ for(int x = 0; x < width; x++) {
+ uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x];
+
+ // convert to grayscale using recommended method: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
+ uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];
+
+ // set the pixels to gray
+ rgbaPixel[RED] = gray;
+ rgbaPixel[GREEN] = gray;
+ rgbaPixel[BLUE] = gray;
+ }
+ }
+
+ // create a new CGImageRef from our context with the modified pixels
+ CGImageRef image = CGBitmapContextCreateImage(context);
+
+ // we're done with the context, color space, and pixels
+ CGContextRelease(context);
+ CGColorSpaceRelease(colorSpace);
+ free(pixels);
+
+ // make a new UIImage to return
+ UIImage *resultUIImage = [UIImage imageWithCGImage:image];
+
+ // we're done with image now too
+ CGImageRelease(image);
+
+ return resultUIImage;
+}
+
+@end