From 9efacfed97988a93fb158c8891308044e883b9c1 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 15 Nov 2023 17:46:53 +0800 Subject: [PATCH] Fix the sd_colorAtPoint return wrong value on pre-multiplied CGImage Should always un-multiplied the color to match CGColor/UIColor documentation --- SDWebImage/Core/UIImage+Transform.h | 2 + SDWebImage/Core/UIImage+Transform.m | 68 +++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/SDWebImage/Core/UIImage+Transform.h b/SDWebImage/Core/UIImage+Transform.h index 699def7a..60b488ae 100644 --- a/SDWebImage/Core/UIImage+Transform.h +++ b/SDWebImage/Core/UIImage+Transform.h @@ -107,6 +107,7 @@ typedef NS_OPTIONS(NSUInteger, SDRectCorner) { /** Return the pixel color at specify position. The point is from the top-left to the bottom-right and 0-based. The returned the color is always be RGBA format. The image must be CG-based. + @note The point's x/y will be converted into integer. @note The point's x/y should not be smaller than 0, or greater than or equal to width/height. @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. @@ -117,6 +118,7 @@ typedef NS_OPTIONS(NSUInteger, SDRectCorner) { /** Return the pixel color array with specify rectangle. The rect is from the top-left to the bottom-right and 0-based. The returned the color is always be RGBA format. The image must be CG-based. + @note The rect's origin and size will be converted into integer. @note The rect's width/height should not be smaller than or equal to 0. The minX/minY should not be smaller than 0. The maxX/maxY should not be greater than width/height. Attention this limit is different from `sd_colorAtPoint:` (point: (0, 0) like rect: (0, 0, 1, 1)) @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. diff --git a/SDWebImage/Core/UIImage+Transform.m b/SDWebImage/Core/UIImage+Transform.m index 3ce0c97d..cbfd14e5 100644 --- a/SDWebImage/Core/UIImage+Transform.m +++ b/SDWebImage/Core/UIImage+Transform.m @@ -169,7 +169,32 @@ static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmap default: break; } switch (alphaInfo) { - case kCGImageAlphaPremultipliedFirst: + case kCGImageAlphaPremultipliedFirst: { + if (byteOrderNormal) { + // ARGB8888-premultiplied + a = pixel[0] / 255.0; + r = pixel[1] / 255.0; + g = pixel[2] / 255.0; + b = pixel[3] / 255.0; + if (a > 0) { + r /= a; + g /= a; + b /= a; + } + } else { + // BGRA8888-premultiplied + b = pixel[0] / 255.0; + g = pixel[1] / 255.0; + r = pixel[2] / 255.0; + a = pixel[3] / 255.0; + if (a > 0) { + r /= a; + g /= a; + b /= a; + } + } + break; + } case kCGImageAlphaFirst: { if (byteOrderNormal) { // ARGB8888 @@ -186,7 +211,32 @@ static inline UIColor * SDGetColorFromRGBA(Pixel_8888 pixel, CGBitmapInfo bitmap } } break; - case kCGImageAlphaPremultipliedLast: + case kCGImageAlphaPremultipliedLast: { + if (byteOrderNormal) { + // RGBA8888-premultiplied + r = pixel[0] / 255.0; + g = pixel[1] / 255.0; + b = pixel[2] / 255.0; + a = pixel[3] / 255.0; + if (a > 0) { + r /= a; + g /= a; + b /= a; + } + } else { + // ABGR8888-premultiplied + a = pixel[0] / 255.0; + b = pixel[1] / 255.0; + g = pixel[2] / 255.0; + r = pixel[3] / 255.0; + if (a > 0) { + r /= a; + g /= a; + b /= a; + } + } + break; + } case kCGImageAlphaLast: { if (byteOrderNormal) { // RGBA8888 @@ -546,9 +596,11 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull } // Check point - CGFloat width = CGImageGetWidth(imageRef); - CGFloat height = CGImageGetHeight(imageRef); - if (point.x < 0 || point.y < 0 || point.x >= width || point.y >= height) { + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + size_t x = point.x; + size_t y = point.y; + if (x < 0 || y < 0 || x >= width || y >= height) { CGImageRelease(imageRef); return nil; } @@ -570,7 +622,7 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull size_t components = CGImageGetBitsPerPixel(imageRef) / CGImageGetBitsPerComponent(imageRef); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); - CFRange range = CFRangeMake(bytesPerRow * point.y + components * point.x, components); + CFRange range = CFRangeMake(bytesPerRow * y + components * x, components); if (CFDataGetLength(data) < range.location + range.length) { CFRelease(data); CGImageRelease(imageRef); @@ -620,8 +672,8 @@ static inline CGImageRef _Nullable SDCreateCGImageFromCIImage(CIImage * _Nonnull } // Check rect - CGFloat width = CGImageGetWidth(imageRef); - CGFloat height = CGImageGetHeight(imageRef); + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); if (CGRectGetWidth(rect) <= 0 || CGRectGetHeight(rect) <= 0 || CGRectGetMinX(rect) < 0 || CGRectGetMinY(rect) < 0 || CGRectGetMaxX(rect) > width || CGRectGetMaxY(rect) > height) { CGImageRelease(imageRef); return nil;