Parcourir la source

Update for #787 Add an option to scale down large images on iOS - code cleanup

Bogdan Poplauschi il y a 9 ans
Parent
commit
00bf467
1 fichiers modifiés avec 96 ajouts et 104 suppressions
  1. 96 104
      SDWebImage/SDWebImageDecoder.m

+ 96 - 104
SDWebImage/SDWebImageDecoder.m

@@ -9,56 +9,28 @@
 
 #import "SDWebImageDecoder.h"
 
+@implementation UIImage (ForceDecode)
+
 #if SD_UIKIT || SD_WATCH
 static const size_t kBytesPerPixel = 4;
 static const size_t kBitsPerComponent = 8;
-#endif
-
-
-@implementation UIImage (ForceDecode)
 
 + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
-    if (image == nil) { return nil; } // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
-    
-#if SD_MAC
-    return image;
-#elif SD_UIKIT || SD_WATCH
+    if (![UIImage shouldDecodeImage:image]) {
+        return image;
+    }
     
-    // while downloading huge amount of images
-    // autorelease the bitmap context
-    // and all vars to help system to free memory
-    // when there are memory warning.
-    // on iOS7, do not forget to call
-    // [[SDImageCache sharedImageCache] clearMemory];
+    // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.
+    // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];
     @autoreleasepool{
-        if (image.images != nil) { return image; } // do not decode animated images
         
         CGImageRef imageRef = image.CGImage;
-        
-        CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
-        BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
-                         alpha == kCGImageAlphaLast ||
-                         alpha == kCGImageAlphaPremultipliedFirst ||
-                         alpha == kCGImageAlphaPremultipliedLast);
-        if (anyAlpha) { return image; } // do not decode images with alpha
-        
-        // current
-        CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
-        CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
-        
-        BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
-                                      imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
-                                      imageColorSpaceModel == kCGColorSpaceModelCMYK ||
-                                      imageColorSpaceModel == kCGColorSpaceModelIndexed);
-        if (unsupportedColorSpace) {
-            colorspaceRef = CGColorSpaceCreateDeviceRGB();
-        }
+        CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
         
         size_t width = CGImageGetWidth(imageRef);
         size_t height = CGImageGetHeight(imageRef);
         size_t bytesPerRow = kBytesPerPixel * width;
 
-
         // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
         // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
         // to create bitmap graphics contexts without alpha info.
@@ -77,16 +49,11 @@ static const size_t kBitsPerComponent = 8;
                                                          scale:image.scale
                                                    orientation:image.imageOrientation];
         
-        if (unsupportedColorSpace) {
-            CGColorSpaceRelease(colorspaceRef);
-        }
-        
         CGContextRelease(context);
         CGImageRelease(imageRefWithoutAlpha);
         
         return imageWithoutAlpha;
     }
-#endif
 }
 
 /*
@@ -95,7 +62,7 @@ static const size_t kBitsPerComponent = 8;
  * Suggested value for iPad2 and iPhone 4: 120.
  * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30.
  */
-#define kDestImageSizeMB 60.0f
+static const CGFloat kDestImageSizeMB = 60.0f;
 
 /*
  * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set
@@ -103,47 +70,29 @@ static const size_t kBitsPerComponent = 8;
  * Suggested value for iPad2 and iPhone 4: 40.
  * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10.
  */
-#define kSourceImageTileSizeMB 20.0f
+static const CGFloat kSourceImageTileSizeMB = 20.0f;
+
+static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
+static const CGFloat kPixelsPerMB = kBytesPerMB * kBytesPerPixel;
+static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
+static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
 
-#define bytesPerMB 1048576.0f
-#define pixelsPerMB ( bytesPerMB / kBytesPerPixel )
-#define destTotalPixels kDestImageSizeMB * pixelsPerMB
-#define tileTotalPixels kSourceImageTileSizeMB * pixelsPerMB
-#define destSeemOverlap 2.0f // the numbers of pixels to overlap the seems where tiles meet.
+static const CGFloat kDestSeemOverlap = 2.0f;   // the numbers of pixels to overlap the seems where tiles meet.
 
 + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image {
-    if (image == nil) { return nil; } // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
-    
-#if SD_MAC
-    return image;
-#elif SD_UIKIT || SD_WATCH
-    
-    BOOL shouldDownSize = ({
-        BOOL shouldDoIt = YES;
-        
-        CGImageRef sourceImageRef = image.CGImage;
-        CGSize sourceResolution = CGSizeZero;
-        sourceResolution.width = CGImageGetWidth(sourceImageRef);
-        sourceResolution.height = CGImageGetHeight(sourceImageRef);
-        float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
-        float imageScale = destTotalPixels / sourceTotalPixels;
-        if (imageScale < 1) {
-            shouldDoIt = YES;
-        } else {
-            shouldDoIt = NO;
-        }
-        shouldDoIt;
-    });
+    if (![UIImage shouldDecodeImage:image]) {
+        return image;
+    }
     
-    if (!shouldDownSize) {
+    if (![UIImage shouldScaleDownImage:image]) {
         return [UIImage decodedImageWithImage:image];
     }
     
     CGContextRef destContext;
     
+    // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.
+    // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];
     @autoreleasepool {
-        if (image.images != nil) { return image; } // do not decode animated images
-        
         CGImageRef sourceImageRef = image.CGImage;
         
         CGSize sourceResolution = CGSizeZero;
@@ -153,42 +102,22 @@ static const size_t kBitsPerComponent = 8;
         // Determine the scale ratio to apply to the input image
         // that results in an output image of the defined size.
         // see kDestImageSizeMB, and how it relates to destTotalPixels.
-        float imageScale = destTotalPixels / sourceTotalPixels;
+        float imageScale = kDestTotalPixels / sourceTotalPixels;
         CGSize destResolution = CGSizeZero;
         destResolution.width = (int)(sourceResolution.width*imageScale);
         destResolution.height = (int)(sourceResolution.height*imageScale);
         
-        CGImageAlphaInfo alpha = CGImageGetAlphaInfo(sourceImageRef);
-        BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
-                         alpha == kCGImageAlphaLast ||
-                         alpha == kCGImageAlphaPremultipliedFirst ||
-                         alpha == kCGImageAlphaPremultipliedLast);
-        if (anyAlpha) { return image; }  // do not decode images with alpha
-        
         // current color space
-        CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(sourceImageRef));
-        CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(sourceImageRef);
-        
-        BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
-                                      imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
-                                      imageColorSpaceModel == kCGColorSpaceModelCMYK ||
-                                      imageColorSpaceModel == kCGColorSpaceModelIndexed);
-        if (unsupportedColorSpace) {
-            colorspaceRef = CGColorSpaceCreateDeviceRGB();
-        }
+        CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:sourceImageRef];
         
         size_t bytesPerRow = kBytesPerPixel * destResolution.width;
         
         // Allocate enough pixel data to hold the output image.
         void* destBitmapData = malloc( bytesPerRow * destResolution.height );
         if (destBitmapData == NULL) {
-            if (unsupportedColorSpace) {
-                CGColorSpaceRelease(colorspaceRef);
-            }
             return image;
         }
         
-        
         // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
         // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
         // to create bitmap graphics contexts without alpha info.
@@ -200,10 +129,6 @@ static const size_t kBitsPerComponent = 8;
                                             colorspaceRef,
                                             kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
         
-        if (unsupportedColorSpace) {
-            CGColorSpaceRelease(colorspaceRef);
-        }
-        
         if (destContext == NULL) {
             free( destBitmapData );
             return image;
@@ -224,7 +149,7 @@ static const size_t kBitsPerComponent = 8;
         // The source tile height is dynamic. Since we specified the size
         // of the source tile in MB, see how many rows of pixels high it
         // can be given the input image width.
-        sourceTile.size.height = (int)( tileTotalPixels / sourceTile.size.width );
+        sourceTile.size.height = (int)(kTileTotalPixels / sourceTile.size.width );
         sourceTile.origin.x = 0.0f;
         // The output tile is the same proportions as the input tile, but
         // scaled to image scale.
@@ -234,7 +159,7 @@ static const size_t kBitsPerComponent = 8;
         destTile.origin.x = 0.0f;
         // The source seem overlap is proportionate to the destination seem overlap.
         // this is the amount of pixels to overlap each tile as we assemble the ouput image.
-        float sourceSeemOverlap = (int)((destSeemOverlap/destResolution.height)*sourceResolution.height);
+        float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);
         CGImageRef sourceTileImageRef;
         // calculate the number of read/write operations required to assemble the
         // output image.
@@ -248,11 +173,11 @@ static const size_t kBitsPerComponent = 8;
         // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations.
         float sourceTileHeightMinusOverlap = sourceTile.size.height;
         sourceTile.size.height += sourceSeemOverlap;
-        destTile.size.height += destSeemOverlap;
+        destTile.size.height += kDestSeemOverlap;
         for( int y = 0; y < iterations; ++y ) {
             @autoreleasepool {
                 sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
-                destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + destSeemOverlap);
+                destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap);
                 sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile );
                 if( y == iterations - 1 && remainder ) {
                     float dify = destTile.size.height;
@@ -277,8 +202,75 @@ static const size_t kBitsPerComponent = 8;
         }
         return destImage;
     }
-#endif
 }
 
++ (BOOL)shouldDecodeImage:(nullable UIImage *)image {
+    // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
+    if (image == nil) {
+        return NO;
+    }
+
+    // do not decode animated images
+    if (image.images != nil) {
+        return NO;
+    }
+    
+    CGImageRef imageRef = image.CGImage;
+    
+    CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
+    BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
+                     alpha == kCGImageAlphaLast ||
+                     alpha == kCGImageAlphaPremultipliedFirst ||
+                     alpha == kCGImageAlphaPremultipliedLast);
+    // do not decode images with alpha
+    if (anyAlpha) {
+        return NO;
+    }
+    
+    return YES;
+}
+
++ (BOOL)shouldScaleDownImage:(nonnull UIImage *)image {
+    BOOL shouldScaleDown = YES;
+        
+    CGImageRef sourceImageRef = image.CGImage;
+    CGSize sourceResolution = CGSizeZero;
+    sourceResolution.width = CGImageGetWidth(sourceImageRef);
+    sourceResolution.height = CGImageGetHeight(sourceImageRef);
+    float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
+    float imageScale = kDestTotalPixels / sourceTotalPixels;
+    if (imageScale < 1) {
+        shouldScaleDown = YES;
+    } else {
+        shouldScaleDown = NO;
+    }
+    
+    return shouldScaleDown;
+}
+
++ (CGColorSpaceRef)colorSpaceForImageRef:(CGImageRef)imageRef {
+    // current
+    CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
+    CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
+    
+    BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
+                                  imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
+                                  imageColorSpaceModel == kCGColorSpaceModelCMYK ||
+                                  imageColorSpaceModel == kCGColorSpaceModelIndexed);
+    if (unsupportedColorSpace) {
+        colorspaceRef = CGColorSpaceCreateDeviceRGB();
+        CFAutorelease(colorspaceRef);
+    }
+    return colorspaceRef;
+}
+#elif SD_MAC
++ (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
+    return image;
+}
+
++ (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image {
+    return image;
+}
+#endif
 
 @end