Rename to use struct to return the alignment, preserve for future HDR support
This commit is contained in:
parent
45b497c5f6
commit
808cedc7e5
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue