Merge pull request #3575 from dreampiggy/bugfix/CGImageCreateScale
Fix the CGImageCreateScaled to support 16/32 bit depth CGImage (RGB161616) and always preserve pixel format info
This commit is contained in:
commit
7e78633845
|
@ -131,6 +131,8 @@ typedef struct SDImagePixelFormat {
|
||||||
Create a scaled CGImage by the provided CGImage and size. This follows The Create Rule and you are response to call release after usage.
|
Create a scaled CGImage by the provided CGImage and size. This follows The Create Rule and you are response to call release after usage.
|
||||||
It will detect whether the image size matching the scale size, if not, stretch the image to the target size.
|
It will detect whether the image size matching the scale size, if not, stretch the image to the target size.
|
||||||
@note If you need to keep aspect ratio, you can calculate the scale size by using `scaledSizeWithImageSize` first.
|
@note If you need to keep aspect ratio, you can calculate the scale size by using `scaledSizeWithImageSize` first.
|
||||||
|
@note This scale does not change bits per components (which means RGB888 in, RGB888 out), supports 8/16/32(float) bpc. But the method in UIImage+Transform does not gurantee this.
|
||||||
|
@note All supported CGImage pixel format: https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB
|
||||||
|
|
||||||
@param cgImage The CGImage
|
@param cgImage The CGImage
|
||||||
@param size The scale size in pixel.
|
@param size The scale size in pixel.
|
||||||
|
|
|
@ -434,45 +434,110 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
||||||
if (!cgImage) {
|
if (!cgImage) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (size.width == 0 || size.height == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
size_t width = CGImageGetWidth(cgImage);
|
size_t width = CGImageGetWidth(cgImage);
|
||||||
size_t height = CGImageGetHeight(cgImage);
|
size_t height = CGImageGetHeight(cgImage);
|
||||||
if (width == size.width && height == size.height) {
|
if (width == size.width && height == size.height) {
|
||||||
|
// Already same size
|
||||||
CGImageRetain(cgImage);
|
CGImageRetain(cgImage);
|
||||||
return cgImage;
|
return cgImage;
|
||||||
}
|
}
|
||||||
|
size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
|
||||||
|
if (bitsPerComponent != 8 && bitsPerComponent != 16 && bitsPerComponent != 32) {
|
||||||
|
// Unsupported
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
|
||||||
|
CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
|
||||||
|
CGColorRenderingIntent renderingIntent = CGImageGetRenderingIntent(cgImage);
|
||||||
|
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage);
|
||||||
|
CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
|
||||||
|
CGImageByteOrderInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
|
||||||
|
CGBitmapInfo alphaBitmapInfo = (uint32_t)byteOrderInfo;
|
||||||
|
|
||||||
|
// Input need to convert with alpha
|
||||||
|
if (alphaInfo == kCGImageAlphaNone) {
|
||||||
|
// Convert RGB8/16/F -> ARGB8/16/F
|
||||||
|
alphaBitmapInfo |= kCGImageAlphaFirst;
|
||||||
|
} else {
|
||||||
|
alphaBitmapInfo |= alphaInfo;
|
||||||
|
}
|
||||||
|
uint32_t components;
|
||||||
|
if (alphaInfo == kCGImageAlphaOnly) {
|
||||||
|
// Alpha only, simple to 1 channel
|
||||||
|
components = 1;
|
||||||
|
} else {
|
||||||
|
components = 4;
|
||||||
|
}
|
||||||
|
if (SD_OPTIONS_CONTAINS(bitmapInfo, kCGBitmapFloatComponents)) {
|
||||||
|
// Keep float components
|
||||||
|
alphaBitmapInfo |= kCGBitmapFloatComponents;
|
||||||
|
}
|
||||||
__block vImage_Buffer input_buffer = {}, output_buffer = {};
|
__block vImage_Buffer input_buffer = {}, output_buffer = {};
|
||||||
@onExit {
|
@onExit {
|
||||||
if (input_buffer.data) free(input_buffer.data);
|
if (input_buffer.data) free(input_buffer.data);
|
||||||
if (output_buffer.data) free(output_buffer.data);
|
if (output_buffer.data) free(output_buffer.data);
|
||||||
};
|
};
|
||||||
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
|
// Always provide alpha channel
|
||||||
// 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 preferredPixelFormat:hasAlpha].bitmapInfo;
|
|
||||||
vImage_CGImageFormat format = (vImage_CGImageFormat) {
|
vImage_CGImageFormat format = (vImage_CGImageFormat) {
|
||||||
.bitsPerComponent = 8,
|
.bitsPerComponent = (uint32_t)bitsPerComponent,
|
||||||
.bitsPerPixel = 32,
|
.bitsPerPixel = (uint32_t)bitsPerComponent * components,
|
||||||
.colorSpace = NULL,
|
.colorSpace = colorSpace,
|
||||||
|
.bitmapInfo = alphaBitmapInfo,
|
||||||
|
.version = 0,
|
||||||
|
.decode = NULL,
|
||||||
|
.renderingIntent = renderingIntent
|
||||||
|
};
|
||||||
|
// input
|
||||||
|
vImage_Error ret = vImageBuffer_InitWithCGImage(&input_buffer, &format, NULL, cgImage, kvImageNoFlags);
|
||||||
|
if (ret != kvImageNoError) return NULL;
|
||||||
|
// output
|
||||||
|
vImageBuffer_Init(&output_buffer, size.height, size.width, (uint32_t)bitsPerComponent * components, kvImageNoFlags);
|
||||||
|
if (!output_buffer.data) return NULL;
|
||||||
|
|
||||||
|
if (components == 4) {
|
||||||
|
if (bitsPerComponent == 32) {
|
||||||
|
ret = vImageScale_ARGBFFFF(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
} else if (bitsPerComponent == 16) {
|
||||||
|
ret = vImageScale_ARGB16U(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
} else if (bitsPerComponent == 8) {
|
||||||
|
ret = vImageScale_ARGB8888(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bitsPerComponent == 32) {
|
||||||
|
ret = vImageScale_PlanarF(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
} else if (bitsPerComponent == 16) {
|
||||||
|
ret = vImageScale_Planar16U(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
} else if (bitsPerComponent == 8) {
|
||||||
|
ret = vImageScale_Planar8(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret != kvImageNoError) return NULL;
|
||||||
|
|
||||||
|
// Convert back to non-alpha for RGB input to preserve pixel format
|
||||||
|
if (alphaInfo == kCGImageAlphaNone) {
|
||||||
|
// in-place, no extra allocation
|
||||||
|
if (bitsPerComponent == 32) {
|
||||||
|
ret = vImageConvert_ARGBFFFFtoRGBFFF(&output_buffer, &output_buffer, kvImageNoFlags);
|
||||||
|
} else if (bitsPerComponent == 16) {
|
||||||
|
ret = vImageConvert_ARGB16UtoRGB16U(&output_buffer, &output_buffer, kvImageNoFlags);
|
||||||
|
} else if (bitsPerComponent == 8) {
|
||||||
|
ret = vImageConvert_ARGB8888toRGB888(&output_buffer, &output_buffer, kvImageNoFlags);
|
||||||
|
}
|
||||||
|
if (ret != kvImageNoError) return NULL;
|
||||||
|
}
|
||||||
|
vImage_CGImageFormat output_format = (vImage_CGImageFormat) {
|
||||||
|
.bitsPerComponent = (uint32_t)bitsPerComponent,
|
||||||
|
.bitsPerPixel = (uint32_t)bitsPerPixel,
|
||||||
|
.colorSpace = colorSpace,
|
||||||
.bitmapInfo = bitmapInfo,
|
.bitmapInfo = bitmapInfo,
|
||||||
.version = 0,
|
.version = 0,
|
||||||
.decode = NULL,
|
.decode = NULL,
|
||||||
.renderingIntent = CGImageGetRenderingIntent(cgImage)
|
.renderingIntent = renderingIntent
|
||||||
};
|
};
|
||||||
|
CGImageRef outputImage = vImageCreateCGImageFromBuffer(&output_buffer, &output_format, NULL, NULL, kvImageNoFlags, &ret);
|
||||||
vImage_Error a_ret = vImageBuffer_InitWithCGImage(&input_buffer, &format, NULL, cgImage, kvImageNoFlags);
|
|
||||||
if (a_ret != kvImageNoError) return NULL;
|
|
||||||
output_buffer.width = MAX(size.width, 0);
|
|
||||||
output_buffer.height = MAX(size.height, 0);
|
|
||||||
output_buffer.rowBytes = SDByteAlign(output_buffer.width * 4, 64);
|
|
||||||
output_buffer.data = malloc(output_buffer.rowBytes * output_buffer.height);
|
|
||||||
if (!output_buffer.data) return NULL;
|
|
||||||
|
|
||||||
vImage_Error ret = vImageScale_ARGB8888(&input_buffer, &output_buffer, NULL, kvImageHighQualityResampling);
|
|
||||||
if (ret != kvImageNoError) return NULL;
|
|
||||||
|
|
||||||
CGImageRef outputImage = vImageCreateCGImageFromBuffer(&output_buffer, &format, NULL, NULL, kvImageNoFlags, &ret);
|
|
||||||
if (ret != kvImageNoError) {
|
if (ret != kvImageNoError) {
|
||||||
CGImageRelease(outputImage);
|
CGImageRelease(outputImage);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -11,6 +11,75 @@
|
||||||
#import "UIColor+SDHexString.h"
|
#import "UIColor+SDHexString.h"
|
||||||
#import <CoreImage/CoreImage.h>
|
#import <CoreImage/CoreImage.h>
|
||||||
|
|
||||||
|
static void SDAssertCGImagePixelFormatEqual(CGImageRef image1, CGImageRef image2) {
|
||||||
|
CGBitmapInfo bitmapInfo1 = CGImageGetBitmapInfo(image1);
|
||||||
|
CGBitmapInfo bitmapInfo2 = CGImageGetBitmapInfo(image2);
|
||||||
|
XCTAssertEqual(bitmapInfo1, bitmapInfo2);
|
||||||
|
// alphaInfo && byteOrderInfo && pixelFomat are just calculation of bitmapInfo
|
||||||
|
XCTAssertEqual(CGImageGetColorSpace(image1), CGImageGetColorSpace(image2));
|
||||||
|
XCTAssertEqual(CGImageGetBitsPerPixel(image1), CGImageGetBitsPerPixel(image2));
|
||||||
|
XCTAssertEqual(CGImageGetBitsPerComponent(image1), CGImageGetBitsPerComponent(image2));
|
||||||
|
XCTAssertEqual(CGImageGetRenderingIntent(image1), CGImageGetRenderingIntent(image2));
|
||||||
|
XCTAssertEqual(CGImageGetShouldInterpolate(image1), CGImageGetShouldInterpolate(image2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Current sd_colorAtPoint: support 8-bits only, 16bits and float color will fail...
|
||||||
|
// So I write this `SDAssertCGImageFirstComponentWhite` :(
|
||||||
|
static void SDAssertCGImageFirstComponentWhite(CGImageRef image, OSType pixelType) {
|
||||||
|
CGDataProviderRef provider = CGImageGetDataProvider(image);
|
||||||
|
CFDataRef data = CGDataProviderCopyData(provider);
|
||||||
|
if (pixelType == kCVPixelFormatType_128RGBAFloat) {
|
||||||
|
float *buffer = (float *)CFDataGetBytePtr(data);
|
||||||
|
float r = buffer[0];
|
||||||
|
float g = buffer[1];
|
||||||
|
float b = buffer[2];
|
||||||
|
float a = buffer[3];
|
||||||
|
XCTAssertEqual(r, 1.0);
|
||||||
|
XCTAssertEqual(g, 1.0);
|
||||||
|
XCTAssertEqual(b, 1.0);
|
||||||
|
XCTAssertEqual(a, 1.0);
|
||||||
|
} else if (pixelType == kCVPixelFormatType_64RGBALE) {
|
||||||
|
uint16_t *buffer = (uint16_t *)CFDataGetBytePtr(data);
|
||||||
|
uint16_t r = buffer[0];
|
||||||
|
uint16_t g = buffer[1];
|
||||||
|
uint16_t b = buffer[2];
|
||||||
|
uint16_t a = buffer[3];
|
||||||
|
XCTAssertEqual(r, UINT16_MAX);
|
||||||
|
XCTAssertEqual(g, UINT16_MAX);
|
||||||
|
XCTAssertEqual(b, UINT16_MAX);
|
||||||
|
XCTAssertEqual(a, UINT16_MAX);
|
||||||
|
} else if (pixelType == kCVPixelFormatType_32ARGB) {
|
||||||
|
uint8_t *buffer = (uint8_t *)CFDataGetBytePtr(data);
|
||||||
|
uint8_t a = buffer[0];
|
||||||
|
uint8_t r = buffer[1];
|
||||||
|
uint8_t g = buffer[2];
|
||||||
|
uint8_t b = buffer[3];
|
||||||
|
XCTAssertEqual(a, UINT8_MAX);
|
||||||
|
XCTAssertEqual(r, UINT8_MAX);
|
||||||
|
XCTAssertEqual(g, UINT8_MAX);
|
||||||
|
XCTAssertEqual(b, UINT8_MAX);
|
||||||
|
} else if (pixelType == kCVPixelFormatType_24RGB) {
|
||||||
|
uint8_t *buffer = (uint8_t *)CFDataGetBytePtr(data);
|
||||||
|
uint8_t r = buffer[0];
|
||||||
|
uint8_t g = buffer[1];
|
||||||
|
uint8_t b = buffer[2];
|
||||||
|
XCTAssertEqual(r, UINT8_MAX);
|
||||||
|
XCTAssertEqual(g, UINT8_MAX);
|
||||||
|
XCTAssertEqual(b, UINT8_MAX);
|
||||||
|
} else if (pixelType == kCVPixelFormatType_48RGB) {
|
||||||
|
uint16_t *buffer = (uint16_t *)CFDataGetBytePtr(data);
|
||||||
|
uint16_t r = buffer[0];
|
||||||
|
uint16_t g = buffer[1];
|
||||||
|
uint16_t b = buffer[2];
|
||||||
|
XCTAssertEqual(r, UINT16_MAX);
|
||||||
|
XCTAssertEqual(g, UINT16_MAX);
|
||||||
|
XCTAssertEqual(b, UINT16_MAX);
|
||||||
|
} else {
|
||||||
|
XCTFail(@"Should not hit here");
|
||||||
|
}
|
||||||
|
CFRelease(data);
|
||||||
|
}
|
||||||
|
|
||||||
@interface SDImageTransformerTests : SDTestCase
|
@interface SDImageTransformerTests : SDTestCase
|
||||||
|
|
||||||
@property (nonatomic, strong) UIImage *testImageCG;
|
@property (nonatomic, strong) UIImage *testImageCG;
|
||||||
|
@ -417,6 +486,169 @@
|
||||||
expect([[testColor sd_hexString] isEqualToString:UIColor.blackColor.sd_hexString]).beFalsy();
|
expect([[testColor sd_hexString] isEqualToString:UIColor.blackColor.sd_hexString]).beFalsy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)test22CGImageCreateScaledWithSize {
|
||||||
|
size_t width = 100;
|
||||||
|
size_t height = 100;
|
||||||
|
size_t scaledWidth = 50;
|
||||||
|
size_t scaledHeight = 50;
|
||||||
|
// RGB888
|
||||||
|
CGImageRef RGB888Image = ^(){
|
||||||
|
size_t bitsPerComponent = 8;
|
||||||
|
size_t components = 3;
|
||||||
|
size_t bitsPerPixel = bitsPerComponent * components;
|
||||||
|
size_t bytesPerRow = bitsPerPixel / 8 * width;
|
||||||
|
size_t size = bytesPerRow * height;
|
||||||
|
size_t count = width * height * components;
|
||||||
|
uint8_t bitmap[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
bitmap[i] = UINT8_MAX;
|
||||||
|
}
|
||||||
|
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||||
|
CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;
|
||||||
|
CFDataRef data = CFDataCreate(NULL, (UInt8 *)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);
|
||||||
|
return cgImage;
|
||||||
|
}();
|
||||||
|
CGImageRef RGB888Scaled = [SDImageCoderHelper CGImageCreateScaled:RGB888Image size:CGSizeMake(scaledWidth, scaledHeight)];
|
||||||
|
XCTAssertEqual(CGImageGetWidth(RGB888Scaled), scaledWidth);
|
||||||
|
XCTAssertEqual(CGImageGetHeight(RGB888Scaled), scaledHeight);
|
||||||
|
SDAssertCGImagePixelFormatEqual(RGB888Scaled, RGB888Image);
|
||||||
|
SDAssertCGImageFirstComponentWhite(RGB888Scaled, kCVPixelFormatType_24RGB);
|
||||||
|
|
||||||
|
// RGB16161616
|
||||||
|
CGImageRef RGB161616Image = ^(){
|
||||||
|
size_t bitsPerComponent = 16;
|
||||||
|
size_t components = 3;
|
||||||
|
size_t bitsPerPixel = bitsPerComponent * components;
|
||||||
|
size_t bytesPerRow = bitsPerPixel / 8 * width;
|
||||||
|
size_t size = bytesPerRow * height;
|
||||||
|
size_t count = width * height * components;
|
||||||
|
uint16_t bitmap[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
bitmap[i] = UINT16_MAX;
|
||||||
|
}
|
||||||
|
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||||
|
CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrder16Host;
|
||||||
|
CFDataRef data = CFDataCreate(NULL, (UInt8 *)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);
|
||||||
|
return cgImage;
|
||||||
|
}();
|
||||||
|
CGImageRef RGB161616Scaled = [SDImageCoderHelper CGImageCreateScaled:RGB161616Image size:CGSizeMake(scaledWidth, scaledHeight)];
|
||||||
|
XCTAssertEqual(CGImageGetWidth(RGB161616Scaled), scaledWidth);
|
||||||
|
XCTAssertEqual(CGImageGetHeight(RGB161616Scaled), scaledHeight);
|
||||||
|
SDAssertCGImagePixelFormatEqual(RGB161616Scaled, RGB161616Image);
|
||||||
|
SDAssertCGImageFirstComponentWhite(RGB161616Scaled, kCVPixelFormatType_48RGB);
|
||||||
|
|
||||||
|
// ARGB8888
|
||||||
|
CGImageRef ARGB8888Image = ^(){
|
||||||
|
size_t bitsPerComponent = 8;
|
||||||
|
size_t components = 4;
|
||||||
|
size_t bitsPerPixel = bitsPerComponent * components;
|
||||||
|
size_t bytesPerRow = bitsPerPixel / 8 * width;
|
||||||
|
size_t size = bytesPerRow * height;
|
||||||
|
size_t count = width * height * components;
|
||||||
|
uint8_t bitmap[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
bitmap[i] = UINT8_MAX;
|
||||||
|
}
|
||||||
|
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||||
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault;
|
||||||
|
CFDataRef data = CFDataCreate(NULL, (UInt8 *)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);
|
||||||
|
return cgImage;
|
||||||
|
}();
|
||||||
|
CGImageRef ARGB8888Scaled = [SDImageCoderHelper CGImageCreateScaled:ARGB8888Image size:CGSizeMake(scaledWidth, scaledHeight)];
|
||||||
|
XCTAssertEqual(CGImageGetWidth(ARGB8888Scaled), scaledWidth);
|
||||||
|
XCTAssertEqual(CGImageGetHeight(ARGB8888Scaled), scaledHeight);
|
||||||
|
SDAssertCGImagePixelFormatEqual(ARGB8888Scaled, ARGB8888Image);
|
||||||
|
SDAssertCGImageFirstComponentWhite(ARGB8888Scaled, kCVPixelFormatType_32ARGB);
|
||||||
|
|
||||||
|
// RGBA16161616
|
||||||
|
CGImageRef RGBA16161616Image = ^(){
|
||||||
|
size_t bitsPerComponent = 16;
|
||||||
|
size_t components = 4;
|
||||||
|
size_t bitsPerPixel = bitsPerComponent * components;
|
||||||
|
size_t bytesPerRow = bitsPerPixel / 8 * width;
|
||||||
|
size_t size = bytesPerRow * height;
|
||||||
|
size_t count = width * height * components;
|
||||||
|
uint16_t bitmap[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
bitmap[i] = UINT16_MAX;
|
||||||
|
}
|
||||||
|
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||||
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder16Host;
|
||||||
|
CFDataRef data = CFDataCreate(NULL, (UInt8 *)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);
|
||||||
|
return cgImage;
|
||||||
|
}();
|
||||||
|
CGImageRef RGBA16161616Scaled = [SDImageCoderHelper CGImageCreateScaled:RGBA16161616Image size:CGSizeMake(scaledWidth, scaledHeight)];
|
||||||
|
XCTAssertEqual(CGImageGetWidth(RGBA16161616Scaled), scaledWidth);
|
||||||
|
XCTAssertEqual(CGImageGetHeight(RGBA16161616Scaled), scaledHeight);
|
||||||
|
SDAssertCGImagePixelFormatEqual(RGBA16161616Scaled, RGBA16161616Image);
|
||||||
|
SDAssertCGImageFirstComponentWhite(RGBA16161616Scaled, kCVPixelFormatType_64RGBALE);
|
||||||
|
|
||||||
|
// RGBAFFFF
|
||||||
|
CGImageRef RGBAFFFFImage = ^(){
|
||||||
|
size_t bitsPerComponent = 32;
|
||||||
|
size_t components = 4;
|
||||||
|
size_t bitsPerPixel = bitsPerComponent * components;
|
||||||
|
size_t bytesPerRow = bitsPerPixel / 8 * width;
|
||||||
|
size_t size = bytesPerRow * height;
|
||||||
|
size_t count = width * height * components;
|
||||||
|
float bitmap[count];
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
bitmap[i] = 1.0;
|
||||||
|
}
|
||||||
|
CGColorSpaceRef colorspace = [SDImageCoderHelper colorSpaceGetDeviceRGB];
|
||||||
|
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Host | kCGBitmapFloatComponents;
|
||||||
|
CFDataRef data = CFDataCreate(NULL, (UInt8 *)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);
|
||||||
|
return cgImage;
|
||||||
|
}();
|
||||||
|
CGImageRef RGBAFFFFScaled = [SDImageCoderHelper CGImageCreateScaled:RGBAFFFFImage size:CGSizeMake(scaledWidth, scaledHeight)];
|
||||||
|
XCTAssertEqual(CGImageGetWidth(RGBAFFFFScaled), scaledWidth);
|
||||||
|
XCTAssertEqual(CGImageGetHeight(RGBAFFFFScaled), scaledHeight);
|
||||||
|
SDAssertCGImagePixelFormatEqual(RGBAFFFFScaled, RGBAFFFFImage);
|
||||||
|
SDAssertCGImageFirstComponentWhite(RGBAFFFFScaled, kCVPixelFormatType_128RGBAFloat);
|
||||||
|
|
||||||
|
// Cleanup and check by human eyes using preview, all should be white image
|
||||||
|
CGImageRelease(RGB888Image);
|
||||||
|
CGImageRelease(RGB888Scaled);
|
||||||
|
CGImageRelease(RGB161616Image);
|
||||||
|
CGImageRelease(RGB161616Scaled);
|
||||||
|
CGImageRelease(ARGB8888Image);
|
||||||
|
CGImageRelease(ARGB8888Scaled);
|
||||||
|
CGImageRelease(RGBA16161616Image);
|
||||||
|
CGImageRelease(RGBA16161616Scaled);
|
||||||
|
CGImageRelease(RGBAFFFFImage);
|
||||||
|
CGImageRelease(RGBAFFFFScaled);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Helper
|
#pragma mark - Helper
|
||||||
|
|
||||||
- (UIImage *)testImageCG {
|
- (UIImage *)testImageCG {
|
||||||
|
|
Loading…
Reference in New Issue