Provide the API for custom coders (like WebPCoder) to use runtime check to handle the decode output bitmap info
Include: colorspace, byte alignment, bitmap info Remove the exists hardcode on BGRA8888
This commit is contained in:
parent
f5f27a9e01
commit
b4559994ea
|
@ -20,6 +20,14 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
|
|||
SDImageCoderDecodeSolutionUIKit
|
||||
};
|
||||
|
||||
/// Byte alignment the bytes size with alignment
|
||||
/// - Parameters:
|
||||
/// - size: The bytes size
|
||||
/// - alignment: The alignment, in bytes
|
||||
static inline size_t SDByteAlign(size_t size, size_t alignment) {
|
||||
return ((size + (alignment - 1)) / alignment) * alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
Provide some common helper methods for building the image decoder/encoder.
|
||||
*/
|
||||
|
@ -45,16 +53,32 @@ typedef NS_ENUM(NSUInteger, SDImageCoderDecodeSolution) {
|
|||
*/
|
||||
+ (NSArray<SDImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage NS_SWIFT_NAME(frames(from:));
|
||||
|
||||
#pragma mark - Preferred Rendering Format
|
||||
/// For coders who use `CGImageCreate`, use the information below to create an effient CGImage which can be render on GPU without Core Animation's extra copy (`CA::Render::copy_image`), which can be debugged using `Color Copied Image` in Xcode Instruments
|
||||
/// `CGImageCreate`'s `bytesPerRow`, `space`, `bitmapInfo` params should use the information below.
|
||||
/**
|
||||
Return the shared device-dependent RGB color space. This follows The Get Rule.
|
||||
On iOS, it's created with deviceRGB (if available, use sRGB).
|
||||
On macOS, it's from the screen colorspace (if failed, use deviceRGB)
|
||||
Because it's shared, you should not retain or release this object.
|
||||
Typically is sRGB, the color space defaults to rendering on Apple's devices.
|
||||
|
||||
@return The device-dependent RGB color space
|
||||
*/
|
||||
+ (CGColorSpaceRef _Nonnull)colorSpaceGetDeviceRGB CF_RETURNS_NOT_RETAINED;
|
||||
|
||||
/**
|
||||
From v5.17.0, this returns the byte alignment used for bytesPerRow(stride) **Preferred from current hardward && OS using runtime detection**
|
||||
Typically is 64, the system page size.
|
||||
@note To calculate the bytesPerRow, use the formula `SDByteAlign(width * (bitsPerPixel / 8), alignment)`
|
||||
*/
|
||||
+ (size_t)preferredByteAlignment;
|
||||
|
||||
/**
|
||||
From v5.17.0, this returns the bitmap info **Preferred from current hardward && OS using runtime detection**
|
||||
Typically is pre-multiplied RGBA8888 for alpha image, RGBX8888 for non-alpha image.
|
||||
@param containsAlpha Whether the image to render contains alpha channel
|
||||
*/
|
||||
+ (CGBitmapInfo)preferredBitmapInfo:(BOOL)containsAlpha;
|
||||
|
||||
/**
|
||||
Check whether CGImage contains alpha channel.
|
||||
|
||||
|
|
|
@ -19,10 +19,6 @@
|
|||
#import "SDInternalMacros.h"
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
static inline size_t SDByteAlign(size_t size, size_t alignment) {
|
||||
return ((size + (alignment - 1)) / alignment) * alignment;
|
||||
}
|
||||
|
||||
#if SD_UIKIT
|
||||
static inline UIImage *SDImageDecodeUIKit(UIImage *image) {
|
||||
// See: https://developer.apple.com/documentation/uikit/uiimage/3750834-imagebypreparingfordisplay
|
||||
|
@ -72,6 +68,42 @@ static inline BOOL SDImageSupportsHardwareHEVCDecoder(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static UIImage * _Nonnull SDImageGetAlphaDummyImage(void) {
|
||||
static dispatch_once_t onceToken;
|
||||
static UIImage *dummyImage;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SDGraphicsImageRendererFormat *format = [SDGraphicsImageRendererFormat preferredFormat];
|
||||
format.scale = 1;
|
||||
format.opaque = NO;
|
||||
CGSize size = CGSizeMake(1, 1);
|
||||
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
|
||||
dummyImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||
CGContextSetFillColorWithColor(context, UIColor.redColor.CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
|
||||
}];
|
||||
NSCAssert(dummyImage, @"The sample alpha image (1x1 pixels) returns nil, OS bug ?");
|
||||
});
|
||||
return dummyImage;
|
||||
}
|
||||
|
||||
static UIImage * _Nonnull SDImageGetNonAlphaDummyImage(void) {
|
||||
static dispatch_once_t onceToken;
|
||||
static UIImage *dummyImage;
|
||||
dispatch_once(&onceToken, ^{
|
||||
SDGraphicsImageRendererFormat *format = [SDGraphicsImageRendererFormat preferredFormat];
|
||||
format.scale = 1;
|
||||
format.opaque = YES;
|
||||
CGSize size = CGSizeMake(1, 1);
|
||||
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
|
||||
dummyImage = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||
CGContextSetFillColorWithColor(context, UIColor.redColor.CGColor);
|
||||
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
|
||||
}];
|
||||
NSCAssert(dummyImage, @"The sample non-alpha image (1x1 pixels) returns nil, OS bug ?");
|
||||
});
|
||||
return dummyImage;
|
||||
}
|
||||
|
||||
static SDImageCoderDecodeSolution kDefaultDecodeSolution = SDImageCoderDecodeSolutionAutomatic;
|
||||
|
||||
static const size_t kBytesPerPixel = 4;
|
||||
|
@ -263,6 +295,26 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
return colorSpace;
|
||||
}
|
||||
|
||||
+ (size_t)preferredByteAlignment {
|
||||
// Actually the page size of system
|
||||
static int __pageSize = 0;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
__pageSize = getpagesize();
|
||||
});
|
||||
return __pageSize;
|
||||
}
|
||||
|
||||
+ (CGBitmapInfo)preferredBitmapInfo:(BOOL)containsAlpha {
|
||||
CGImageRef cgImage;
|
||||
if (containsAlpha) {
|
||||
cgImage = SDImageGetAlphaDummyImage().CGImage;
|
||||
} else {
|
||||
cgImage = SDImageGetNonAlphaDummyImage().CGImage;
|
||||
}
|
||||
return CGImageGetBitmapInfo(cgImage);
|
||||
}
|
||||
|
||||
+ (BOOL)CGImageContainsAlpha:(CGImageRef)cgImage {
|
||||
if (!cgImage) {
|
||||
return NO;
|
||||
|
@ -307,16 +359,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
|
||||
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
|
||||
// Check #3330 for more detail about why this bitmap is choosen.
|
||||
CGBitmapInfo bitmapInfo;
|
||||
if (hasAlpha) {
|
||||
// iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
|
||||
// BGRA8888
|
||||
bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
|
||||
} else {
|
||||
// BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
|
||||
// RGB888
|
||||
bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
|
||||
}
|
||||
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
|
||||
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
|
||||
if (!context) {
|
||||
return NULL;
|
||||
|
@ -351,16 +395,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
|
||||
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
|
||||
// Check #3330 for more detail about why this bitmap is choosen.
|
||||
CGBitmapInfo bitmapInfo;
|
||||
if (hasAlpha) {
|
||||
// iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
|
||||
// BGRA8888
|
||||
bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
|
||||
} else {
|
||||
// BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
|
||||
// RGB888
|
||||
bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
|
||||
}
|
||||
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
|
||||
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
|
||||
vImage_CGImageFormat format = (vImage_CGImageFormat) {
|
||||
.bitsPerComponent = 8,
|
||||
.bitsPerPixel = 32,
|
||||
|
@ -563,16 +599,8 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
|
||||
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
|
||||
// Check #3330 for more detail about why this bitmap is choosen.
|
||||
CGBitmapInfo bitmapInfo;
|
||||
if (hasAlpha) {
|
||||
// iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
|
||||
// BGRA8888
|
||||
bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
|
||||
} else {
|
||||
// BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
|
||||
// RGB888
|
||||
bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
|
||||
}
|
||||
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
|
||||
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
|
||||
CGContextRef destContext = CGBitmapContextCreate(NULL,
|
||||
destResolution.width,
|
||||
destResolution.height,
|
||||
|
|
|
@ -32,14 +32,14 @@ static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGF
|
|||
CGColorSpaceRef space = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||
// kCGImageAlphaNone is not supported in CGBitmapContextCreate.
|
||||
// Check #3330 for more detail about why this bitmap is choosen.
|
||||
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
|
||||
// However, macOS's runtime detection will also call this function, cause recursive, so still hardcode here
|
||||
// CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
|
||||
CGBitmapInfo bitmapInfo;
|
||||
if (!opaque) {
|
||||
// iPhone GPU prefer to use BGRA8888, see: https://forums.raywenderlich.com/t/why-mtlpixelformat-bgra8unorm/53489
|
||||
// BGRA8888
|
||||
bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
|
||||
// [NSImage imageWithSize:flipped:drawingHandler:] returns float(16-bits) RGBA8888 on alpha image, which we don't need
|
||||
bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
|
||||
} else {
|
||||
// BGR888 previously works on iOS 8~iOS 14, however, iOS 15+ will result a black image. FB9958017
|
||||
// RGB888
|
||||
bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;
|
||||
}
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, bitmapInfo);
|
||||
|
|
Loading…
Reference in New Issue