@@ -17,6 +17,196 @@ static void FreeImageData(void *info, const void *data, size_t size) {
1717 free ((void *)data);
1818}
1919
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 CalcRGBPrimaries (uint16_t const colorPrimaries, vImageRGBPrimaries* const prim) {
28+ float primaries[8 ];
29+ avifNclxColourPrimariesGetValues (colorPrimaries, primaries);
30+ prim->red_x = primaries[0 ];
31+ prim->red_y = primaries[1 ];
32+ prim->green_x = primaries[2 ];
33+ prim->green_y = primaries[3 ];
34+ prim->blue_x = primaries[4 ];
35+ prim->blue_y = primaries[5 ];
36+ prim->white_x = primaries[6 ];
37+ prim->white_y = primaries[7 ];
38+ }
39+
40+ static void CalcTransferFunction (uint16_t const transferCharacteristics, vImageTransferFunction* const tf) {
41+ // See: https://www.itu.int/rec/T-REC-H.273/en
42+ static const float alpha = 1 .099296826809442f ;
43+ static const float beta = 0 .018053968510807f ;
44+ /*
45+ // R' = c0 * pow( c1 * R + c2, gamma ) + c3, (R >= cutoff)
46+ // R' = c4 * R + c5 (R < cutoff)
47+ */
48+
49+ switch (transferCharacteristics) {
50+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA28: // 5
51+ tf->cutoff = -INFINITY;
52+ tf->c0 = 1 .0f ;
53+ tf->c1 = 1 .0f ;
54+ tf->c2 = 0 .0f ;
55+ tf->c3 = 0 .0f ;
56+ tf->c4 = 0 .0f ;
57+ tf->c5 = 0 .0f ;
58+ tf->gamma = 1 .0f /2 .8f ;
59+ break ;
60+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709: // 1
61+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT601: // 6
62+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT: // 14
63+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT: // 15
64+ tf->cutoff = beta;
65+ //
66+ tf->c0 = alpha;
67+ tf->c1 = 1 .0f ;
68+ tf->c2 = 0 .0f ;
69+ tf->gamma = 0 .45f ;
70+ tf->c3 = -(alpha - 1 );
71+ //
72+ tf->c4 = 4 .5f ;
73+ tf->c5 = 0 .0f ;
74+ break ;
75+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST240: // 7
76+ tf->cutoff = beta;
77+ //
78+ tf->c0 = alpha;
79+ tf->c1 = 1 .0f ;
80+ tf->c2 = 0 .0f ;
81+ tf->gamma = 0 .45f ;
82+ tf->c3 = -(alpha - 1 );
83+ //
84+ tf->c4 = 4 .0f ;
85+ tf->c5 = 0 .0f ;
86+ break ;
87+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LINEAR: // 8
88+ tf->cutoff = INFINITY;
89+ //
90+ tf->c0 = 1 .0f ;
91+ tf->c1 = 1 .0f ;
92+ tf->c2 = 0 .0f ;
93+ tf->gamma = 1 .0f ;
94+ tf->c3 = 0 .0f ;
95+ //
96+ tf->c4 = 4 .0f ;
97+ tf->c5 = 0 .0f ;
98+ break ;
99+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_IEC61966: // 11
100+ tf->cutoff = beta;
101+ //
102+ tf->c0 = alpha;
103+ tf->c1 = 1 .0f ;
104+ tf->c2 = 0 .0f ;
105+ tf->gamma = 0 .45f ;
106+ tf->c3 = -(alpha - 1 );
107+ //
108+ tf->c4 = 4 .5f ;
109+ tf->c5 = 0 .0f ;
110+ break ;
111+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT1361_EXTENDED: // 12
112+ tf->cutoff = beta;
113+ //
114+ tf->c0 = alpha;
115+ tf->c1 = 1 .0f ;
116+ tf->c2 = 0 .0f ;
117+ tf->gamma = 0 .45f ;
118+ tf->c3 = -(alpha - 1 );
119+ //
120+ tf->c4 = 4 .5f ;
121+ tf->c5 = 0 .0f ;
122+ break ;
123+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB: // 13
124+ tf->cutoff = beta;
125+ //
126+ tf->c0 = alpha;
127+ tf->c1 = 1 .0f ;
128+ tf->c2 = 0 .0f ;
129+ tf->gamma = 1 .0f /2 .4f ;
130+ tf->c3 = -(alpha - 1 );
131+ //
132+ tf->c4 = 12 .92f ;
133+ tf->c5 = 0 .0f ;
134+ break ;
135+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST428: // 17
136+ tf->cutoff = -INFINITY;
137+ //
138+ tf->c0 = 1 .0f ;
139+ tf->c1 = 48 .0f / 52 .37f ;
140+ tf->c2 = 0 .0f ;
141+ tf->gamma = 1 .0f /2 .6f ;
142+ tf->c3 = 0 .0f ;
143+ //
144+ tf->c4 = 1 .0f ;
145+ tf->c5 = 0 .0f ;
146+ break ;
147+ // Can't be represented by vImageTransferFunction. Use gamma 2.2 as a fallback.
148+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_ST2084: // 16
149+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2100_HLG: // 18
150+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_1: // 9
151+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_LOG_100_SQRT: // 10
152+ //
153+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN: // 0
154+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED: // 2
155+ case AVIF_NCLX_TRANSFER_CHARACTERISTICS_GAMMA22: // 4
156+ default :
157+ tf->cutoff = -INFINITY;
158+ tf->c0 = 1 .0f ;
159+ tf->c1 = 1 .0f ;
160+ tf->c2 = 0 .0f ;
161+ tf->c3 = 0 .0f ;
162+ tf->c4 = 0 .0f ;
163+ tf->c5 = 0 .0f ;
164+ tf->gamma = 1 .0f /2 .2f ;
165+ break ;
166+ }
167+ }
168+ static CGColorSpaceRef CreateColorSpaceMono (uint16_t const colorPrimaries, uint16_t const transferCharacteristics) {
169+ if (@available (macOS 10.10 , iOS 8.0 , tvOS 8.0 , *)) {
170+ vImage_Error err;
171+ vImageWhitePoint white;
172+ vImageTransferFunction transfer;
173+ CalcWhitePoint (colorPrimaries, &white);
174+ CalcTransferFunction (transferCharacteristics, &transfer);
175+ CGColorSpaceRef colorSpace = vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction (&white, &transfer, kCGRenderingIntentDefault , kvImagePrintDiagnosticsToConsole, &err);
176+ if (err != kvImageNoError) {
177+ NSLog (@" [BUG] Failed to create monochrome color space: %ld " , err);
178+ if (colorSpace != NULL ) {
179+ CGColorSpaceRelease (colorSpace);
180+ }
181+ return NULL ;
182+ }
183+ return colorSpace;
184+ }else {
185+ return NULL ;
186+ }
187+ }
188+
189+ static CGColorSpaceRef CreateColorSpaceRGB (uint16_t const colorPrimaries, uint16_t const transferCharacteristics) {
190+ if (@available (macOS 10.10 , iOS 8.0 , tvOS 8.0 , *)) {
191+ vImage_Error err;
192+ vImageRGBPrimaries primaries;
193+ vImageTransferFunction transfer;
194+ CalcRGBPrimaries (colorPrimaries, &primaries);
195+ CalcTransferFunction (transferCharacteristics, &transfer);
196+ CGColorSpaceRef colorSpace = vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction (&primaries, &transfer, kCGRenderingIntentDefault , kvImagePrintDiagnosticsToConsole, &err);
197+ if (err != kvImageNoError) {
198+ NSLog (@" [BUG] Failed to create monochrome color space: %ld " , err);
199+ if (colorSpace != NULL ) {
200+ CGColorSpaceRelease (colorSpace);
201+ }
202+ return NULL ;
203+ }
204+ return colorSpace;
205+ }else {
206+ return NULL ;
207+ }
208+ }
209+
20210static void CalcColorSpaceMono (avifImage * avif, CGColorSpaceRef* ref, BOOL * shouldRelease) {
21211 static CGColorSpaceRef defaultColorSpace;
22212 {
@@ -46,12 +236,81 @@ static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* sho
46236 *shouldRelease = FALSE ;
47237 return ;
48238 }
49- // TODO: (ledyba-z):
50- // We can support other color spaces using
51- // vImageCreateMonochromeColorSpaceWithWhitePointAndTransferFunction
52- // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces
53- *ref = defaultColorSpace;
54- *shouldRelease = FALSE ;
239+ uint16_t const colorPrimaries = avif->nclx .colourPrimaries ;
240+ uint16_t const transferCharacteristics = avif->nclx .transferCharacteristics ;
241+ if ((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN ||
242+ colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) &&
243+ (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN ||
244+ transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNSPECIFIED)) {
245+ *ref = defaultColorSpace;
246+ *shouldRelease = FALSE ;
247+ return ;
248+ }
249+ if (colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_SRGB &&
250+ transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) {
251+ static CGColorSpaceRef sRGB = NULL ;
252+ static dispatch_once_t onceToken;
253+ dispatch_once (&onceToken, ^{
254+ sRGB = CreateColorSpaceMono (colorPrimaries, transferCharacteristics);
255+ if (sRGB == NULL ) {
256+ sRGB = defaultColorSpace;
257+ }
258+ });
259+ *ref = sRGB ;
260+ *shouldRelease = FALSE ;
261+ return ;
262+ }
263+ if (colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT709 &&
264+ transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT709) {
265+ static CGColorSpaceRef bt709 = NULL ;
266+ static dispatch_once_t onceToken;
267+ dispatch_once (&onceToken, ^{
268+ bt709 = CreateColorSpaceMono (colorPrimaries, transferCharacteristics);
269+ if (bt709 == NULL ) {
270+ bt709 = defaultColorSpace;
271+ }
272+ });
273+ *ref = bt709;
274+ *shouldRelease = FALSE ;
275+ return ;
276+ }
277+ if (colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_BT2020 &&
278+ (transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_10BIT ||
279+ transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_BT2020_12BIT)) {
280+ static CGColorSpaceRef bt2020 = NULL ;
281+ static dispatch_once_t onceToken;
282+ dispatch_once (&onceToken, ^{
283+ bt2020 = CreateColorSpaceMono (colorPrimaries, transferCharacteristics);
284+ if (bt2020 == NULL ) {
285+ bt2020 = defaultColorSpace;
286+ }
287+ });
288+ *ref = bt2020;
289+ *shouldRelease = FALSE ;
290+ return ;
291+ }
292+ if (colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_P3 &&
293+ transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_SRGB) {
294+ static CGColorSpaceRef p3 = NULL ;
295+ static dispatch_once_t onceToken;
296+ dispatch_once (&onceToken, ^{
297+ p3 = CreateColorSpaceMono (colorPrimaries, transferCharacteristics);
298+ if (p3 == NULL ) {
299+ p3 = defaultColorSpace;
300+ }
301+ });
302+ *ref = p3;
303+ *shouldRelease = FALSE ;
304+ return ;
305+ }
306+
307+ *ref = CreateColorSpaceMono (colorPrimaries, transferCharacteristics);
308+ if (*ref != NULL ) {
309+ *shouldRelease = TRUE ;
310+ } else {
311+ *ref = defaultColorSpace;
312+ *shouldRelease = FALSE ;
313+ }
55314}
56315
57316static void CalcColorSpaceRGB (avifImage * avif, CGColorSpaceRef* ref, BOOL * shouldRelease) {
@@ -261,12 +520,13 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou
261520 return ;
262521 }
263522
264- // TODO: (ledyba-z):
265- // We can support other color spaces using
266- // vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction
267- // https://github.com/link-u/SDWebImageAVIFCoder/tree/feature/create-custom-colorspaces
268- *ref = defaultColorSpace;
269- *shouldRelease = FALSE ;
523+ *ref = CreateColorSpaceRGB (colorPrimaries, transferCharacteristics);
524+ if (*ref != NULL ) {
525+ *shouldRelease = TRUE ;
526+ } else {
527+ *ref = defaultColorSpace;
528+ *shouldRelease = FALSE ;
529+ }
270530}
271531
272532static CGImageRef CreateImageFromBuffer (avifImage * avif, vImage_Buffer* result) {
0 commit comments