|
16 | 16 | static void FreeImageData(void *info, const void *data, size_t size) {
|
17 | 17 | free((void *)data);
|
18 | 18 | }
|
| 19 | + |
| 20 | +static void CalcWhitePoint(uint16_t const colorPrimaries, vImageWhitePoint* const white) { |
| 21 | + float primaries[8]; |
| 22 | + avifNclxColourPrimariesGetValues(colorPrimaries, primaries); |
| 23 | + white->white_x = primaries[6]; |
| 24 | + white->white_y = primaries[7]; |
| 25 | +} |
| 26 | + |
| 27 | +static void CalcTransferFunction(uint16_t const transferCharacteristics, vImageTransferFunction* const tf) { |
| 28 | + static const float alpha = 1.099296826809442f; |
| 29 | + static const float beta = 0.018053968510807f; |
| 30 | + /* |
| 31 | + // R' = c0 * pow( c1 * R + c2, gamma ) + c3, (R >= cutoff) |
| 32 | + // R' = c4 * R + c5 (R < cutoff) |
| 33 | + */ |
| 34 | + |
| 35 | + // See: https://www.itu.int/rec/T-REC-H.273/en |
| 36 | + switch(transferCharacteristics) { |
| 37 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5 |
| 38 | + tf->cutoff = -INFINITY; |
| 39 | + tf->c0 = 1.0f; |
| 40 | + tf->c1 = 1.0f; |
| 41 | + tf->c2 = 0.0f; |
| 42 | + tf->c3 = 0.0f; |
| 43 | + tf->c4 = 0.0f; |
| 44 | + tf->c5 = 0.0f; |
| 45 | + tf->gamma = 1.0f/2.8f; |
| 46 | + break; |
| 47 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709: // 1 |
| 48 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT601: // 6 |
| 49 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT: // 14 |
| 50 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT: // 15 |
| 51 | + tf->cutoff = beta; |
| 52 | + // |
| 53 | + tf->c0 = alpha; |
| 54 | + tf->c1 = 1.0f; |
| 55 | + tf->c2 = 0.0f; |
| 56 | + tf->gamma = 0.45f; |
| 57 | + tf->c3 = -(alpha - 1); |
| 58 | + // |
| 59 | + tf->c4 = 4.5f; |
| 60 | + tf->c5 = 0.0f; |
| 61 | + break; |
| 62 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST240: // 7 |
| 63 | + tf->cutoff = beta; |
| 64 | + // |
| 65 | + tf->c0 = alpha; |
| 66 | + tf->c1 = 1.0f; |
| 67 | + tf->c2 = 0.0f; |
| 68 | + tf->gamma = 0.45f; |
| 69 | + tf->c3 = -(alpha - 1); |
| 70 | + // |
| 71 | + tf->c4 = 4.0f; |
| 72 | + tf->c5 = 0.0f; |
| 73 | + break; |
| 74 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR: // 8 |
| 75 | + tf->cutoff = INFINITY; |
| 76 | + // |
| 77 | + tf->c0 = 1.0f; |
| 78 | + tf->c1 = 1.0f; |
| 79 | + tf->c2 = 0.0f; |
| 80 | + tf->gamma = 1.0f; |
| 81 | + tf->c3 = 0.0f; |
| 82 | + // |
| 83 | + tf->c4 = 4.0f; |
| 84 | + tf->c5 = 0.0f; |
| 85 | + break; |
| 86 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_IEC61966: // 11 |
| 87 | + tf->cutoff = beta; |
| 88 | + // |
| 89 | + tf->c0 = alpha; |
| 90 | + tf->c1 = 1.0f; |
| 91 | + tf->c2 = 0.0f; |
| 92 | + tf->gamma = 0.45f; |
| 93 | + tf->c3 = -(alpha - 1); |
| 94 | + // |
| 95 | + tf->c4 = 4.5f; |
| 96 | + tf->c5 = 0.0f; |
| 97 | + break; |
| 98 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT1361_EXTENDED: // 12 |
| 99 | + tf->cutoff = beta; |
| 100 | + // |
| 101 | + tf->c0 = alpha; |
| 102 | + tf->c1 = 1.0f; |
| 103 | + tf->c2 = 0.0f; |
| 104 | + tf->gamma = 0.45f; |
| 105 | + tf->c3 = -(alpha - 1); |
| 106 | + // |
| 107 | + tf->c4 = 4.5f; |
| 108 | + tf->c5 = 0.0f; |
| 109 | + break; |
| 110 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB: // 13 |
| 111 | + tf->cutoff = beta; |
| 112 | + // |
| 113 | + tf->c0 = alpha; |
| 114 | + tf->c1 = 1.0f; |
| 115 | + tf->c2 = 0.0f; |
| 116 | + tf->gamma = 1.0f/2.4f; |
| 117 | + tf->c3 = -(alpha - 1); |
| 118 | + // |
| 119 | + tf->c4 = 12.92f; |
| 120 | + tf->c5 = 0.0f; |
| 121 | + break; |
| 122 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST428: // 17 |
| 123 | + tf->cutoff = -INFINITY; |
| 124 | + // |
| 125 | + tf->c0 = 1.0f; |
| 126 | + tf->c1 = 48.0f / 52.37f; |
| 127 | + tf->c2 = 0.0f; |
| 128 | + tf->gamma = 1.0f/2.6f; |
| 129 | + tf->c3 = 0.0f; |
| 130 | + // |
| 131 | + tf->c4 = 1.0f; |
| 132 | + tf->c5 = 0.0f; |
| 133 | + break; |
| 134 | + // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a default. |
| 135 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16 |
| 136 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18 |
| 137 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9 |
| 138 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_SQRT: // 10 |
| 139 | + // |
| 140 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN: // 0 |
| 141 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED: // 2 |
| 142 | + case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA22: // 4 |
| 143 | + default: |
| 144 | + tf->cutoff = -INFINITY; |
| 145 | + tf->c0 = 1.0f; |
| 146 | + tf->c1 = 1.0f; |
| 147 | + tf->c2 = 0.0f; |
| 148 | + tf->c3 = 0.0f; |
| 149 | + tf->c4 = 0.0f; |
| 150 | + tf->c5 = 0.0f; |
| 151 | + tf->gamma = 1.0f/2.2f; |
| 152 | + break; |
| 153 | + } |
| 154 | +} |
| 155 | +static CGColorSpaceRef CreateMonoColorSpace(uint16_t const colorPrimaries, uint16_t const transferCharacteristics) { |
| 156 | + if (@available(macOS 10.10, iOS 8.0, tvOS 8.0, *)) { |
| 157 | + vImage_Error err; |
| 158 | + vImageWhitePoint white; |
| 159 | + vImageTransferFunction transfer; |
| 160 | + CalcWhitePoint(colorPrimaries, &white); |
| 161 | + CalcTransferFunction(transferCharacteristics, &transfer); |
| 162 | + CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction(&white, &transfer, kCGRenderingIntentDefault, kvImagePrintDiagnosticsToConsole, &err); |
| 163 | + if(err != kvImageNoError) { |
| 164 | + NSLog(@"[BUG] Failed to create monochrome color space: %ld", err); |
| 165 | + return NULL; |
| 166 | + } |
| 167 | + return colorSpace; |
| 168 | + }else{ |
| 169 | + return NULL; |
| 170 | + } |
| 171 | +} |
| 172 | + |
19 | 173 | static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) {
|
20 |
| - [SDImageCoderHelper colorSpaceGetDeviceRGB]; |
21 | 174 | static CGColorSpaceRef defaultColorSpace;
|
22 | 175 | {
|
23 | 176 | static dispatch_once_t onceToken;
|
24 | 177 | dispatch_once(&onceToken, ^{
|
25 | 178 | defaultColorSpace = CGColorSpaceCreateDeviceGray();
|
26 | 179 | });
|
27 | 180 | }
|
28 |
| -default_color_space: |
29 |
| - *ref = defaultColorSpace; |
30 |
| - *shouldRelease = FALSE; |
| 181 | + if(avif->profileFormat == AVIF_PROFILE_FORMAT_NONE) { |
| 182 | + *ref = defaultColorSpace; |
| 183 | + *shouldRelease = FALSE; |
| 184 | + return; |
| 185 | + } |
| 186 | + if(avif->profileFormat == AVIF_PROFILE_FORMAT_ICC) { |
| 187 | + if(avif->icc.data && avif->icc.size) { |
| 188 | + if(@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) { |
| 189 | + *ref = CGColorSpaceCreateWithICCData(avif->icc.data); |
| 190 | + *shouldRelease = TRUE; |
| 191 | + return; |
| 192 | + } |
| 193 | + } |
| 194 | + *ref = defaultColorSpace; |
| 195 | + *shouldRelease = FALSE; |
| 196 | + return; |
| 197 | + } |
| 198 | + uint16_t const colorPrimaries = avif->nclx.colourPrimaries; |
| 199 | + uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; |
| 200 | + if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN || |
| 201 | + colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) && |
| 202 | + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN || |
| 203 | + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) { |
| 204 | + *ref = defaultColorSpace; |
| 205 | + *shouldRelease = FALSE; |
| 206 | + return; |
| 207 | + } |
| 208 | + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB && |
| 209 | + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { |
| 210 | + static CGColorSpaceRef sRGB = NULL; |
| 211 | + static dispatch_once_t onceToken; |
| 212 | + dispatch_once(&onceToken, ^{ |
| 213 | + sRGB = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); |
| 214 | + if(sRGB == NULL) { |
| 215 | + sRGB = defaultColorSpace; |
| 216 | + } |
| 217 | + }); |
| 218 | + *ref = sRGB; |
| 219 | + *shouldRelease = FALSE; |
| 220 | + return; |
| 221 | + } |
| 222 | + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 && |
| 223 | + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) { |
| 224 | + static CGColorSpaceRef bt709 = NULL; |
| 225 | + static dispatch_once_t onceToken; |
| 226 | + dispatch_once(&onceToken, ^{ |
| 227 | + bt709 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); |
| 228 | + if(bt709 == NULL) { |
| 229 | + bt709 = defaultColorSpace; |
| 230 | + } |
| 231 | + }); |
| 232 | + *ref = bt709; |
| 233 | + *shouldRelease = FALSE; |
| 234 | + return; |
| 235 | + } |
| 236 | + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 && |
| 237 | + (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT || |
| 238 | + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) { |
| 239 | + static CGColorSpaceRef bt2020 = NULL; |
| 240 | + static dispatch_once_t onceToken; |
| 241 | + dispatch_once(&onceToken, ^{ |
| 242 | + bt2020 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); |
| 243 | + if(bt2020 == NULL) { |
| 244 | + bt2020 = defaultColorSpace; |
| 245 | + } |
| 246 | + }); |
| 247 | + *ref = bt2020; |
| 248 | + *shouldRelease = FALSE; |
| 249 | + return; |
| 250 | + } |
| 251 | + if(colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 && |
| 252 | + transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) { |
| 253 | + static CGColorSpaceRef p3 = NULL; |
| 254 | + static dispatch_once_t onceToken; |
| 255 | + dispatch_once(&onceToken, ^{ |
| 256 | + p3 = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); |
| 257 | + if(p3 == NULL) { |
| 258 | + p3 = defaultColorSpace; |
| 259 | + } |
| 260 | + }); |
| 261 | + *ref = p3; |
| 262 | + *shouldRelease = FALSE; |
| 263 | + return; |
| 264 | + } |
| 265 | + |
| 266 | + *ref = CreateMonoColorSpace(colorPrimaries, transferCharacteristics); |
| 267 | + if(*ref != NULL) { |
| 268 | + *shouldRelease = TRUE; |
| 269 | + } else { |
| 270 | + *ref = defaultColorSpace; |
| 271 | + *shouldRelease = FALSE; |
| 272 | + } |
31 | 273 | }
|
32 | 274 |
|
33 | 275 | static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) {
|
@@ -55,8 +297,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou
|
55 | 297 | *shouldRelease = FALSE;
|
56 | 298 | return;
|
57 | 299 | }
|
58 |
| - uint16_t colorPrimaries = avif->nclx.colourPrimaries; |
59 |
| - uint16_t transferCharacteristics = avif->nclx.transferCharacteristics; |
| 300 | + uint16_t const colorPrimaries = avif->nclx.colourPrimaries; |
| 301 | + uint16_t const transferCharacteristics = avif->nclx.transferCharacteristics; |
60 | 302 | if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN ||
|
61 | 303 | colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) &&
|
62 | 304 | (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN ||
|
@@ -232,6 +474,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou
|
232 | 474 | *shouldRelease = FALSE;
|
233 | 475 | return;
|
234 | 476 | }
|
| 477 | + // TODO(ledyba-z): We can support more color spaces |
| 478 | + // using vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction |
235 | 479 |
|
236 | 480 | *ref = defaultColorSpace;
|
237 | 481 | *shouldRelease = FALSE;
|
|
0 commit comments