Skip to content

Commit 18f0e4e

Browse files
committed
Crete monochrome color spaces
1 parent b27dda6 commit 18f0e4e

File tree

1 file changed

+250
-6
lines changed

1 file changed

+250
-6
lines changed

SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m

Lines changed: 250 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,260 @@
1616
static void FreeImageData(void *info, const void *data, size_t size) {
1717
free((void *)data);
1818
}
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+
19173
static void CalcColorSpaceMono(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) {
20-
[SDImageCoderHelper colorSpaceGetDeviceRGB];
21174
static CGColorSpaceRef defaultColorSpace;
22175
{
23176
static dispatch_once_t onceToken;
24177
dispatch_once(&onceToken, ^{
25178
defaultColorSpace = CGColorSpaceCreateDeviceGray();
26179
});
27180
}
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+
}
31273
}
32274

33275
static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shouldRelease) {
@@ -55,8 +297,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou
55297
*shouldRelease = FALSE;
56298
return;
57299
}
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;
60302
if((colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNKNOWN ||
61303
colorPrimaries == AVIF_NCLX_COLOUR_PRIMARIES_UNSPECIFIED) &&
62304
(transferCharacteristics == AVIF_NCLX_TRANSFER_CHARACTERISTICS_UNKNOWN ||
@@ -232,6 +474,8 @@ static void CalcColorSpaceRGB(avifImage * avif, CGColorSpaceRef* ref, BOOL* shou
232474
*shouldRelease = FALSE;
233475
return;
234476
}
477+
// TODO(ledyba-z): We can support more color spaces
478+
// using vImageCreateRGBColorSpaceWithPrimariesAndTransferFunction
235479

236480
*ref = defaultColorSpace;
237481
*shouldRelease = FALSE;

0 commit comments

Comments
 (0)