From 38600bf7227e4303a1d47ef512387679c3799ea4 Mon Sep 17 00:00:00 2001 From: psi Date: Fri, 13 Mar 2020 10:49:00 +0900 Subject: [PATCH 01/18] Support libavif v0.6.0(decoder) --- Cartfile | 2 +- Package.swift | 2 +- SDWebImageAVIFCoder.podspec | 2 +- .../Classes/SDImageAVIFCoder.m | 93 ++++++++++++++++--- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/Cartfile b/Cartfile index 988b4ee..430283e 100644 --- a/Cartfile +++ b/Cartfile @@ -1,2 +1,2 @@ github "SDWebImage/SDWebImage" ~> 5.0 -github "SDWebImage/libavif-Xcode" ~> 0.5 \ No newline at end of file +github "SDWebImage/libavif-Xcode" ~> 0.6 \ No newline at end of file diff --git a/Package.swift b/Package.swift index 83bc09b..c4f7b44 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0"), - .package(url: "https://github.com/SDWebImage/libavif-Xcode.git", from: "0.5.0") + .package(url: "https://github.com/SDWebImage/libavif-Xcode.git", from: "0.6.0") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/SDWebImageAVIFCoder.podspec b/SDWebImageAVIFCoder.podspec index d63ea5c..e0b9213 100644 --- a/SDWebImageAVIFCoder.podspec +++ b/SDWebImageAVIFCoder.podspec @@ -36,5 +36,5 @@ Which is built based on the open-sourced libavif codec. s.source_files = 'SDWebImageAVIFCoder/Classes/**/*', 'SDWebImageAVIFCoder/Module/SDWebImageAVIFCoder.h' s.dependency 'SDWebImage', '~> 5.0' - s.dependency 'libavif', '~> 0.5' + s.dependency 'libavif', '~> 0.6' end diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 7b151e0..62c6b2b 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -53,7 +53,16 @@ static void SetupConversionInfo(avifImage * avif, avifReformatState* state, vImage_YpCbCrToARGBMatrix* matrix, vImage_YpCbCrPixelRange* pixelRange) { - avifPrepareReformatState(avif, state); + avifRGBImage emptyRGBImage = { + .width = avif->width, + .height = avif->height, + .depth = avif->depth, + .format = AVIF_RGB_FORMAT_ARGB, + + .pixels = NULL, + .rowBytes = 0, + }; + avifPrepareReformatState(avif, &emptyRGBImage, state); // Setup Matrix matrix->Yp = 1.0f; @@ -144,6 +153,7 @@ static CGImageRef CreateImage8(avifImage * avif) { uint8_t* argbBufferData = NULL; uint8_t* dummyCbData = NULL; uint8_t* dummyCrData = NULL; + uint8_t* scaledAlphaBufferData = NULL; vImage_Error err = kvImageNoError; @@ -413,12 +423,52 @@ static CGImageRef CreateImage8(avifImage * avif) { } if(hasAlpha) { // alpha - vImage_Buffer alphaBuffer = { - .data = avif->alphaPlane, - .width = avif->width, - .height = avif->height, - .rowBytes = avif->alphaRowBytes, - }; + vImage_Buffer alphaBuffer = {0}; + if(avif->alphaRange == AVIF_RANGE_LIMITED) { + float* floatAlphaBufferData = NULL; + floatAlphaBufferData = calloc(avif->width * avif->height, sizeof(float)); + scaledAlphaBufferData = calloc(avif->width * avif->height, sizeof(uint8_t)); + if(floatAlphaBufferData == NULL || scaledAlphaBufferData == NULL) { + err = kvImageMemoryAllocationError; + goto end_prepare_alpha; + } + vImage_Buffer origAlphaBuffer = { + .data = avif->alphaPlane, + .width = avif->width, + .height = avif->height, + .rowBytes = avif->alphaRowBytes, + }; + vImage_Buffer floatAlphaBuffer = { + .data = floatAlphaBufferData, + .width = avif->width, + .height = avif->height, + .rowBytes = avif->width * sizeof(float), + }; + alphaBuffer.width = avif->width; + alphaBuffer.height = avif->height; + alphaBuffer.data = scaledAlphaBufferData; + alphaBuffer.rowBytes = avif->width * sizeof(uint8_t); + err = vImageConvert_Planar8toPlanarF(&origAlphaBuffer, &floatAlphaBuffer, 255.0f, 0.0f, kvImageNoFlags); + if(err != kvImageNoError) { + NSLog(@"Failed to convert alpha planes from uint8 to float: %ld", err); + goto end_prepare_alpha; + } + err = vImageConvert_PlanarFtoPlanar8(&floatAlphaBuffer, &alphaBuffer, 235.0f, 16.0f, kvImageNoFlags); + if(err != kvImageNoError) { + NSLog(@"Failed to convert alpha planes from float to uint8: %ld", err); + goto end_prepare_alpha; + } + end_prepare_alpha: + free(floatAlphaBufferData); + if(err != kvImageNoError) { + goto end_alpha; + } + } else { + alphaBuffer.width = avif->width; + alphaBuffer.height = avif->height; + alphaBuffer.data = avif->alphaPlane; + alphaBuffer.rowBytes = avif->alphaRowBytes; + } if(monochrome) { // alpha_mono uint8_t* tmpBufferData = NULL; uint8_t* monoBufferData = NULL; @@ -515,6 +565,7 @@ static CGImageRef CreateImage8(avifImage * avif) { free(argbBufferData); free(dummyCbData); free(dummyCrData); + free(scaledAlphaBufferData); return result; } @@ -665,18 +716,32 @@ static CGImageRef CreateImage16U(avifImage * avif) { .height = avif->height, .rowBytes = avif->width * sizeof(uint16_t), }; + float offset = 0.0f; + float rangeMax = 0.0f; + if(avif->depth == 10) { + if(avif->alphaRange == AVIF_RANGE_LIMITED) { + offset = 64.0f; + rangeMax = 940.0f; + } else { + offset = 0.0f; + rangeMax = 1023.0f; + } + } else if(avif->depth == 12) { + if(avif->alphaRange == AVIF_RANGE_LIMITED) { + offset = 256.0f; + rangeMax = 3760.0f; + } else { + offset = 0.0f; + rangeMax = 4095.0f; + } + } + float const scale = (float)(rangeMax - offset) / 65535.0f; 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); + err = vImageConvert_FTo16U(&floatAlphaBuffer, &scaledAlphaBuffer, offset, scale, kvImageNoFlags); if(err != kvImageNoError) { NSLog(@"Failed to convert alpha planes from uint16 to float: %ld", err); goto end_prepare_alpha; From 641d79132016fa32ee13f1d140484b9f1b3f7e81 Mon Sep 17 00:00:00 2001 From: psi Date: Fri, 13 Mar 2020 11:04:53 +0900 Subject: [PATCH 02/18] Support new encoding API. --- .../Classes/SDImageAVIFCoder.m | 54 +++++-------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 62c6b2b..7536c30 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -1056,30 +1056,6 @@ static CGImageRef CreateImage16U(avifImage * avif) { return result; } -static void FillRGBABufferWithAVIFImage(vImage_Buffer *red, vImage_Buffer *green, vImage_Buffer *blue, vImage_Buffer *alpha, avifImage *img) { - red->width = img->width; - red->height = img->height; - red->data = img->rgbPlanes[AVIF_CHAN_R]; - red->rowBytes = img->rgbRowBytes[AVIF_CHAN_R]; - - green->width = img->width; - green->height = img->height; - green->data = img->rgbPlanes[AVIF_CHAN_G]; - green->rowBytes = img->rgbRowBytes[AVIF_CHAN_G]; - - blue->width = img->width; - blue->height = img->height; - blue->data = img->rgbPlanes[AVIF_CHAN_B]; - blue->rowBytes = img->rgbRowBytes[AVIF_CHAN_B]; - - if (img->alphaPlane != NULL) { - alpha->width = img->width; - alpha->height = img->height; - alpha->data = img->alphaPlane; - alpha->rowBytes = img->alphaRowBytes; - } -} - @implementation SDImageAVIFCoder + (instancetype)sharedCoder { @@ -1219,32 +1195,28 @@ - (nullable NSData *)encodedDataWithImage:(nullable UIImage *)image format:(SDIm } avifPixelFormat avifFormat = AVIF_PIXEL_FORMAT_YUV444; - enum avifPlanesFlags planesFlags = hasAlpha ? AVIF_PLANES_RGB | AVIF_PLANES_A : AVIF_PLANES_RGB; - + avifImage *avif = avifImageCreate((int)width, (int)height, 8, avifFormat); if (!avif) { free(dest.data); return nil; } - avifImageAllocatePlanes(avif, planesFlags); - + avifRGBImage rgb = { + .width = (uint32_t)width, + .height = (uint32_t)height, + .depth = 8, + .format = hasAlpha ? AVIF_RGB_FORMAT_ARGB : AVIF_RGB_FORMAT_RGB, + .pixels = dest.data, + .rowBytes = (uint32_t)dest.rowBytes, + }; + avifImageRGBToYUV(avif, &rgb); + free(dest.data); + dest.data = NULL; + NSData *iccProfile = (__bridge_transfer NSData *)CGColorSpaceCopyICCProfile([SDImageCoderHelper colorSpaceGetDeviceRGB]); avifImageSetProfileICC(avif, (uint8_t *)iccProfile.bytes, iccProfile.length); - vImage_Buffer red, green, blue, alpha; - FillRGBABufferWithAVIFImage(&red, &green, &blue, &alpha, avif); - - if (hasAlpha) { - v_error = vImageConvert_ARGB8888toPlanar8(&dest, &alpha, &red, &green, &blue, kvImageNoFlags); - } else { - v_error = vImageConvert_RGB888toPlanar8(&dest, &red, &green, &blue, kvImageNoFlags); - } - free(dest.data); - if (v_error != kvImageNoError) { - return nil; - } - double compressionQuality = 1; if (options[SDImageCoderEncodeCompressionQuality]) { compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue]; From 54ca88beaf1884b9bc84e22e59f337c43d749132 Mon Sep 17 00:00:00 2001 From: psi Date: Tue, 3 Mar 2020 21:59:25 +0900 Subject: [PATCH 03/18] [WIP] call vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction --- SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 7536c30..8f46cfd 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -30,7 +30,11 @@ static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) // use avif->nclx.colourPrimaries and avif->nclx.transferCharacteristics to detect appropriate color space. CGColorSpaceRef colorSpace = NULL; if(monochrome){ - colorSpace = CGColorSpaceCreateDeviceGray(); + vImage_Error err; + vImageWhitePoint whitePoint ; + vImageTransferFunction transferFunction; + CGColorRenderingIntent intent; + colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&whitePoint, &transferFunction, intent, kvImageNoFlags, &err); }else{ colorSpace = CGColorSpaceCreateDeviceRGB(); } From c996a59aa0c9c8f5ce9c3766dc650a7affdcbafb Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 19:06:04 +0900 Subject: [PATCH 04/18] [WIP] Implement BT709(sRGB) and BT2020 --- .../Classes/SDImageAVIFCoder.m | 81 ++++++++++++++++--- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 8f46cfd..c47313c 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -16,6 +16,68 @@ static void FreeImageData(void *info, const void *data, size_t size) { free((void *)data); } +static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { + static CGColorSpaceRef defaultColorSpace; + { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultColorSpace = CGColorSpaceCreateDeviceGray(); + }); + } +default_color_space: + *ref = defaultColorSpace; + *shouldRelease = FALSE; +} +static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { + static CGColorSpaceRef defaultColorSpace; + static CGColorSpaceRef bt709; + static CGColorSpaceRef bt2020; + { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(iOS 9.0, tvOS 9.0, *)) { + defaultColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + } else { + defaultColorSpace = CGColorSpaceCreateDeviceRGB(); + } + if (@available(macOS 10.11, *)) { + bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); + } else { + bt709 = defaultColorSpace; + } + if (@available(macOS 10.11, *)) { + bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); + } else { + bt2020 = defaultColorSpace; + } + }); + } + + if((avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) && avif->icc.data && avif->icc.size) { + if (@available(macOS 10.12, *)) { + *ref = CGColorSpaceCreateWithICCData(avif->icc.data); + *shouldRelease = TRUE; + return; + } + } + uint16_t colorPrimaries = avif->nclx.colourPrimaries; + uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN) { + goto default_color_space; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { + *ref = bt709; + *shouldRelease = FALSE; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { + *ref = bt2020; + *shouldRelease = FALSE; + } + +default_color_space: + *ref = defaultColorSpace; + *shouldRelease = FALSE; +} static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) { BOOL monochrome = avif->yuvPlanes[1] == NULL || avif->yuvPlanes[2] == NULL; @@ -26,20 +88,17 @@ static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, result->data, result->rowBytes * result->height, FreeImageData); CGBitmapInfo bitmapInfo = usesU16 ? kCGBitmapByteOrder16Host : kCGBitmapByteOrderDefault; bitmapInfo |= hasAlpha ? kCGImageAlphaFirst : kCGImageAlphaNone; - // FIXME: (ledyba-z): Set appropriate color space. - // use avif->nclx.colourPrimaries and avif->nclx.transferCharacteristics to detect appropriate color space. + + // Calc color space CGColorSpaceRef colorSpace = NULL; + BOOL shouldReleaseColorSpace = FALSE; if(monochrome){ - vImage_Error err; - vImageWhitePoint whitePoint ; - vImageTransferFunction transferFunction; - CGColorRenderingIntent intent; - colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&whitePoint, &transferFunction, intent, kvImageNoFlags, &err); + CalcColorSpaceMono(avif, &colorSpace, &shouldReleaseColorSpace); }else{ - colorSpace = CGColorSpaceCreateDeviceRGB(); + CalcColorSpaceRGB(avif, &colorSpace, &shouldReleaseColorSpace); } + CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; - size_t bitsPerComponent = usesU16 ? 16 : 8; size_t bitsPerPixel = components * bitsPerComponent; size_t rowBytes = result->width * components * (usesU16 ? sizeof(uint16_t) : sizeof(uint8_t)); @@ -47,7 +106,9 @@ static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) CGImageRef imageRef = CGImageCreate(result->width, result->height, bitsPerComponent, bitsPerPixel, rowBytes, colorSpace, bitmapInfo, provider, NULL, NO, renderingIntent); // clean up - CGColorSpaceRelease(colorSpace); + if(shouldReleaseColorSpace) { + CGColorSpaceRelease(colorSpace); + } CGDataProviderRelease(provider); return imageRef; From d1b7f9be3eff46ea26071c6204b9c376204f0ab5 Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 19:30:20 +0900 Subject: [PATCH 05/18] Support sRGB --- .../Classes/SDImageAVIFCoder.m | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index c47313c..6238b5d 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -17,6 +17,7 @@ static void FreeImageData(void *info, const void *data, size_t size) { free((void *)data); } static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { + [SDImageCoderHelper colorSpaceGetDeviceRGB]; static CGColorSpaceRef defaultColorSpace; { static dispatch_once_t onceToken; @@ -30,22 +31,24 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho } static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { static CGColorSpaceRef defaultColorSpace; + static CGColorSpaceRef sRGB; static CGColorSpaceRef bt709; static CGColorSpaceRef bt2020; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ + defaultColorSpace = CGColorSpaceCreateDeviceRGB(); if (@available(iOS 9.0, tvOS 9.0, *)) { - defaultColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + sRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); } else { - defaultColorSpace = CGColorSpaceCreateDeviceRGB(); + sRGB = defaultColorSpace; } - if (@available(macOS 10.11, *)) { + if (@available(iOS 9.0, tvOS 9.0, *)) { bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); } else { bt709 = defaultColorSpace; } - if (@available(macOS 10.11, *)) { + if (@available(iOS 9.0, tvOS 9.0, *)) { bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); } else { bt2020 = defaultColorSpace; @@ -54,7 +57,7 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou } if((avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) && avif->icc.data && avif->icc.size) { - if (@available(macOS 10.12, *)) { + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { *ref = CGColorSpaceCreateWithICCData(avif->icc.data); *shouldRelease = TRUE; return; @@ -68,10 +71,17 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { *ref = bt709; *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + *ref = sRGB; + *shouldRelease = FALSE; + return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { *ref = bt2020; *shouldRelease = FALSE; + return; } default_color_space: From 001d9e1e8b94b492723008465e0e7a1232fe537c Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 19:37:39 +0900 Subject: [PATCH 06/18] upload artifacts --- .github/workflows/check-image-decoding.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/check-image-decoding.yml b/.github/workflows/check-image-decoding.yml index 64e47f7..4c4f223 100644 --- a/.github/workflows/check-image-decoding.yml +++ b/.github/workflows/check-image-decoding.yml @@ -34,6 +34,11 @@ jobs: file=$(basename ${file}) "${CMD}" "${file}" "./decoded/${file}.png" done + - name: Upload result + uses: actions/upload-artifact@v1 + with: + name: decoded-images + path: decoded - name: Install imagemagick to compare images. shell: bash run: brew install imagemagick From baf59163a946caf3cbc7f700b63de3549d7454eb Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 19:40:26 +0900 Subject: [PATCH 07/18] Add @available gurd for macOS --- SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 6238b5d..3da2535 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -38,17 +38,17 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ defaultColorSpace = CGColorSpaceCreateDeviceRGB(); - if (@available(iOS 9.0, tvOS 9.0, *)) { + if (@available(macOS 10.5, iOS 9.0, tvOS 9.0, *)) { sRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); } else { sRGB = defaultColorSpace; } - if (@available(iOS 9.0, tvOS 9.0, *)) { + if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); } else { bt709 = defaultColorSpace; } - if (@available(iOS 9.0, tvOS 9.0, *)) { + if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); } else { bt2020 = defaultColorSpace; From 9086e775f4574424ad42c8753e746dc7698be050 Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 19:41:32 +0900 Subject: [PATCH 08/18] Fix artifact path --- .github/workflows/check-image-decoding.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-image-decoding.yml b/.github/workflows/check-image-decoding.yml index 4c4f223..800f9da 100644 --- a/.github/workflows/check-image-decoding.yml +++ b/.github/workflows/check-image-decoding.yml @@ -38,7 +38,7 @@ jobs: uses: actions/upload-artifact@v1 with: name: decoded-images - path: decoded + path: avif-sample-images/decoded - name: Install imagemagick to compare images. shell: bash run: brew install imagemagick From 7af803725f96bb48c19b0beaea35310a94f7dc4e Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 21:50:58 +0900 Subject: [PATCH 09/18] add all built-in RGB color spaces. --- .../Classes/SDImageAVIFCoder.m | 99 ++++++++++++++++--- 1 file changed, 88 insertions(+), 11 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 3da2535..bd195e4 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -29,11 +29,19 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho *ref = defaultColorSpace; *shouldRelease = FALSE; } + static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { - static CGColorSpaceRef defaultColorSpace; - static CGColorSpaceRef sRGB; - static CGColorSpaceRef bt709; - static CGColorSpaceRef bt2020; + static CGColorSpaceRef defaultColorSpace = NULL; + static CGColorSpaceRef sRGB = NULL; + static CGColorSpaceRef bt709 = NULL; + static CGColorSpaceRef bt2020 = NULL; + static CGColorSpaceRef bt2020hlg = NULL; + static CGColorSpaceRef bt2020pq = NULL; + static CGColorSpaceRef bt2020linear = NULL; + static CGColorSpaceRef p3 = NULL; + static CGColorSpaceRef p3hlg = NULL; + static CGColorSpaceRef p3pq = NULL; + static CGColorSpaceRef p3linear = NULL; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -45,13 +53,35 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou } if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); + bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); } else { bt709 = defaultColorSpace; + bt2020 = defaultColorSpace; + bt2020hlg = defaultColorSpace; } - if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { - bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); + if (@available(macOS 10.11.2, iOS 9.3, tvOS 9.3, *)) { + p3 = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); } else { - bt2020 = defaultColorSpace; + p3 = defaultColorSpace; + } + if (@available(macOS 10.14.3, iOS 12.3, tvOS 12.3, *)) { + p3linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3); + bt2020linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearITUR_2020); + } else { + p3linear = defaultColorSpace; + bt2020linear = defaultColorSpace; + } + if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { + bt2020hlg = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_HLG); + p3hlg = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_HLG); + bt2020pq = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_PQ_EOTF); + p3pq = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_PQ_EOTF); + + } else { + bt2020hlg = defaultColorSpace; + p3hlg = defaultColorSpace; + bt2020pq = defaultColorSpace; + p3pq = defaultColorSpace; } }); } @@ -65,24 +95,71 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou } uint16_t colorPrimaries = avif->nclx.colourPrimaries; uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN) { + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN) { goto default_color_space; } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { *ref = bt709; *shouldRelease = FALSE; return; } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { *ref = sRGB; *shouldRelease = FALSE; return; } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { *ref = bt2020; *shouldRelease = FALSE; return; } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + *ref = bt2020linear; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2100 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG) { + *ref = bt2020hlg; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2100 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_PQ) { + *ref = bt2020pq; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + *ref = p3; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG) { + *ref = p3hlg; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_PQ) { + *ref = p3pq; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + *ref = p3linear; + *shouldRelease = FALSE; + return; + } default_color_space: *ref = defaultColorSpace; From d72b92f49bc18045a5ca5190850dfd7cb72ea36e Mon Sep 17 00:00:00 2001 From: psi Date: Sun, 8 Mar 2020 21:54:54 +0900 Subject: [PATCH 10/18] add linear sRGB --- SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index bd195e4..993d180 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -33,6 +33,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { static CGColorSpaceRef defaultColorSpace = NULL; static CGColorSpaceRef sRGB = NULL; + static CGColorSpaceRef sRGBlinear = NULL; static CGColorSpaceRef bt709 = NULL; static CGColorSpaceRef bt2020 = NULL; static CGColorSpaceRef bt2020hlg = NULL; @@ -64,6 +65,11 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou } else { p3 = defaultColorSpace; } + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { + sRGBlinear = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB); + } else { + sRGBlinear = defaultColorSpace; + } if (@available(macOS 10.14.3, iOS 12.3, tvOS 12.3, *)) { p3linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3); bt2020linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearITUR_2020); @@ -111,6 +117,12 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou *shouldRelease = FALSE; return; } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + *ref = sRGBlinear; + *shouldRelease = FALSE; + return; + } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { From 5568e75adc3730977a78fc47b9981c48b760434e Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 14:03:01 +0900 Subject: [PATCH 11/18] Make color spaces as lazy as possible. --- .../Classes/SDImageAVIFCoder.m | 164 ++++++++++++------ 1 file changed, 107 insertions(+), 57 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 993d180..058d434 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -32,63 +32,10 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { static CGColorSpaceRef defaultColorSpace = NULL; - static CGColorSpaceRef sRGB = NULL; - static CGColorSpaceRef sRGBlinear = NULL; - static CGColorSpaceRef bt709 = NULL; - static CGColorSpaceRef bt2020 = NULL; - static CGColorSpaceRef bt2020hlg = NULL; - static CGColorSpaceRef bt2020pq = NULL; - static CGColorSpaceRef bt2020linear = NULL; - static CGColorSpaceRef p3 = NULL; - static CGColorSpaceRef p3hlg = NULL; - static CGColorSpaceRef p3pq = NULL; - static CGColorSpaceRef p3linear = NULL; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ defaultColorSpace = CGColorSpaceCreateDeviceRGB(); - if (@available(macOS 10.5, iOS 9.0, tvOS 9.0, *)) { - sRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - } else { - sRGB = defaultColorSpace; - } - if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { - bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); - bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); - } else { - bt709 = defaultColorSpace; - bt2020 = defaultColorSpace; - bt2020hlg = defaultColorSpace; - } - if (@available(macOS 10.11.2, iOS 9.3, tvOS 9.3, *)) { - p3 = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); - } else { - p3 = defaultColorSpace; - } - if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { - sRGBlinear = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB); - } else { - sRGBlinear = defaultColorSpace; - } - if (@available(macOS 10.14.3, iOS 12.3, tvOS 12.3, *)) { - p3linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3); - bt2020linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearITUR_2020); - } else { - p3linear = defaultColorSpace; - bt2020linear = defaultColorSpace; - } - if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { - bt2020hlg = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_HLG); - p3hlg = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_HLG); - bt2020pq = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_PQ_EOTF); - p3pq = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_PQ_EOTF); - - } else { - bt2020hlg = defaultColorSpace; - p3hlg = defaultColorSpace; - bt2020pq = defaultColorSpace; - p3pq = defaultColorSpace; - } }); } @@ -101,24 +48,55 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou } uint16_t colorPrimaries = avif->nclx.colourPrimaries; uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN && - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN) { - goto default_color_space; + if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || + colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { + static CGColorSpaceRef bt709 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { + bt709 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); + } else { + bt709 = defaultColorSpace; + } + }); *ref = bt709; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef sRGB = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.5, iOS 9.0, tvOS 9.0, *)) { + sRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + } else { + sRGB = defaultColorSpace; + } + }); *ref = sRGB; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + static CGColorSpaceRef sRGBlinear = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { + sRGBlinear = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB); + } else { + sRGBlinear = defaultColorSpace; + } + }); *ref = sRGBlinear; *shouldRelease = FALSE; return; @@ -126,54 +104,126 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { + static CGColorSpaceRef bt2020 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) { + bt2020 = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); + } else { + bt2020 = defaultColorSpace; + } + }); *ref = bt2020; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + static CGColorSpaceRef bt2020linear = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.3, iOS 12.3, tvOS 12.3, *)) { + bt2020linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearITUR_2020); + } else { + bt2020linear = defaultColorSpace; + } + }); *ref = bt2020linear; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2100 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG) { + static CGColorSpaceRef bt2020hlg = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { + bt2020hlg = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_HLG); + } else { + bt2020hlg = defaultColorSpace; + } + }); *ref = bt2020hlg; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2100 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_PQ) { + static CGColorSpaceRef bt2020pq = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { + bt2020pq = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020_PQ_EOTF); + } else { + bt2020pq = defaultColorSpace; + } + }); *ref = bt2020pq; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef p3 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.11.2, iOS 9.3, tvOS 9.3, *)) { + p3 = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3); + } else { + p3 = defaultColorSpace; + } + }); *ref = p3; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG) { + static CGColorSpaceRef p3hlg = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { + p3hlg = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_HLG); + } else { + p3hlg = defaultColorSpace; + } + }); + *ref = p3hlg; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_PQ) { + static CGColorSpaceRef p3pq = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.6, iOS 13.0, tvOS 13.0, *)) { + p3pq = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3_PQ_EOTF); + } else { + p3pq = defaultColorSpace; + } + }); *ref = p3pq; *shouldRelease = FALSE; return; } if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR) { + static CGColorSpaceRef p3linear = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (@available(macOS 10.14.3, iOS 12.3, tvOS 12.3, *)) { + p3linear = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3); + } else { + p3linear = defaultColorSpace; + } + }); *ref = p3linear; *shouldRelease = FALSE; return; } -default_color_space: *ref = defaultColorSpace; *shouldRelease = FALSE; } From 420a6aad8314f7d6d2cbc543d4c5bc4db630fe3b Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 14:07:31 +0900 Subject: [PATCH 12/18] handle unknown color spcaces properly --- .../Classes/SDImageAVIFCoder.m | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 058d434..cf30e89 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -38,13 +38,22 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou defaultColorSpace = CGColorSpaceCreateDeviceRGB(); }); } - - if((avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) && avif->icc.data && avif->icc.size) { - if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { - *ref = CGColorSpaceCreateWithICCData(avif->icc.data); - *shouldRelease = TRUE; - return; + if(avif->profileFormat == AVIF_PROFILE_FORMAT_NONE) { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; + } + if(avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) { + if(avif->icc.data && avif->icc.size) { + if(@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { + *ref = CGColorSpaceCreateWithICCData(avif->icc.data); + *shouldRelease = TRUE; + return; + } } + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; } uint16_t colorPrimaries = avif->nclx.colourPrimaries; uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; From 9082d46783d7f4df6888b75a6fafd6dad575b173 Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 16:00:02 +0900 Subject: [PATCH 13/18] Crete monochrome color spaces --- .../Classes/SDImageAVIFCoder.m | 256 +++++++++++++++++- 1 file changed, 250 insertions(+), 6 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index cf30e89..8efeedb 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -16,8 +16,161 @@ static void FreeImageData(void *info, const void *data, size_t size) { free((void *)data); } + +static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* const white) { + float primaries[8]; + avifNclxColourPrimariesGetValues(colorPrimaries, primaries); + white->white_x = primaries[6]; + white->white_y = primaries[7]; +} + +static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageTransferFunction* const tf) { + static const float alpha = 1.099296826809442f; + static const float beta = 0.018053968510807f; + /* + // R' = c0 * pow( c1 * R + c2, gamma ) + c3, (R >= cutoff) + // R' = c4 * R + c5 (R < cutoff) + */ + + // See: https://www.itu.int/rec/T-REC-H.273/en + switch(transferCharacteristics) { + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5 + tf->cutoff = -INFINITY; + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->c3 = 0.0f; + tf->c4 = 0.0f; + tf->c5 = 0.0f; + tf->gamma = 1.0f/2.8f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709: // 1 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT601: // 6 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT: // 14 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT: // 15 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST240: // 7 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.0f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR: // 8 + tf->cutoff = INFINITY; + // + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 1.0f; + tf->c3 = 0.0f; + // + tf->c4 = 4.0f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_IEC61966: // 11 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT1361_EXTENDED: // 12 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB: // 13 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 1.0f/2.4f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 12.92f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST428: // 17 + tf->cutoff = -INFINITY; + // + tf->c0 = 1.0f; + tf->c1 = 48.0f / 52.37f; + tf->c2 = 0.0f; + tf->gamma = 1.0f/2.6f; + tf->c3 = 0.0f; + // + tf->c4 = 1.0f; + tf->c5 = 0.0f; + break; + // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a default. + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_SQRT: // 10 + // + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN: // 0 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED: // 2 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA22: // 4 + default: + tf->cutoff = -INFINITY; + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->c3 = 0.0f; + tf->c4 = 0.0f; + tf->c5 = 0.0f; + tf->gamma = 1.0f/2.2f; + break; + } +} +static CGColorSpaceRef CreateMonoColorSpace(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { + if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { + vImage_Error err; + vImageWhitePoint white; + vImageTransferFunction transfer; + CalcWhitePoint(colorPrimaries, &white); + CalcTransferFunction(transferCharacteristics, &transfer); + CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&white, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); + if(err != kvImageNoError) { + NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); + return NULL; + } + return colorSpace; + }else{ + return NULL; + } +} + static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { - [SDImageCoderHelper colorSpaceGetDeviceRGB]; static CGColorSpaceRef defaultColorSpace; { static dispatch_once_t onceToken; @@ -25,9 +178,98 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho defaultColorSpace = CGColorSpaceCreateDeviceGray(); }); } -default_color_space: - *ref = defaultColorSpace; - *shouldRelease = FALSE; + if(avif->profileFormat == AVIF_PROFILE_FORMAT_NONE) { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; + } + if(avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) { + if(avif->icc.data && avif->icc.size) { + if(@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { + *ref = CGColorSpaceCreateWithICCData(avif->icc.data); + *shouldRelease = TRUE; + return; + } + } + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; + } + uint16_t const colorPrimaries = avif->nclx.colourPrimaries; + uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; + if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || + colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef sRGB = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sRGB = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + if(sRGB == NULL) { + sRGB = defaultColorSpace; + } + }); + *ref = sRGB; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { + static CGColorSpaceRef bt709 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bt709 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + if(bt709 == NULL) { + bt709 = defaultColorSpace; + } + }); + *ref = bt709; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { + static CGColorSpaceRef bt2020 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bt2020 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + if(bt2020 == NULL) { + bt2020 = defaultColorSpace; + } + }); + *ref = bt2020; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef p3 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + p3 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + if(p3 == NULL) { + p3 = defaultColorSpace; + } + }); + *ref = p3; + *shouldRelease = FALSE; + return; + } + + *ref = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + if(*ref != NULL) { + *shouldRelease = TRUE; + } else { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + } } static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { @@ -55,8 +297,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou *shouldRelease = FALSE; return; } - uint16_t colorPrimaries = avif->nclx.colourPrimaries; - uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; + uint16_t const colorPrimaries = avif->nclx.colourPrimaries; + uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || @@ -232,6 +474,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou *shouldRelease = FALSE; return; } + // TODO(ledyba-z): We can support more color spaces + // using vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction *ref = defaultColorSpace; *shouldRelease = FALSE; From 834437fdc92cbc36d5692b648b4f84812a5bb537 Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 17:08:13 +0900 Subject: [PATCH 14/18] Create RGB color spaces from ColorPrimaries and TransferCharacteristics --- .../Classes/SDImageAVIFCoder.m | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 8efeedb..7a3966f 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -24,7 +24,21 @@ static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* cons white->white_y = primaries[7]; } +static void CalcRGBPrimaries(uint16_t const colorPrimaries, vImageRGBPrimaries* const prim) { + float primaries[8]; + avifNclxColourPrimariesGetValues(colorPrimaries, primaries); + prim->red_x = primaries[0]; + prim->red_y = primaries[1]; + prim->green_x = primaries[2]; + prim->green_y = primaries[3]; + prim->blue_x = primaries[4]; + prim->blue_y = primaries[5]; + prim->white_x = primaries[6]; + prim->white_y = primaries[7]; +} + static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageTransferFunction* const tf) { + // See: https://www.itu.int/rec/T-REC-H.273/en static const float alpha = 1.099296826809442f; static const float beta = 0.018053968510807f; /* @@ -32,7 +46,6 @@ static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageT // R' = c4 * R + c5 (R < cutoff) */ - // See: https://www.itu.int/rec/T-REC-H.273/en switch(transferCharacteristics) { case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5 tf->cutoff = -INFINITY; @@ -131,7 +144,7 @@ static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageT tf->c4 = 1.0f; tf->c5 = 0.0f; break; - // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a default. + // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a fallback. case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16 case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18 case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9 @@ -152,7 +165,7 @@ static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageT break; } } -static CGColorSpaceRef CreateMonoColorSpace(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { +static CGColorSpaceRef CreateColorSpaceMono(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { vImage_Error err; vImageWhitePoint white; @@ -162,6 +175,30 @@ static CGColorSpaceRef CreateMonoColorSpace(uint16_t const colorPrimaries, uint1 CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&white, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); if(err != kvImageNoError) { NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); + if(colorSpace != NULL) { + CGColorSpaceRelease(colorSpace); + } + return NULL; + } + return colorSpace; + }else{ + return NULL; + } +} + +static CGColorSpaceRef CreateColorSpaceRGB(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { + if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { + vImage_Error err; + vImageRGBPrimaries primaries; + vImageTransferFunction transfer; + CalcRGBPrimaries(colorPrimaries, &primaries); + CalcTransferFunction(transferCharacteristics, &transfer); + CGColorSpaceRef colorSpace = vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction(&primaries, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); + if(err != kvImageNoError) { + NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); + if(colorSpace != NULL) { + CGColorSpaceRelease(colorSpace); + } return NULL; } return colorSpace; @@ -210,7 +247,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static CGColorSpaceRef sRGB = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sRGB = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + sRGB = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); if(sRGB == NULL) { sRGB = defaultColorSpace; } @@ -224,7 +261,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static CGColorSpaceRef bt709 = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - bt709 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + bt709 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); if(bt709 == NULL) { bt709 = defaultColorSpace; } @@ -239,7 +276,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static CGColorSpaceRef bt2020 = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - bt2020 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + bt2020 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); if(bt2020 == NULL) { bt2020 = defaultColorSpace; } @@ -253,7 +290,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho static CGColorSpaceRef p3 = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - p3 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + p3 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); if(p3 == NULL) { p3 = defaultColorSpace; } @@ -263,7 +300,7 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho return; } - *ref = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); + *ref = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); if(*ref != NULL) { *shouldRelease = TRUE; } else { @@ -474,11 +511,14 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou *shouldRelease = FALSE; return; } - // TODO(ledyba-z): We can support more color spaces - // using vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction - *ref = defaultColorSpace; - *shouldRelease = FALSE; + *ref = CreateColorSpaceRGB(colorPrimaries, transferCharacteristics); + if(*ref != NULL) { + *shouldRelease = TRUE; + } else { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + } } static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) { From 477585dd47c4c1477bca08580ecf0cebdf0ff8d0 Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 18:51:13 +0900 Subject: [PATCH 15/18] Use CGColorSpaceCreateWithICCProfile for older iOS --- SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 7a3966f..6ade7f3 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -225,8 +225,12 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho if(@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { *ref = CGColorSpaceCreateWithICCData(avif->icc.data); *shouldRelease = TRUE; - return; + }else{ + CFDataRef iccData = CFDataCreateWithBytesNoCopy(NULL, avif->icc.data, avif->icc.size, NULL); + *ref = CGColorSpaceCreateWithICCProfile(iccData); + *shouldRelease = TRUE; } + return; } *ref = defaultColorSpace; *shouldRelease = FALSE; @@ -327,8 +331,12 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou if(@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { *ref = CGColorSpaceCreateWithICCData(avif->icc.data); *shouldRelease = TRUE; - return; + }else{ + CFDataRef iccData = CFDataCreateWithBytesNoCopy(NULL, avif->icc.data, avif->icc.size, NULL); + *ref = CGColorSpaceCreateWithICCProfile(iccData); + *shouldRelease = TRUE; } + return; } *ref = defaultColorSpace; *shouldRelease = FALSE; From f7ecb26ba05360d7d4c5ff02fc182598702dfcc7 Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 20:43:43 +0900 Subject: [PATCH 16/18] Avoid potential leaks. --- SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 6ade7f3..79ac849 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -226,8 +226,8 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho *ref = CGColorSpaceCreateWithICCData(avif->icc.data); *shouldRelease = TRUE; }else{ - CFDataRef iccData = CFDataCreateWithBytesNoCopy(NULL, avif->icc.data, avif->icc.size, NULL); - *ref = CGColorSpaceCreateWithICCProfile(iccData); + NSData* iccData = [NSData dataWithBytes:avif->icc.data length:avif->icc.size]; + *ref = CGColorSpaceCreateWithICCProfile((__bridge CFDataRef)iccData); *shouldRelease = TRUE; } return; @@ -332,8 +332,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou *ref = CGColorSpaceCreateWithICCData(avif->icc.data); *shouldRelease = TRUE; }else{ - CFDataRef iccData = CFDataCreateWithBytesNoCopy(NULL, avif->icc.data, avif->icc.size, NULL); - *ref = CGColorSpaceCreateWithICCProfile(iccData); + NSData* iccData = [NSData dataWithBytes:avif->icc.data length:avif->icc.size]; + *ref = CGColorSpaceCreateWithICCProfile((__bridge CFDataRef)iccData); *shouldRelease = TRUE; } return; From 08d517234c794f44b2ed175285ed9b33d1d66ab1 Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 21:06:16 +0900 Subject: [PATCH 17/18] We won't create CGColorSpaceRefs ourselves today. --- .../Classes/SDImageAVIFCoder.m | 284 +----------------- 1 file changed, 12 insertions(+), 272 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index 79ac849..bcb82c7 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -17,196 +17,6 @@ static void FreeImageData(void *info, const void *data, size_t size) { free((void *)data); } -static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* const white) { - float primaries[8]; - avifNclxColourPrimariesGetValues(colorPrimaries, primaries); - white->white_x = primaries[6]; - white->white_y = primaries[7]; -} - -static void CalcRGBPrimaries(uint16_t const colorPrimaries, vImageRGBPrimaries* const prim) { - float primaries[8]; - avifNclxColourPrimariesGetValues(colorPrimaries, primaries); - prim->red_x = primaries[0]; - prim->red_y = primaries[1]; - prim->green_x = primaries[2]; - prim->green_y = primaries[3]; - prim->blue_x = primaries[4]; - prim->blue_y = primaries[5]; - prim->white_x = primaries[6]; - prim->white_y = primaries[7]; -} - -static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageTransferFunction* const tf) { - // See: https://www.itu.int/rec/T-REC-H.273/en - static const float alpha = 1.099296826809442f; - static const float beta = 0.018053968510807f; - /* - // R' = c0 * pow( c1 * R + c2, gamma ) + c3, (R >= cutoff) - // R' = c4 * R + c5 (R < cutoff) - */ - - switch(transferCharacteristics) { - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5 - tf->cutoff = -INFINITY; - tf->c0 = 1.0f; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->c3 = 0.0f; - tf->c4 = 0.0f; - tf->c5 = 0.0f; - tf->gamma = 1.0f/2.8f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709: // 1 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT601: // 6 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT: // 14 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT: // 15 - tf->cutoff = beta; - // - tf->c0 = alpha; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 0.45f; - tf->c3 = -(alpha - 1); - // - tf->c4 = 4.5f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST240: // 7 - tf->cutoff = beta; - // - tf->c0 = alpha; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 0.45f; - tf->c3 = -(alpha - 1); - // - tf->c4 = 4.0f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR: // 8 - tf->cutoff = INFINITY; - // - tf->c0 = 1.0f; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 1.0f; - tf->c3 = 0.0f; - // - tf->c4 = 4.0f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_IEC61966: // 11 - tf->cutoff = beta; - // - tf->c0 = alpha; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 0.45f; - tf->c3 = -(alpha - 1); - // - tf->c4 = 4.5f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT1361_EXTENDED: // 12 - tf->cutoff = beta; - // - tf->c0 = alpha; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 0.45f; - tf->c3 = -(alpha - 1); - // - tf->c4 = 4.5f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB: // 13 - tf->cutoff = beta; - // - tf->c0 = alpha; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->gamma = 1.0f/2.4f; - tf->c3 = -(alpha - 1); - // - tf->c4 = 12.92f; - tf->c5 = 0.0f; - break; - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST428: // 17 - tf->cutoff = -INFINITY; - // - tf->c0 = 1.0f; - tf->c1 = 48.0f / 52.37f; - tf->c2 = 0.0f; - tf->gamma = 1.0f/2.6f; - tf->c3 = 0.0f; - // - tf->c4 = 1.0f; - tf->c5 = 0.0f; - break; - // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a fallback. - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_SQRT: // 10 - // - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN: // 0 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED: // 2 - case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA22: // 4 - default: - tf->cutoff = -INFINITY; - tf->c0 = 1.0f; - tf->c1 = 1.0f; - tf->c2 = 0.0f; - tf->c3 = 0.0f; - tf->c4 = 0.0f; - tf->c5 = 0.0f; - tf->gamma = 1.0f/2.2f; - break; - } -} -static CGColorSpaceRef CreateColorSpaceMono(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { - if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { - vImage_Error err; - vImageWhitePoint white; - vImageTransferFunction transfer; - CalcWhitePoint(colorPrimaries, &white); - CalcTransferFunction(transferCharacteristics, &transfer); - CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&white, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); - if(err != kvImageNoError) { - NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); - if(colorSpace != NULL) { - CGColorSpaceRelease(colorSpace); - } - return NULL; - } - return colorSpace; - }else{ - return NULL; - } -} - -static CGColorSpaceRef CreateColorSpaceRGB(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { - if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { - vImage_Error err; - vImageRGBPrimaries primaries; - vImageTransferFunction transfer; - CalcRGBPrimaries(colorPrimaries, &primaries); - CalcTransferFunction(transferCharacteristics, &transfer); - CGColorSpaceRef colorSpace = vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction(&primaries, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); - if(err != kvImageNoError) { - NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); - if(colorSpace != NULL) { - CGColorSpaceRelease(colorSpace); - } - return NULL; - } - return colorSpace; - }else{ - return NULL; - } -} - static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { static CGColorSpaceRef defaultColorSpace; { @@ -236,81 +46,12 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho *shouldRelease = FALSE; return; } - uint16_t const colorPrimaries = avif->nclx.colourPrimaries; - uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; - if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || - colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && - (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) { - *ref = defaultColorSpace; - *shouldRelease = FALSE; - return; - } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { - static CGColorSpaceRef sRGB = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sRGB = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); - if(sRGB == NULL) { - sRGB = defaultColorSpace; - } - }); - *ref = sRGB; - *shouldRelease = FALSE; - return; - } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { - static CGColorSpaceRef bt709 = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - bt709 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); - if(bt709 == NULL) { - bt709 = defaultColorSpace; - } - }); - *ref = bt709; - *shouldRelease = FALSE; - return; - } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && - (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { - static CGColorSpaceRef bt2020 = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - bt2020 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); - if(bt2020 == NULL) { - bt2020 = defaultColorSpace; - } - }); - *ref = bt2020; - *shouldRelease = FALSE; - return; - } - if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && - transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { - static CGColorSpaceRef p3 = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - p3 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); - if(p3 == NULL) { - p3 = defaultColorSpace; - } - }); - *ref = p3; - *shouldRelease = FALSE; - return; - } - - *ref = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); - if(*ref != NULL) { - *shouldRelease = TRUE; - } else { - *ref = defaultColorSpace; - *shouldRelease = FALSE; - } + // TODO: (ledyba-z): + // We can support other color spaces using + // vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction + // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces + *ref = defaultColorSpace; + *shouldRelease = FALSE; } static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { @@ -520,13 +261,12 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou return; } - *ref = CreateColorSpaceRGB(colorPrimaries, transferCharacteristics); - if(*ref != NULL) { - *shouldRelease = TRUE; - } else { - *ref = defaultColorSpace; - *shouldRelease = FALSE; - } + // TODO: (ledyba-z): + // We can support other color spaces using + // vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction + // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces + *ref = defaultColorSpace; + *shouldRelease = FALSE; } static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) { From 9da58e81ee9db30cbb2b54e445ea50a506b6386b Mon Sep 17 00:00:00 2001 From: psi Date: Mon, 9 Mar 2020 21:06:16 +0900 Subject: [PATCH 18/18] Revert "We won't create CGColorSpaceRefs ourselves today." This reverts commit 71ecfa1d0a2273d52f9042199221708297da319a. --- .../Classes/SDImageAVIFCoder.m | 284 +++++++++++++++++- 1 file changed, 272 insertions(+), 12 deletions(-) diff --git a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m index bcb82c7..79ac849 100644 --- a/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m +++ b/SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m @@ -17,6 +17,196 @@ static void FreeImageData(void *info, const void *data, size_t size) { free((void *)data); } +static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* const white) { + float primaries[8]; + avifNclxColourPrimariesGetValues(colorPrimaries, primaries); + white->white_x = primaries[6]; + white->white_y = primaries[7]; +} + +static void CalcRGBPrimaries(uint16_t const colorPrimaries, vImageRGBPrimaries* const prim) { + float primaries[8]; + avifNclxColourPrimariesGetValues(colorPrimaries, primaries); + prim->red_x = primaries[0]; + prim->red_y = primaries[1]; + prim->green_x = primaries[2]; + prim->green_y = primaries[3]; + prim->blue_x = primaries[4]; + prim->blue_y = primaries[5]; + prim->white_x = primaries[6]; + prim->white_y = primaries[7]; +} + +static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageTransferFunction* const tf) { + // See: https://www.itu.int/rec/T-REC-H.273/en + static const float alpha = 1.099296826809442f; + static const float beta = 0.018053968510807f; + /* + // R' = c0 * pow( c1 * R + c2, gamma ) + c3, (R >= cutoff) + // R' = c4 * R + c5 (R < cutoff) + */ + + switch(transferCharacteristics) { + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5 + tf->cutoff = -INFINITY; + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->c3 = 0.0f; + tf->c4 = 0.0f; + tf->c5 = 0.0f; + tf->gamma = 1.0f/2.8f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709: // 1 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT601: // 6 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT: // 14 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT: // 15 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST240: // 7 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.0f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR: // 8 + tf->cutoff = INFINITY; + // + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 1.0f; + tf->c3 = 0.0f; + // + tf->c4 = 4.0f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_IEC61966: // 11 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT1361_EXTENDED: // 12 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 0.45f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 4.5f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB: // 13 + tf->cutoff = beta; + // + tf->c0 = alpha; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->gamma = 1.0f/2.4f; + tf->c3 = -(alpha - 1); + // + tf->c4 = 12.92f; + tf->c5 = 0.0f; + break; + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST428: // 17 + tf->cutoff = -INFINITY; + // + tf->c0 = 1.0f; + tf->c1 = 48.0f / 52.37f; + tf->c2 = 0.0f; + tf->gamma = 1.0f/2.6f; + tf->c3 = 0.0f; + // + tf->c4 = 1.0f; + tf->c5 = 0.0f; + break; + // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a fallback. + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_SQRT: // 10 + // + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN: // 0 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED: // 2 + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA22: // 4 + default: + tf->cutoff = -INFINITY; + tf->c0 = 1.0f; + tf->c1 = 1.0f; + tf->c2 = 0.0f; + tf->c3 = 0.0f; + tf->c4 = 0.0f; + tf->c5 = 0.0f; + tf->gamma = 1.0f/2.2f; + break; + } +} +static CGColorSpaceRef CreateColorSpaceMono(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { + if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { + vImage_Error err; + vImageWhitePoint white; + vImageTransferFunction transfer; + CalcWhitePoint(colorPrimaries, &white); + CalcTransferFunction(transferCharacteristics, &transfer); + CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&white, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); + if(err != kvImageNoError) { + NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); + if(colorSpace != NULL) { + CGColorSpaceRelease(colorSpace); + } + return NULL; + } + return colorSpace; + }else{ + return NULL; + } +} + +static CGColorSpaceRef CreateColorSpaceRGB(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { + if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { + vImage_Error err; + vImageRGBPrimaries primaries; + vImageTransferFunction transfer; + CalcRGBPrimaries(colorPrimaries, &primaries); + CalcTransferFunction(transferCharacteristics, &transfer); + CGColorSpaceRef colorSpace = vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction(&primaries, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); + if(err != kvImageNoError) { + NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); + if(colorSpace != NULL) { + CGColorSpaceRelease(colorSpace); + } + return NULL; + } + return colorSpace; + }else{ + return NULL; + } +} + static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { static CGColorSpaceRef defaultColorSpace; { @@ -46,12 +236,81 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho *shouldRelease = FALSE; return; } - // TODO: (ledyba-z): - // We can support other color spaces using - // vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction - // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces - *ref = defaultColorSpace; - *shouldRelease = FALSE; + uint16_t const colorPrimaries = avif->nclx.colourPrimaries; + uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; + if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || + colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef sRGB = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sRGB = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); + if(sRGB == NULL) { + sRGB = defaultColorSpace; + } + }); + *ref = sRGB; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { + static CGColorSpaceRef bt709 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bt709 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); + if(bt709 == NULL) { + bt709 = defaultColorSpace; + } + }); + *ref = bt709; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { + static CGColorSpaceRef bt2020 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bt2020 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); + if(bt2020 == NULL) { + bt2020 = defaultColorSpace; + } + }); + *ref = bt2020; + *shouldRelease = FALSE; + return; + } + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { + static CGColorSpaceRef p3 = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + p3 = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); + if(p3 == NULL) { + p3 = defaultColorSpace; + } + }); + *ref = p3; + *shouldRelease = FALSE; + return; + } + + *ref = CreateColorSpaceMono(colorPrimaries, transferCharacteristics); + if(*ref != NULL) { + *shouldRelease = TRUE; + } else { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + } } static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) { @@ -261,12 +520,13 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou return; } - // TODO: (ledyba-z): - // We can support other color spaces using - // vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction - // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces - *ref = defaultColorSpace; - *shouldRelease = FALSE; + *ref = CreateColorSpaceRGB(colorPrimaries, transferCharacteristics); + if(*ref != NULL) { + *shouldRelease = TRUE; + } else { + *ref = defaultColorSpace; + *shouldRelease = FALSE; + } } static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) {