Rename to use struct to return the alignment, preserve for future HDR support

This commit is contained in:
DreamPiggy 2023-07-13 22:00:56 +08:00
parent 45b497c5f6
commit 808cedc7e5
6 changed files with 45 additions and 33 deletions

View File

@ -41,6 +41,15 @@ static inline size_t SDByteAlign(size_t size, size_t alignment) {
return ((size + (alignment - 1)) / alignment) * alignment;
}
/// The pixel format about the information to call `CGImageCreate` suitable for current hardware rendering
///
typedef struct SDImagePixelFormat {
/// Typically is pre-multiplied RGBA8888 for alpha image, RGBX8888 for non-alpha image.
CGBitmapInfo bitmapInfo;
/// Typically is 32, the 8 pixels bytesPerRow.
size_t alignment;
} SDImagePixelFormat;
/**
Provide some common helper methods for building the image decoder/encoder.
*/
@ -79,23 +88,15 @@ static inline size_t SDByteAlign(size_t size, size_t alignment) {
+ (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 32, the 8 pixels bytesPerRow.
@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.
Tthis returns the pixel format **Preferred from current hardward && OS using runtime detection**
@param containsAlpha Whether the image to render contains alpha channel
*/
+ (CGBitmapInfo)preferredBitmapInfo:(BOOL)containsAlpha;
+ (SDImagePixelFormat)preferredPixelFormat:(BOOL)containsAlpha;
/**
Check whether CGImage is hardware supported to rendering on screen, without the trigger of `CA::Render::copy_image`
You can debug the copied image by using Xcode's `Color Copied Image`, the copied image will turn Cyan and occupy double RAM for bitmap buffer.
Typically, when the CGImage's using the method above (`colorspace` / `byteAlignment` / `bitmapInfo`) can render withtout the copy.
Typically, when the CGImage's using the method above (`colorspace` / `alignment` / `bitmapInfo`) can render withtout the copy.
*/
+ (BOOL)CGImageIsHardwareSupported:(_Nonnull CGImageRef)cgImage;

View File

@ -308,27 +308,36 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
return colorSpace;
}
+ (size_t)preferredByteAlignment {
// https://github.com/path/FastImageCache#byte-alignment
// A properly aligned bytes-per-row value must be a multiple of 8 pixels × bytes per pixel.
return 32;
}
+ (CGBitmapInfo)preferredBitmapInfo:(BOOL)containsAlpha {
+ (SDImagePixelFormat)preferredPixelFormat:(BOOL)containsAlpha {
CGImageRef cgImage;
if (containsAlpha) {
cgImage = SDImageGetAlphaDummyImage().CGImage;
} else {
cgImage = SDImageGetNonAlphaDummyImage().CGImage;
}
return CGImageGetBitmapInfo(cgImage);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage);
size_t bitsPerPixel = 8;
if (SD_OPTIONS_CONTAINS(bitmapInfo, kCGBitmapFloatComponents)) {
bitsPerPixel = 16;
}
size_t components = 4; // Hardcode now
// https://github.com/path/FastImageCache#byte-alignment
// A properly aligned bytes-per-row value must be a multiple of 8 pixels × bytes per pixel.
size_t alignment = (bitsPerPixel / 8) * components * 8;
SDImagePixelFormat pixelFormat = {
.bitmapInfo = bitmapInfo,
.alignment = alignment
};
return pixelFormat;
}
+ (BOOL)CGImageIsHardwareSupported:(CGImageRef)cgImage {
BOOL supported = YES;
// 1. Check byte alignment
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
if (SDByteAlign(bytesPerRow, [SDImageCoderHelper preferredByteAlignment]) == bytesPerRow) {
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
SDImagePixelFormat pixelFormat = [self preferredPixelFormat:hasAlpha];
if (SDByteAlign(bytesPerRow, pixelFormat.alignment) == bytesPerRow) {
// byte aligned, OK
supported &= YES;
} else {
@ -405,7 +414,7 @@ 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.
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
if (!context) {
return NULL;
@ -441,7 +450,7 @@ 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.
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
vImage_CGImageFormat format = (vImage_CGImageFormat) {
.bitsPerComponent = 8,
.bitsPerPixel = 32,
@ -653,7 +662,7 @@ 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.
// From v5.17.0, use runtime detection of bitmap info instead of hardcode.
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:hasAlpha];
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:hasAlpha].bitmapInfo;
CGContextRef destContext = CGBitmapContextCreate(NULL,
destResolution.width,
destResolution.height,

View File

@ -34,7 +34,6 @@ static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGF
// 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) {
// [NSImage imageWithSize:flipped:drawingHandler:] returns float(16-bits) RGBA8888 on alpha image, which we don't need

View File

@ -18,7 +18,7 @@
Force decode is used for 2 cases:
-- 1. for ImageIO created image (via `CGImageCreateWithImageSource` SPI), it's lazy and we trigger the decode before rendering
-- 2. for non-ImageIO created image (via `CGImageCreate` API), we can ensure it's alignment is suitable to render on screen without copy by CoreAnimation
@note For coder plugin developer, always use the SDImageCoderHelper's `colorSpaceGetDeviceRGB`/`preferredByteAlignment`/`preferredBitmapInfo:` to create CGImage.
@note For coder plugin developer, always use the SDImageCoderHelper's `colorSpaceGetDeviceRGB`/`preferredPixelFormat` to create CGImage.
@note For more information why force decode, see: https://github.com/path/FastImageCache#byte-alignment
@note From v5.17.0, the default value is always NO. Use `SDImageForceDecodePolicy` to control complicated policy.
*/

View File

@ -455,19 +455,21 @@
size_t bitsPerComponent = 8;
size_t components = 4;
size_t bitsPerPixel = bitsPerComponent * components;
size_t bytesPerRow = SDByteAlign(bitsPerPixel / 8 * width, SDImageCoderHelper.preferredByteAlignment);
size_t bytesPerRow = SDByteAlign(bitsPerPixel / 8 * width, [SDImageCoderHelper preferredPixelFormat:YES].alignment);
size_t size = bytesPerRow * height;
uint8_t bitmap[size];
for (size_t i = 0; i < size; i++) {
bitmap[i] = 255;
}
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:YES];
CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, bitmap, size, NULL);
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:YES].bitmapInfo;
CFDataRef data = CFDataCreate(NULL, bitmap, size);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
BOOL shouldInterpolate = YES;
CGColorRenderingIntent intent = kCGRenderingIntentDefault;
CGImageRef cgImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorspace, bitmapInfo, provider, NULL, shouldInterpolate, intent);
CGDataProviderRelease(provider);
XCTAssert(cgImage);
BOOL result = [SDImageCoderHelper CGImageIsHardwareSupported:cgImage];
// Since it's 32 bytes aligned, return true
@ -500,12 +502,14 @@
bitmap[i] = 255;
}
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredBitmapInfo:YES];
CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, bitmap, size, NULL);
CGBitmapInfo bitmapInfo = [SDImageCoderHelper preferredPixelFormat:YES].bitmapInfo;
CFDataRef data = CFDataCreate(NULL, bitmap, size);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CFRelease(data);
BOOL shouldInterpolate = YES;
CGColorRenderingIntent intent = kCGRenderingIntentDefault;
CGImageRef cgImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorspace, bitmapInfo, provider, NULL, shouldInterpolate, intent);
CGDataProviderRelease(provider);
XCTAssert(cgImage);
BOOL result = [SDImageCoderHelper CGImageIsHardwareSupported:cgImage];
// Since it's not 32 bytes aligned, return false

View File

@ -298,8 +298,7 @@
NSUInteger defaultLimitBytes = SDImageCoderHelper.defaultScaleDownLimitBytes;
SDImageCoderHelper.defaultScaleDownLimitBytes = 1000 * 1000 * 4; // Limit 1000x1000 pixel
// From v5.5.0, the `SDWebImageScaleDownLargeImages` translate to `SDWebImageContextImageThumbnailPixelSize`, and works for progressive loading
[SDImageCache.sharedImageCache removeImageFromDiskForKey:originalImageURL.absoluteString];
[SDWebImageManager.sharedManager loadImageWithURL:originalImageURL options:SDWebImageScaleDownLargeImages | SDWebImageProgressiveLoad progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
[SDWebImageManager.sharedManager loadImageWithURL:originalImageURL options:SDWebImageFromLoaderOnly | SDWebImageScaleDownLargeImages | SDWebImageProgressiveLoad progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
expect(image).notTo.beNil();
expect(image.size).equal(CGSizeMake(1000, 1000));
if (finished) {
@ -310,7 +309,7 @@
}
}];
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
[self waitForExpectationsWithTimeout:100 handler:^(NSError * _Nullable error) {
SDImageCoderHelper.defaultScaleDownLimitBytes = defaultLimitBytes;
}];
}