Skip to content

Scale alpha planes [0, (1 << bit-depth) - 1] to [0, 65536] #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 40 additions & 20 deletions Example/Tests/Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ -(void)assertColor8: (NSString*)filename img:(CGImageRef)img expectedColor:(UInt
for(size_t c = 0; c < 3; ++c) {
int32_t result = pix[c];
int32_t expected = expectedColor[c];
XCTAssertTrue(ok = (abs(result - expected) <= threshold8), "(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)", x, y, c, result, expected, filename);
XCTAssertTrue(ok = (abs(result - expected) <= threshold8),
"(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)",
x, y, c, result, expected, filename);
}
if(!ok) {
goto end;
Expand Down Expand Up @@ -202,9 +204,13 @@ -(void)assertColorAlpha8: (NSString*)filename img:(CGImageRef)img expectedColor:
for(size_t c = 1; c < 4; ++c) {
int32_t result = pix[c];
int32_t expected = expectedColor[c-1];
XCTAssertTrue(ok &= (abs(result - expected) <= threshold8), "(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)", x, y, c, result, expected, filename);
XCTAssertTrue(ok &= (abs(result - expected) <= threshold8),
"(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)",
x, y, c, result, expected, filename);
}
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xff : 0x00)), "(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)", x, y, pix[0], x < 128 ? 0xff : 0x00, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xff : 0x00)),
"(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)",
x, y, pix[0], x < 128 ? 0xff : 0x00, filename);
if(!ok) {
goto end;
}
Expand Down Expand Up @@ -233,7 +239,9 @@ -(void)assertColor16: (NSString*)filename img:(CGImageRef)img expectedColor:(UIn
for(size_t c = 0; c < 3; ++c) {
int32_t result = pix[c];
int32_t expected = expectedColor[c];
XCTAssertTrue(ok &= (abs(result - expected) <= threshold16), "(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)", x, y, c, result, expected, filename);
XCTAssertTrue(ok &= (abs(result - expected) <= threshold16),
"(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)",
x, y, c, result, expected, filename);
}
if(!ok) {
goto end;
Expand All @@ -257,15 +265,19 @@ -(void)assertColorAlpha16: (NSString*)filename img:(CGImageRef)img expectedColor
XCTAssertEqual(numComponents, 4);
XCTAssertEqual(bytesPerPixel, 8);
for(size_t y = 0; y < height; ++y) {
for(size_t x = 0; x < width; ++x) {
for(size_t x = 64; x < width; x+=128) {
UInt16* pix = (UInt16*)(buf + (stride * y) + (bytesPerPixel * x));
bool ok = true;
for(size_t c = 1; c < 4; ++c) {
int32_t result = pix[c];
int32_t expected = expectedColor[c-1];
XCTAssertTrue(ok &= (abs(result - expected) <= threshold16), "(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)", x, y, c, result, expected, filename);
XCTAssertTrue(ok &= (abs(result - expected) <= threshold16),
"(x: %ld, y: %ld, c:%ld): result=%d vs expected=%d (%@)",
x, y, c, result, expected, filename);
}
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xffff : 0x0000)), "(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)", x, y, pix[0], x < 128 ? 0xffff : 0x00, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xffff : 0x0000)),
"(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)",
x, y, pix[0], x < 128 ? 0xffff : 0x00, filename);
if(!ok) {
goto end;
}
Expand All @@ -292,7 +304,9 @@ -(void)assertMono8: (NSString*)filename img:(CGImageRef)img
for(size_t x = 0; x < width; ++x) {
bool ok = true;
UInt8* pix = (buf + (stride * y) + (bytesPerPixel * x));
XCTAssertTrue(ok &= (abs(pix0 - *pix) <= threshold8), "(x: %ld, y: %ld): result=%d vs expected=%d (%@)", x, y, *pix, pix0, filename);
XCTAssertTrue(ok &= (abs(pix0 - *pix) <= threshold8),
"(x: %ld, y: %ld): result=%d vs expected=%d (%@)",
x, y, *pix, pix0, filename);
if(!ok) {
goto end;
}
Expand All @@ -319,8 +333,12 @@ -(void)assertMonoAlpha8: (NSString*)filename img:(CGImageRef)img
for(size_t x = 64; x < width; x+=128) {
bool ok = true;
UInt8* pix = (buf + (stride * y) + (bytesPerPixel * x));
XCTAssertTrue(ok &= (abs(pix0 - pix[1]) <= threshold8), "(x: %ld, y: %ld, c: mono): result=%d vs expected=%d (%@)", x, y, pix[1], pix0, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xff : 0x00)), "(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)", x, y, pix[0], x < 128 ? 0xff : 0x00, filename);
XCTAssertTrue(ok &= (abs(pix0 - pix[1]) <= threshold8),
"(x: %ld, y: %ld, c: mono): result=%d vs expected=%d (%@)",
x, y, pix[1], pix0, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xff : 0x00)),
"(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)",
x, y, pix[0], x < 128 ? 0xff : 0x00, filename);
if(!ok) {
goto end;
}
Expand Down Expand Up @@ -348,7 +366,9 @@ -(void)assertMono16: (NSString*)filename img:(CGImageRef)img
for(size_t x = 0; x < width; ++x) {
UInt16* pix = (UInt16*)(buf + (stride * y) + (bytesPerPixel * x));
bool ok = true;
XCTAssertTrue(ok &= (abs(pix0 - *pix) <= threshold16), "(x: %ld, y: %ld): result=%d vs expected=%d (%@)", x, y, *pix, pix0, filename);
XCTAssertTrue(ok &= (abs(pix0 - *pix) <= threshold16),
"(x: %ld, y: %ld): result=%d vs expected=%d (%@)",
x, y, *pix, pix0, filename);
if(!ok) {
goto end;
}
Expand All @@ -372,11 +392,15 @@ -(void)assertMonoAlpha16: (NSString*)filename img:(CGImageRef)img
XCTAssertEqual(bytesPerPixel, 4);
UInt16 const pix0 = ((UInt16*)buf)[1];
for(size_t y = 0; y < height; ++y) {
for(size_t x = 0; x < width; ++x) {
for(size_t x = 64; x < width; x+=128) {
UInt16* pix = (UInt16*)(buf + (stride * y) + (bytesPerPixel * x));
bool ok = true;
XCTAssertTrue(ok &= (abs(pix0 - pix[1]) <= threshold16), "(x: %ld, y: %ld): result=%d vs expected=%d (%@)", x, y, pix[1], pix0, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xffff : 0x0000)), "(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)", x, y, pix[0], x < 128 ? 0xffff : 0x0000, filename);
XCTAssertTrue(ok &= (abs(pix0 - pix[1]) <= threshold16),
"(x: %ld, y: %ld): result=%d vs expected=%d (%@)",
x, y, pix[1], pix0, filename);
XCTAssertTrue(ok &= (pix[0] == (x < 128 ? 0xffff : 0x0000)),
"(x: %ld, y: %ld, c: alpha): result=%d vs expected=%d (%@)",
x, y, pix[0], x < 128 ? 0xffff : 0x0000, filename);
if(!ok) {
goto end;
}
Expand Down Expand Up @@ -420,9 +444,7 @@ - (void)assertImages: (NSMutableArray*) list colorName:(NSString*)colorName expe
}
}else{
if([alpha isEqualToString:@"with-alpha"]) {
// FIXME(ledyba-z): This issue blocks testing alpha plane:
// https://github.com/AOMediaCodec/libavif/issues/86
//[self assertMonoAlpha16: convertedFilename img:img.CGImage];
[self assertMonoAlpha16: convertedFilename img:img.CGImage];
}else{
[self assertMono16: convertedFilename img:img.CGImage];
}
Expand All @@ -436,9 +458,7 @@ - (void)assertImages: (NSMutableArray*) list colorName:(NSString*)colorName expe
}
} else {
if([alpha isEqualToString:@"with-alpha"]) {
// FIXME(ledyba-z): This issue blocks testing alpha plane:
// https://github.com/AOMediaCodec/libavif/issues/86
//[self assertColorAlpha16: convertedFilename img:img.CGImage expectedColor:expectedColor16];
[self assertColorAlpha16: convertedFilename img:img.CGImage expectedColor:expectedColor16];
} else {
[self assertColor16: convertedFilename img:img.CGImage expectedColor:expectedColor16];
}
Expand Down
52 changes: 51 additions & 1 deletion SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,10 @@ static CGImageRef CreateImage8(avifImage * avif) {
origCr.rowBytes = 0;
memset(origCr.data, pixelRange.CbCr_bias, origCr.width);
}


//TODO: (ledyba-z) we have to scale alpha when libavif v0.6.0 comes.
// https://github.com/AOMediaCodec/libavif/issues/91

uint8_t const permuteMap[4] = {0, 1, 2, 3};
switch(avif->yuvFormat) {
case AVIF_PIXEL_FORMAT_NONE:
Expand Down Expand Up @@ -521,6 +524,7 @@ static CGImageRef CreateImage16U(avifImage * avif) {
uint16_t* resultBufferData = NULL;
uint16_t* argbBufferData = NULL;
uint16_t* ayuvBufferData = NULL;
uint16_t* scaledAlphaBufferData = NULL;
uint16_t* dummyCbData = NULL;
uint16_t* dummyCrData = NULL;
uint16_t* dummyAlphaData = NULL;
Expand Down Expand Up @@ -637,10 +641,53 @@ static CGImageRef CreateImage16U(avifImage * avif) {

vImage_Buffer origAlpha = {0};
if(hasAlpha) {
float* floatAlphaBufferData = NULL;
floatAlphaBufferData = calloc(avif->width * avif->height, sizeof(float));
scaledAlphaBufferData = calloc(avif->width * avif->height, sizeof(uint16_t));
if(floatAlphaBufferData == NULL || scaledAlphaBufferData == NULL) {
err = kvImageMemoryAllocationError;
goto end_prepare_alpha;
}
origAlpha.data = avif->alphaPlane;
origAlpha.width = avif->width;
origAlpha.height = avif->height;
origAlpha.rowBytes = avif->alphaRowBytes;

vImage_Buffer floatAlphaBuffer = {
.data = floatAlphaBufferData,
.width = avif->width,
.height = avif->height,
.rowBytes = avif->width * sizeof(float),
};
vImage_Buffer scaledAlphaBuffer = {
.data = scaledAlphaBufferData,
.width = avif->width,
.height = avif->height,
.rowBytes = avif->width * sizeof(uint16_t),
};
err = vImageConvert_16UToF(&origAlpha, &floatAlphaBuffer, 0.0f, 1.0f, kvImageNoFlags);
if(err != kvImageNoError) {
NSLog(@"Failed to convert alpha planes from uint16 to float: %ld", err);
goto end_prepare_alpha;
}
float scale = 1.0f;
if(avif->depth == 10) {
scale = (float)((1 << 10) - 1) / 65535.0f;
} else if(avif->depth == 12) {
scale = (float)((1 << 12) - 1) / 65535.0f;
}
err = vImageConvert_FTo16U(&floatAlphaBuffer, &scaledAlphaBuffer, 0.0f, scale, kvImageNoFlags);
if(err != kvImageNoError) {
NSLog(@"Failed to convert alpha planes from uint16 to float: %ld", err);
goto end_prepare_alpha;
}
origAlpha.data = scaledAlphaBufferData;
origAlpha.rowBytes = avif->width * sizeof(uint16_t);
end_prepare_alpha:
free(floatAlphaBufferData);
if(err != kvImageNoError) {
goto end_all;
}
} else {
// allocate dummy data to convert monochrome images.
origAlpha.rowBytes = avif->width * sizeof(uint16_t);
Expand Down Expand Up @@ -750,6 +797,8 @@ static CGImageRef CreateImage16U(avifImage * avif) {
dummyCrData = NULL;
free(dummyAlphaData);
dummyAlphaData = NULL;
free(scaledAlphaBufferData);
scaledAlphaBufferData = NULL;

err = vImageConvert_YpCbCrToARGB_GenerateConversion(&matrix,
&pixelRange,
Expand Down Expand Up @@ -935,6 +984,7 @@ static CGImageRef CreateImage16U(avifImage * avif) {
free(resultBufferData);
free(argbBufferData);
free(ayuvBufferData);
free(scaledAlphaBufferData);
free(dummyCbData);
free(dummyCrData);
free(dummyAlphaData);
Expand Down