Add option to scale down large images on iOS

Option is SDWebImageScaleDownLargeImage
This commit is contained in:
gsempe 2014-06-18 20:02:30 +02:00
parent f6fdaeb6ae
commit f0732d704c
6 changed files with 75 additions and 13 deletions

View File

@ -15,4 +15,6 @@
+ (UIImage *)decodedImageWithImage:(UIImage *)image; + (UIImage *)decodedImageWithImage:(UIImage *)image;
+ (UIImage *)decodedAndScaledDownImageWithImage:(UIImage *)image;
@end @end

View File

@ -10,9 +10,30 @@
#import "SDWebImageDecoder.h" #import "SDWebImageDecoder.h"
/*
Size in MB, compatible with all iOS devices.
*/
#define kSDWebImageDecoderMaxImageSizeMB 4.f
#define SDWebImageDecoderMaxTotalPixels(bitsPerComponent) ((kSDWebImageDecoderMaxImageSizeMB*1024.*1024.*8.)/bitsPerComponent)
inline static CGSize SDWebImageDecoderConstrainedSize(UIImage *image) {
CGImageRef imageRef = image.CGImage;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
size_t imageBitsPerComponent = CGImageGetBitsPerComponent(imageRef);
CGFloat imageTotalPixels = imageSize.width * imageSize.height;
if (imageTotalPixels < SDWebImageDecoderMaxTotalPixels(imageBitsPerComponent)) {
return CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
}
CGFloat ratio = SDWebImageDecoderMaxTotalPixels(imageBitsPerComponent) / imageTotalPixels;
CGFloat maxWidth = imageSize.width * ratio;
CGFloat maxHeight = imageSize.height *ratio;
return CGSizeMake(floorf(maxWidth), floorf(maxHeight));
}
@implementation UIImage (ForceDecode) @implementation UIImage (ForceDecode)
+ (UIImage *)decodedImageWithImage:(UIImage *)image { + (UIImage *)decodedAndScaledDownImageToSize:(CGSize)size withImage:(UIImage *)image {
if (image.images) { if (image.images) {
// Do not decode animated images // Do not decode animated images
return image; return image;
@ -20,15 +41,20 @@
CGImageRef imageRef = image.CGImage; CGImageRef imageRef = image.CGImage;
CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
if ((size.width < imageSize.width) && (size.height < imageSize.height)) {
imageSize = size;
}
CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask); int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
infoMask == kCGImageAlphaNoneSkipFirst || infoMask == kCGImageAlphaNoneSkipFirst ||
infoMask == kCGImageAlphaNoneSkipLast); infoMask == kCGImageAlphaNoneSkipLast);
// CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB. // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
// https://developer.apple.com/library/mac/#qa/qa1037/_index.html // https://developer.apple.com/library/mac/#qa/qa1037/_index.html
@ -39,7 +65,7 @@
// Set noneSkipFirst. // Set noneSkipFirst.
bitmapInfo |= kCGImageAlphaNoneSkipFirst; bitmapInfo |= kCGImageAlphaNoneSkipFirst;
} }
// Some PNGs tell us they have alpha but only 3 components. Odd. // Some PNGs tell us they have alpha but only 3 components. Odd.
else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) { else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
// Unset the old alpha info. // Unset the old alpha info.
bitmapInfo &= ~kCGBitmapAlphaInfoMask; bitmapInfo &= ~kCGBitmapAlphaInfoMask;
@ -48,17 +74,18 @@
// It calculates the bytes-per-row based on the bitsPerComponent and width arguments. // It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
CGContextRef context = CGBitmapContextCreate(NULL, CGContextRef context = CGBitmapContextCreate(NULL,
imageSize.width, imageSize.width,
imageSize.height, imageSize.height,
CGImageGetBitsPerComponent(imageRef), CGImageGetBitsPerComponent(imageRef),
0, 0,
colorSpace, colorSpace,
bitmapInfo); bitmapInfo);
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
// If failed, return undecompressed image // If failed, return undecompressed image
if (!context) return image; if (!context) return image;
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextDrawImage(context, imageRect, imageRef); CGContextDrawImage(context, imageRect, imageRef);
CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
@ -69,4 +96,12 @@
return decompressedImage; return decompressedImage;
} }
+ (UIImage *)decodedImageWithImage:(UIImage *)image {
return [UIImage decodedAndScaledDownImageToSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) withImage:image];
}
+ (UIImage *)decodedAndScaledDownImageWithImage:(UIImage *)image {
return [UIImage decodedAndScaledDownImageToSize:SDWebImageDecoderConstrainedSize(image) withImage:image];
}
@end @end

View File

@ -50,7 +50,11 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
*/ */
SDWebImageDownloaderHighPriority = 1 << 7, SDWebImageDownloaderHighPriority = 1 << 7,
/**
* Scale down the image
*/
SDWebImageDownloaderScaleDownLargeImage = 1 << 8,
}; };
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {

View File

@ -357,7 +357,15 @@
if (!image.images) // Do not force decod animated GIFs if (!image.images) // Do not force decod animated GIFs
{ {
#ifdef TARGET_OS_IPHONE
if (self.options & SDWebImageDownloaderScaleDownLargeImage) {
image = [UIImage decodedAndScaledDownImageWithImage:image];
} else {
image = [UIImage decodedImageWithImage:image];
}
#else
image = [UIImage decodedImageWithImage:image]; image = [UIImage decodedImageWithImage:image];
#endif
} }
if (CGSizeEqualToSize(image.size, CGSizeZero)) { if (CGSizeEqualToSize(image.size, CGSizeZero)) {

View File

@ -74,7 +74,15 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
* By default, placeholder images are loaded while the image is loading. This flag will delay the loading * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
* of the placeholder image until after the image has finished loading. * of the placeholder image until after the image has finished loading.
*/ */
SDWebImageDelayPlaceholder = 1 << 9 SDWebImageDelayPlaceholder = 1 << 9,
/**
* By default, images are decoded respecting their original size. On iOS, this flag will scale down the
* images to a size compatible with the constrained memory of devices.
* If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated.
*/
SDWebImageScaleDownLargeImage = 1 << 10
}; };
typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType); typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType);

View File

@ -134,12 +134,17 @@
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImage) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImage;
if (image && options & SDWebImageRefreshCached) { if (image && options & SDWebImageRefreshCached) {
// force progressive off if image already cached but forced refreshing // force progressive off if image already cached but forced refreshing
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing // ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
} }
if (options & SDWebImageProgressiveDownload) {
// Progressive download deactivate scale down
downloaderOptions &= ~SDWebImageDownloaderScaleDownLargeImage;
}
id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
if (weakOperation.isCancelled) { if (weakOperation.isCancelled) {
dispatch_main_sync_safe(^{ dispatch_main_sync_safe(^{