diff --git a/Example/Tests/Tests.m b/Example/Tests/Tests.m index cc0cddc..410f291 100644 --- a/Example/Tests/Tests.m +++ b/Example/Tests/Tests.m @@ -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; @@ -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; } @@ -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; @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -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]; } @@ -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]; } diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 64991db..7b151e0 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -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: @@ -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; @@ -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); @@ -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, @@ -935,6 +984,7 @@ static CGImageRef CreateImage16U(avifImage * avif) { free(resultBufferData); free(argbBufferData); free(ayuvBufferData); + free(scaledAlphaBufferData); free(dummyCbData); free(dummyCrData); free(dummyAlphaData);