Skip to content

Commit 9da58e8

Browse files
committed
Revert "We won't create CGColorSpaceRefs ourselves today."
This reverts commit 71ecfa1.
1 parent 08d5172 commit 9da58e8

File tree

1 file changed

+272
-12
lines changed

1 file changed

+272
-12
lines changed

SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m

Lines changed: 272 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
20210
static 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

57316
static 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

272532
static CGImageRef CreateImageFromBuffer(avifImage * avif, vImage_Buffer* result) {

0 commit comments

Comments
 (0)