Kaynağa Gözat

fix: sd_colorAtPoint should early return when pixel format is not supported

DreamPiggy 1 yıl önce
ebeveyn
işleme
09faa3add2

+ 2 - 2
SDWebImage/Core/SDImageCoder.h

@@ -89,9 +89,9 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeUseLazyDec
 FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeScaleDownLimitBytes;
 
 /**
- A Boolean value to provide converting to HDR during decoding.
+ A Boolean value (NSNumber) to provide converting to HDR during decoding. Currently if number is 0, use SDR, else use HDR. But we may extend this option to use `NSUInteger` in the future (means, think this options as int number, but not actual boolean)
  @note Supported by iOS 17 and above when using ImageIO coder (third-party coder can support lower firmware)
- Defaults to NO, decoder will automatically convert SDR.
+ Defaults to @(NO), decoder will automatically convert SDR.
  @note works for `SDImageCoder`
  */
 FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderDecodeToHDR;

+ 2 - 2
SDWebImage/Core/SDWebImageDefine.h

@@ -338,9 +338,9 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageT
 FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageScaleDownLimitBytes;
 
 /**
- A Boolean value to provide converting to HDR during decoding.
+ A Boolean value (NSNumber) to provide converting to HDR during decoding. Currently if number is 0, use SDR, else use HDR. But we may extend this option to use `NSUInteger` in the future (means, think this options as int number, but not actual boolean)
  @note Supported by iOS 17 and above when using ImageIO coder (third-party coder can support lower firmware)
- Defaults to NO, decoder will automatically convert SDR.
+ Defaults to @(NO), decoder will automatically convert SDR.
  */
 FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageDecodeToHDR;
 

+ 1 - 0
SDWebImage/Core/UIImage+Transform.h

@@ -123,6 +123,7 @@ typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
  @note The overhead of object creation means this method is best suited for infrequent color sampling. For heavy image processing, grab the raw bitmap data and process yourself.
 
  @param point The position of pixel
+ @warning This API currently support 8 bits per component only (RGBA8888 etc), not RGBA16U/RGBA10
  @return The color for specify pixel, or nil if any error occur
  */
 - (nullable UIColor *)sd_colorAtPoint:(CGPoint)point;

+ 33 - 9
SDWebImage/Core/UIImage+Transform.m

@@ -149,7 +149,7 @@ static inline UIColor * SDGetColorFromGrayscale(Pixel_88 pixel, CGBitmapInfo bit
 #endif
 }
 
-static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmapInfo, CGColorSpaceRef cgColorSpace) {
+static inline UIColor * SDGetColorFromRGBA8(Pixel_8888 pixel, CGBitmapInfo bitmapInfo, CGColorSpaceRef cgColorSpace) {
     // Get alpha info, byteOrder info
     CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
     CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
@@ -752,6 +752,19 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
         return nil;
     }
     
+    // Check pixel format
+    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
+    if (@available(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) {
+        CGImagePixelFormatInfo pixelFormat = (bitmapInfo & kCGImagePixelFormatMask);
+        if (pixelFormat != kCGImagePixelFormatPacked || bitsPerComponent > 8) {
+            // like RGBA1010102, need bitwise to extract pixel from single uint32_t, we don't support currently
+            SD_LOG("Unsupported pixel format: %u, bpc: %zu for CGImage: %@", pixelFormat, bitsPerComponent, imageRef);
+            CGImageRelease(imageRef);
+            return nil;
+        }
+    }
+    
     // Get pixels
     CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
     if (!provider) {
@@ -766,8 +779,7 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
     
     // Get pixel at point
     size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
-    size_t components = CGImageGetBitsPerPixel(imageRef) / CGImageGetBitsPerComponent(imageRef);
-    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
+    size_t components = CGImageGetBitsPerPixel(imageRef) / bitsPerComponent;
     
     CFRange range = CFRangeMake(bytesPerRow * y + components * x, components);
     if (CFDataGetLength(data) < range.location + range.length) {
@@ -793,7 +805,7 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
         CFRelease(data);
         CGImageRelease(imageRef);
         // Convert to color
-        return SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
+        return SDGetColorFromRGBA8(pixel, bitmapInfo, colorSpace);
     } else {
         SD_LOG("Unsupported components: %zu", components);
         CFRelease(data);
@@ -826,6 +838,19 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
         return nil;
     }
     
+    // Check pixel format
+    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
+    if (@available(iOS 12.0, tvOS 12.0, macOS 10.14, watchOS 5.0, *)) {
+        CGImagePixelFormatInfo pixelFormat = (bitmapInfo & kCGImagePixelFormatMask);
+        if (pixelFormat != kCGImagePixelFormatPacked || bitsPerComponent > 8) {
+            // like RGBA1010102, need bitwise to extract pixel from single uint32_t, we don't support currently
+            SD_LOG("Unsupported pixel format: %u, bpc: %zu for CGImage: %@", pixelFormat, bitsPerComponent, imageRef);
+            CGImageRelease(imageRef);
+            return nil;
+        }
+    }
+    
     // Get pixels
     CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
     if (!provider) {
@@ -840,7 +865,7 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
     
     // Get pixels with rect
     size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
-    size_t components = CGImageGetBitsPerPixel(imageRef) / CGImageGetBitsPerComponent(imageRef);
+    size_t components = CGImageGetBitsPerPixel(imageRef) / bitsPerComponent;
     
     size_t start = bytesPerRow * CGRectGetMinY(rect) + components * CGRectGetMinX(rect);
     size_t end = bytesPerRow * (CGRectGetMaxY(rect) - 1) + components * CGRectGetMaxX(rect);
@@ -855,7 +880,6 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
     size_t col = CGRectGetMaxX(rect);
     
     // Convert to color
-    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
     NSMutableArray<UIColor *> *colors = [NSMutableArray arrayWithCapacity:CGRectGetWidth(rect) * CGRectGetHeight(rect)];
     // ColorSpace
     CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
@@ -874,12 +898,12 @@ static NSString * _Nullable SDGetCIFilterNameFromBlendMode(CGBlendMode blendMode
         } else {
             if (components == 3) {
                 Pixel_8888 pixel = {pixels[index], pixels[index+1], pixels[index+2], 0};
-                color = SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
+                color = SDGetColorFromRGBA8(pixel, bitmapInfo, colorSpace);
             } else if (components == 4) {
                 Pixel_8888 pixel = {pixels[index], pixels[index+1], pixels[index+2], pixels[index+3]};
-                color = SDGetColorFromRGBA(pixel, bitmapInfo, colorSpace);
+                color = SDGetColorFromRGBA8(pixel, bitmapInfo, colorSpace);
             } else {
-                SD_LOG("Unsupported components: %zu", components);
+                SD_LOG("Unsupported components: %zu for CGImage: %@", components, imageRef);
             }
         }
         if (color) {