Branch data Line data Source code
1 : : #include "utils.h"
2 : :
3 : 1 : int main()
4 : : {
5 [ + + ]: 13 : for (enum pl_color_system sys = 0; sys < PL_COLOR_SYSTEM_COUNT; sys++) {
6 : 12 : bool ycbcr = sys >= PL_COLOR_SYSTEM_BT_601 && sys <= PL_COLOR_SYSTEM_YCGCO;
7 [ - + ]: 12 : REQUIRE_CMP(ycbcr, ==, pl_color_system_is_ycbcr_like(sys), "d");
8 : : }
9 : :
10 [ + + ]: 18 : for (enum pl_color_transfer trc = 0; trc < PL_COLOR_TRC_COUNT; trc++) {
11 : 17 : printf("Testing color transfer: %s\n", pl_color_transfer_name(trc));
12 : 17 : bool hdr = trc >= PL_COLOR_TRC_PQ && trc <= PL_COLOR_TRC_S_LOG2;
13 [ - + ]: 17 : REQUIRE_CMP(hdr, ==, pl_color_transfer_is_hdr(trc), "d");
14 [ - + ]: 17 : REQUIRE_CMP(pl_color_transfer_nominal_peak(trc), >=, 1.0, "f");
15 : :
16 [ + + ]: 17 : if (trc == PL_COLOR_TRC_LINEAR)
17 : 1 : continue;
18 : :
19 : : // Test round trip
20 : : const float peak = 1.0f, contrast = 1000;
21 : 16 : const struct pl_color_space csp = {
22 : : .transfer = trc,
23 : : .hdr.max_luma = PL_COLOR_SDR_WHITE * peak,
24 : : .hdr.min_luma = PL_COLOR_SDR_WHITE * peak / contrast,
25 : : };
26 [ + + ]: 1632 : for (float x = 0.0f; x <= 1.0f; x += 0.01f) {
27 : 1616 : float color[3] = { x, x, x };
28 : 1616 : pl_color_linearize(&csp, color);
29 [ + + ]: 1616 : if (trc == PL_COLOR_TRC_PQ)
30 [ - + ]: 101 : REQUIRE_FEQ(color[0], pl_hdr_rescale(PL_HDR_PQ, PL_HDR_NORM, x), 1e-5f);
31 [ + + + + ]: 1616 : if (pl_color_space_is_black_scaled(&csp) || trc == PL_COLOR_TRC_BT_1886)
32 [ - + ]: 1212 : REQUIRE_CMP(color[0] + 1e-6f, >=, peak / contrast, "f");
33 [ + + + + ]: 1616 : if (!pl_color_space_is_hdr(&csp) && trc != PL_COLOR_TRC_ST428)
34 [ - + ]: 1010 : REQUIRE_CMP(color[0] - 1e-6f, <=, peak, "f");
35 : :
36 [ + + ]: 1515 : switch (trc) {
37 : : case PL_COLOR_TRC_V_LOG:
38 : : case PL_COLOR_TRC_S_LOG1:
39 : : case PL_COLOR_TRC_S_LOG2:
40 : : // FIXME: these don't currently round-trip on subzero values
41 : : break;
42 : 1313 : default:
43 : 1313 : pl_color_delinearize(&csp, color);
44 [ - + ]: 1313 : REQUIRE_FEQ(color[0], x, 1e-5f);
45 : : break;
46 : : }
47 : : }
48 : : }
49 : :
50 : 1 : float pq_peak = pl_color_transfer_nominal_peak(PL_COLOR_TRC_PQ);
51 [ - + ]: 1 : REQUIRE_FEQ(PL_COLOR_SDR_WHITE * pq_peak, 10000, 1e-7);
52 : :
53 : 1 : struct pl_color_repr tv_repr = {
54 : : .sys = PL_COLOR_SYSTEM_BT_709,
55 : : .levels = PL_COLOR_LEVELS_LIMITED,
56 : : };
57 : :
58 : 1 : struct pl_color_repr pc_repr = {
59 : : .sys = PL_COLOR_SYSTEM_RGB,
60 : : .levels = PL_COLOR_LEVELS_FULL,
61 : : };
62 : :
63 : : // Ensure this is a no-op for bits == bits
64 [ + + ]: 17 : for (int bits = 1; bits <= 16; bits++) {
65 : 16 : tv_repr.bits.color_depth = tv_repr.bits.sample_depth = bits;
66 : 16 : pc_repr.bits.color_depth = pc_repr.bits.sample_depth = bits;
67 [ - + ]: 16 : REQUIRE_FEQ(pl_color_repr_normalize(&tv_repr), 1.0, 1e-7);
68 [ - + ]: 16 : REQUIRE_FEQ(pl_color_repr_normalize(&pc_repr), 1.0, 1e-7);
69 : : }
70 : :
71 : 1 : tv_repr.bits.color_depth = 8;
72 : 1 : tv_repr.bits.sample_depth = 10;
73 : 1 : float tv8to10 = pl_color_repr_normalize(&tv_repr);
74 : :
75 : 1 : tv_repr.bits.color_depth = 8;
76 : 1 : tv_repr.bits.sample_depth = 12;
77 : 1 : float tv8to12 = pl_color_repr_normalize(&tv_repr);
78 : :
79 : : // Simulate the effect of GPU texture sampling on UNORM texture
80 [ - + ]: 1 : REQUIRE_FEQ(tv8to10 * 16 /1023., 64/1023., 1e-7); // black
81 [ - + ]: 1 : REQUIRE_FEQ(tv8to10 * 235/1023., 940/1023., 1e-7); // nominal white
82 [ - + ]: 1 : REQUIRE_FEQ(tv8to10 * 128/1023., 512/1023., 1e-7); // achromatic
83 [ - + ]: 1 : REQUIRE_FEQ(tv8to10 * 240/1023., 960/1023., 1e-7); // nominal chroma peak
84 : :
85 [ - + ]: 1 : REQUIRE_FEQ(tv8to12 * 16 /4095., 256 /4095., 1e-7); // black
86 [ - + ]: 1 : REQUIRE_FEQ(tv8to12 * 235/4095., 3760/4095., 1e-7); // nominal white
87 [ - + ]: 1 : REQUIRE_FEQ(tv8to12 * 128/4095., 2048/4095., 1e-7); // achromatic
88 [ - + ]: 1 : REQUIRE_FEQ(tv8to12 * 240/4095., 3840/4095., 1e-7); // nominal chroma peak
89 : :
90 : : // Ensure lavc's xyz12 is handled correctly
91 : 1 : struct pl_color_repr xyz12 = {
92 : : .sys = PL_COLOR_SYSTEM_XYZ,
93 : : .levels = PL_COLOR_LEVELS_UNKNOWN,
94 : : .bits = {
95 : : .sample_depth = 16,
96 : : .color_depth = 12,
97 : : .bit_shift = 4,
98 : : },
99 : : };
100 : :
101 : 1 : float xyz = pl_color_repr_normalize(&xyz12);
102 [ - + ]: 1 : REQUIRE_FEQ(xyz * (4095 << 4), 65535, 1e-7);
103 : :
104 : : // Assume we uploaded a 10-bit source directly (unshifted) as a 16-bit
105 : : // texture. This texture multiplication factor should make it behave as if
106 : : // it was uploaded as a 10-bit texture instead.
107 : 1 : pc_repr.bits.color_depth = 10;
108 : 1 : pc_repr.bits.sample_depth = 16;
109 : 1 : float pc10to16 = pl_color_repr_normalize(&pc_repr);
110 [ - + ]: 1 : REQUIRE_FEQ(pc10to16 * 1000/65535., 1000/1023., 1e-7);
111 : :
112 : : const struct pl_raw_primaries *bt709, *bt2020, *dcip3;
113 : 1 : bt709 = pl_raw_primaries_get(PL_COLOR_PRIM_BT_709);
114 : 1 : bt2020 = pl_raw_primaries_get(PL_COLOR_PRIM_BT_2020);
115 : 1 : dcip3 = pl_raw_primaries_get(PL_COLOR_PRIM_DCI_P3);
116 [ - + ]: 1 : REQUIRE(pl_primaries_superset(bt2020, bt709));
117 [ - + ]: 1 : REQUIRE(!pl_primaries_superset(bt2020, dcip3)); // small region doesn't overlap
118 [ - + ]: 1 : REQUIRE(pl_primaries_superset(dcip3, bt709));
119 [ - + ]: 1 : REQUIRE(!pl_primaries_superset(bt709, bt2020));
120 [ - + ]: 1 : REQUIRE(pl_primaries_compatible(bt2020, bt2020));
121 [ - + ]: 1 : REQUIRE(pl_primaries_compatible(bt2020, bt709));
122 [ - + ]: 1 : REQUIRE(pl_primaries_compatible(bt709, bt2020));
123 [ - + ]: 1 : REQUIRE(pl_primaries_compatible(bt2020, dcip3));
124 [ - + ]: 1 : REQUIRE(pl_primaries_compatible(bt709, dcip3));
125 : :
126 : 1 : struct pl_raw_primaries bt709_2020 = pl_primaries_clip(bt709, bt2020);
127 : 1 : struct pl_raw_primaries bt2020_709 = pl_primaries_clip(bt2020, bt709);
128 [ - + ]: 1 : REQUIRE(pl_raw_primaries_similar(&bt709_2020, bt709));
129 [ - + ]: 1 : REQUIRE(pl_raw_primaries_similar(&bt2020_709, bt709));
130 : :
131 : 1 : struct pl_raw_primaries dcip3_bt2020 = pl_primaries_clip(dcip3, bt2020);
132 : 1 : struct pl_raw_primaries dcip3_bt709 = pl_primaries_clip(dcip3, bt709);
133 [ - + ]: 1 : REQUIRE(pl_primaries_superset(dcip3, &dcip3_bt2020));
134 [ - + ]: 1 : REQUIRE(pl_primaries_superset(dcip3, &dcip3_bt709));
135 [ - + ]: 1 : REQUIRE(pl_primaries_superset(bt2020, &dcip3_bt2020));
136 [ - + ]: 1 : REQUIRE(pl_primaries_superset(bt709, &dcip3_bt709));
137 : :
138 : : pl_matrix3x3 rgb2xyz, rgb2xyz_;
139 : 1 : rgb2xyz = rgb2xyz_ = pl_get_rgb2xyz_matrix(bt709);
140 : 1 : pl_matrix3x3_invert(&rgb2xyz_);
141 : 1 : pl_matrix3x3_invert(&rgb2xyz_);
142 : :
143 : : // Make sure the double-inversion round trips
144 [ + + ]: 4 : for (int y = 0; y < 3; y++) {
145 [ + + ]: 12 : for (int x = 0; x < 3; x++)
146 [ - + ]: 9 : REQUIRE_FEQ(rgb2xyz.m[y][x], rgb2xyz_.m[y][x], 1e-6);
147 : : }
148 : :
149 : : // Make sure mapping the spectral RGB colors (i.e. the matrix rows) matches
150 : : // our original primaries
151 : 1 : float Y = rgb2xyz.m[1][0];
152 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[0][0], pl_cie_X(bt709->red) * Y, 1e-7);
153 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[2][0], pl_cie_Z(bt709->red) * Y, 1e-7);
154 : 1 : Y = rgb2xyz.m[1][1];
155 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[0][1], pl_cie_X(bt709->green) * Y, 1e-7);
156 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[2][1], pl_cie_Z(bt709->green) * Y, 1e-7);
157 : 1 : Y = rgb2xyz.m[1][2];
158 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[0][2], pl_cie_X(bt709->blue) * Y, 1e-7);
159 [ - + ]: 1 : REQUIRE_FEQ(rgb2xyz.m[2][2], pl_cie_Z(bt709->blue) * Y, 1e-7);
160 : :
161 : : // Make sure the gamut mapping round-trips
162 : : pl_matrix3x3 bt709_bt2020, bt2020_bt709;
163 : 1 : bt709_bt2020 = pl_get_color_mapping_matrix(bt709, bt2020, PL_INTENT_RELATIVE_COLORIMETRIC);
164 : 1 : bt2020_bt709 = pl_get_color_mapping_matrix(bt2020, bt709, PL_INTENT_RELATIVE_COLORIMETRIC);
165 [ + + ]: 11 : for (int n = 0; n < 10; n++) {
166 : 10 : float vec[3] = { RANDOM, RANDOM, RANDOM };
167 : 10 : float dst[3] = { vec[0], vec[1], vec[2] };
168 : 10 : pl_matrix3x3_apply(&bt709_bt2020, dst);
169 : 10 : pl_matrix3x3_apply(&bt2020_bt709, dst);
170 [ + + ]: 40 : for (int i = 0; i < 3; i++)
171 [ - + ]: 30 : REQUIRE_FEQ(dst[i], vec[i], 1e-6);
172 : : }
173 : :
174 : : // Ensure the decoding matrix round-trips to white/black
175 [ + + ]: 13 : for (enum pl_color_system sys = 0; sys < PL_COLOR_SYSTEM_COUNT; sys++) {
176 [ + + ]: 12 : if (!pl_color_system_is_linear(sys))
177 : 5 : continue;
178 : :
179 : 7 : printf("Testing color system: %s\n", pl_color_system_name(sys));
180 : 7 : struct pl_color_repr repr = {
181 : : .levels = PL_COLOR_LEVELS_LIMITED,
182 : : .sys = sys,
183 : : .bits = {
184 : : // synthetic test
185 : : .color_depth = 8,
186 : : .sample_depth = 10,
187 : : },
188 : : };
189 : :
190 : 7 : float scale = pl_color_repr_normalize(&repr);
191 : 7 : pl_transform3x3 yuv2rgb = pl_color_repr_decode(&repr, NULL);
192 : 7 : pl_matrix3x3_scale(&yuv2rgb.mat, scale);
193 : :
194 : : static const float white_ycbcr[3] = { 235/1023., 128/1023., 128/1023. };
195 : : static const float black_ycbcr[3] = { 16/1023., 128/1023., 128/1023. };
196 : : static const float white_other[3] = { 235/1023., 235/1023., 235/1023. };
197 : : static const float black_other[3] = { 16/1023., 16/1023., 16/1023. };
198 : :
199 : : float white[3], black[3];
200 [ + + ]: 28 : for (int i = 0; i < 3; i++) {
201 [ + + ]: 21 : if (pl_color_system_is_ycbcr_like(sys)) {
202 : 15 : white[i] = white_ycbcr[i];
203 : 15 : black[i] = black_ycbcr[i];
204 : : } else {
205 : 6 : white[i] = white_other[i];
206 : 6 : black[i] = black_other[i];
207 : : }
208 : : }
209 : :
210 : 7 : pl_transform3x3_apply(&yuv2rgb, white);
211 [ - + ]: 7 : REQUIRE_FEQ(white[0], 1.0, 1e-6);
212 [ - + ]: 7 : REQUIRE_FEQ(white[1], 1.0, 1e-6);
213 [ - + ]: 7 : REQUIRE_FEQ(white[2], 1.0, 1e-6);
214 : :
215 : 7 : pl_transform3x3_apply(&yuv2rgb, black);
216 [ - + ]: 7 : REQUIRE_FEQ(black[0], 0.0, 1e-6);
217 [ - + ]: 7 : REQUIRE_FEQ(black[1], 0.0, 1e-6);
218 [ - + ]: 7 : REQUIRE_FEQ(black[2], 0.0, 1e-6);
219 : : }
220 : :
221 : : // Make sure chromatic adaptation works
222 : : struct pl_raw_primaries bt709_d50;
223 : 1 : bt709_d50 = *pl_raw_primaries_get(PL_COLOR_PRIM_BT_709);
224 : 1 : bt709_d50.white = (struct pl_cie_xy) { 0.34567, 0.35850 };
225 : :
226 : : pl_matrix3x3 d50_d65;
227 : 1 : d50_d65 = pl_get_color_mapping_matrix(&bt709_d50, bt709, PL_INTENT_RELATIVE_COLORIMETRIC);
228 : :
229 : 1 : float white[3] = { 1.0, 1.0, 1.0 };
230 : 1 : pl_matrix3x3_apply(&d50_d65, white);
231 [ - + ]: 1 : REQUIRE_FEQ(white[0], 1.0, 1e-6);
232 [ - + ]: 1 : REQUIRE_FEQ(white[1], 1.0, 1e-6);
233 [ - + ]: 1 : REQUIRE_FEQ(white[2], 1.0, 1e-6);
234 : :
235 : : // Simulate a typical 10-bit YCbCr -> 16 bit texture conversion
236 : 1 : tv_repr.bits.color_depth = 10;
237 : 1 : tv_repr.bits.sample_depth = 16;
238 : : pl_transform3x3 yuv2rgb;
239 : 1 : yuv2rgb = pl_color_repr_decode(&tv_repr, NULL);
240 : 1 : float test[3] = { 575/65535., 336/65535., 640/65535. };
241 : 1 : pl_transform3x3_apply(&yuv2rgb, test);
242 [ - + ]: 1 : REQUIRE_FEQ(test[0], 0.808305, 1e-6);
243 [ - + ]: 1 : REQUIRE_FEQ(test[1], 0.553254, 1e-6);
244 [ - + ]: 1 : REQUIRE_FEQ(test[2], 0.218841, 1e-6);
245 : :
246 : : // DVD
247 [ - + ]: 1 : REQUIRE_CMP(pl_color_system_guess_ycbcr(720, 480), ==, PL_COLOR_SYSTEM_BT_601, "u");
248 [ - + ]: 1 : REQUIRE_CMP(pl_color_system_guess_ycbcr(720, 576), ==, PL_COLOR_SYSTEM_BT_601, "u");
249 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(720, 576), ==, PL_COLOR_PRIM_BT_601_625, "u");
250 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(720, 480), ==, PL_COLOR_PRIM_BT_601_525, "u");
251 : : // PAL 16:9
252 [ - + ]: 1 : REQUIRE_CMP(pl_color_system_guess_ycbcr(1024, 576), ==, PL_COLOR_SYSTEM_BT_601, "u");
253 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(1024, 576), ==, PL_COLOR_PRIM_BT_601_625, "u");
254 : : // HD
255 [ - + ]: 1 : REQUIRE_CMP(pl_color_system_guess_ycbcr(1280, 720), ==, PL_COLOR_SYSTEM_BT_709, "u");
256 [ - + ]: 1 : REQUIRE_CMP(pl_color_system_guess_ycbcr(1920, 1080), ==, PL_COLOR_SYSTEM_BT_709, "u");
257 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(1280, 720), ==, PL_COLOR_PRIM_BT_709, "u");
258 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(1920, 1080), ==, PL_COLOR_PRIM_BT_709, "u");
259 : :
260 : : // Odd/weird videos
261 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(2000, 576), ==, PL_COLOR_PRIM_BT_709, "u");
262 [ - + ]: 1 : REQUIRE_CMP(pl_color_primaries_guess(200, 200), ==, PL_COLOR_PRIM_BT_709, "u");
263 : :
264 [ - + ]: 1 : REQUIRE(pl_color_repr_equal(&pl_color_repr_sdtv, &pl_color_repr_sdtv));
265 [ - + ]: 1 : REQUIRE(!pl_color_repr_equal(&pl_color_repr_sdtv, &pl_color_repr_hdtv));
266 : :
267 : 1 : struct pl_color_repr repr = pl_color_repr_unknown;
268 : 1 : pl_color_repr_merge(&repr, &pl_color_repr_uhdtv);
269 [ - + ]: 1 : REQUIRE(pl_color_repr_equal(&repr, &pl_color_repr_uhdtv));
270 : :
271 [ - + ]: 1 : REQUIRE(!pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_UNKNOWN));
272 [ - + ]: 1 : REQUIRE(!pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_BT_601_525));
273 [ - + ]: 1 : REQUIRE(!pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_BT_601_625));
274 [ - + ]: 1 : REQUIRE(!pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_BT_709));
275 [ - + ]: 1 : REQUIRE(!pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_BT_470M));
276 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_BT_2020));
277 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_APPLE));
278 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_ADOBE));
279 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_PRO_PHOTO));
280 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_CIE_1931));
281 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_DCI_P3));
282 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_DISPLAY_P3));
283 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_V_GAMUT));
284 [ - + ]: 1 : REQUIRE(pl_color_primaries_is_wide_gamut(PL_COLOR_PRIM_S_GAMUT));
285 : :
286 : 1 : struct pl_color_space space = pl_color_space_unknown;
287 : 1 : pl_color_space_merge(&space, &pl_color_space_bt709);
288 [ - + ]: 1 : REQUIRE(pl_color_space_equal(&space, &pl_color_space_bt709));
289 : :
290 : : // Infer some color spaces
291 : 1 : struct pl_color_space hlg = {
292 : : .primaries = PL_COLOR_PRIM_BT_2020,
293 : : .transfer = PL_COLOR_TRC_HLG,
294 : : };
295 : :
296 : 1 : pl_color_space_infer(&hlg);
297 [ - + ]: 1 : REQUIRE_CMP(hlg.hdr.max_luma, ==, PL_COLOR_HLG_PEAK, "f");
298 : :
299 : 1 : struct pl_color_space unknown = {0};
300 : 1 : struct pl_color_space display = {
301 : : .primaries = PL_COLOR_PRIM_BT_709,
302 : : .transfer = PL_COLOR_TRC_BT_1886,
303 : : };
304 : :
305 : 1 : pl_color_space_infer(&unknown);
306 : 1 : pl_color_space_infer(&display);
307 [ - + ]: 1 : REQUIRE(pl_color_space_equal(&unknown, &display));
308 : :
309 : : float x, y;
310 : 1 : pl_chroma_location_offset(PL_CHROMA_LEFT, &x, &y);
311 [ - + ]: 1 : REQUIRE_CMP(x, ==, -0.5f, "f");
312 [ - + ]: 1 : REQUIRE_CMP(y, ==, 0.0f, "f");
313 : 1 : pl_chroma_location_offset(PL_CHROMA_TOP_LEFT, &x, &y);
314 [ - + ]: 1 : REQUIRE_CMP(x, ==, -0.5f, "f");
315 [ - + ]: 1 : REQUIRE_CMP(y, ==, -0.5f, "f");
316 : 1 : pl_chroma_location_offset(PL_CHROMA_CENTER, &x, &y);
317 [ - + ]: 1 : REQUIRE_CMP(x, ==, 0.0f, "f");
318 [ - + ]: 1 : REQUIRE_CMP(y, ==, 0.0f, "f");
319 : 1 : pl_chroma_location_offset(PL_CHROMA_BOTTOM_CENTER, &x, &y);
320 [ - + ]: 1 : REQUIRE_CMP(x, ==, 0.0f, "f");
321 [ - + ]: 1 : REQUIRE_CMP(y, ==, 0.5f, "f");
322 : :
323 [ - + ]: 1 : REQUIRE_CMP(pl_raw_primaries_get(PL_COLOR_PRIM_UNKNOWN), ==,
324 : : pl_raw_primaries_get(PL_COLOR_PRIM_BT_709), "p");
325 : :
326 : : // Color blindness tests
327 : 1 : float red[3] = { 1.0, 0.0, 0.0 };
328 : 1 : float green[3] = { 0.0, 1.0, 0.0 };
329 : 1 : float blue[3] = { 0.0, 0.0, 1.0 };
330 : :
331 : : #define TEST_CONE(model, color) \
332 : : do { \
333 : : float tmp[3] = { (color)[0], (color)[1], (color)[2] }; \
334 : : pl_matrix3x3 mat = pl_get_cone_matrix(&(model), bt709); \
335 : : pl_matrix3x3_apply(&mat, tmp); \
336 : : printf("%s + %s = %f %f %f\n", #model, #color, tmp[0], tmp[1], tmp[2]); \
337 : : for (int i = 0; i < 3; i++) \
338 : : REQUIRE_FEQ((color)[i], tmp[i], 1e-5f); \
339 : : } while(0)
340 : :
341 : 1 : struct pl_cone_params red_only = { .cones = PL_CONE_MS };
342 : 1 : struct pl_cone_params green_only = { .cones = PL_CONE_LS };
343 : 1 : struct pl_cone_params blue_only = pl_vision_monochromacy;
344 : :
345 : : // These models should all round-trip white
346 [ - + + + ]: 4 : TEST_CONE(pl_vision_normal, white);
347 [ - + + + ]: 4 : TEST_CONE(pl_vision_protanopia, white);
348 [ - + + + ]: 4 : TEST_CONE(pl_vision_protanomaly, white);
349 [ - + + + ]: 4 : TEST_CONE(pl_vision_deuteranomaly, white);
350 [ - + + + ]: 4 : TEST_CONE(pl_vision_tritanomaly, white);
351 [ - + + + ]: 4 : TEST_CONE(pl_vision_achromatopsia, white);
352 [ - + + + ]: 4 : TEST_CONE(red_only, white);
353 [ - + + + ]: 4 : TEST_CONE(green_only, white);
354 [ - + + + ]: 4 : TEST_CONE(blue_only, white);
355 : :
356 : : // These models should round-trip blue
357 [ - + + + ]: 4 : TEST_CONE(pl_vision_normal, blue);
358 [ - + + + ]: 4 : TEST_CONE(pl_vision_protanomaly, blue);
359 [ - + + + ]: 4 : TEST_CONE(pl_vision_deuteranomaly, blue);
360 : :
361 : : // These models should round-trip red
362 [ - + + + ]: 4 : TEST_CONE(pl_vision_normal, red);
363 [ - + + + ]: 4 : TEST_CONE(pl_vision_tritanomaly, red);
364 [ - + + + ]: 4 : TEST_CONE(pl_vision_tritanopia, red);
365 : :
366 : : // These models should round-trip green
367 [ - + + + ]: 4 : TEST_CONE(pl_vision_normal, green);
368 : :
369 : : // Color adaptation tests
370 : 1 : struct pl_cie_xy d65 = pl_white_from_temp(6504);
371 [ - + ]: 1 : REQUIRE_FEQ(d65.x, 0.31271, 1e-3);
372 [ - + ]: 1 : REQUIRE_FEQ(d65.y, 0.32902, 1e-3);
373 : 1 : struct pl_cie_xy d55 = pl_white_from_temp(5503);
374 [ - + ]: 1 : REQUIRE_FEQ(d55.x, 0.33242, 1e-3);
375 [ - + ]: 1 : REQUIRE_FEQ(d55.y, 0.34743, 1e-3);
376 : :
377 : : // Make sure we infer the correct set of metadata parameters
378 : : #define TEST_METADATA(CSP, TYPE, MIN, MAX, AVG) \
379 : : do { \
380 : : float _min, _max, _avg; \
381 : : pl_color_space_nominal_luma_ex(pl_nominal_luma_params( \
382 : : .color = &(CSP), \
383 : : .metadata = TYPE, \
384 : : .scaling = PL_HDR_PQ, \
385 : : .out_min = &_min, \
386 : : .out_max = &_max, \
387 : : .out_avg = &_avg, \
388 : : )); \
389 : : const float _min_ref = pl_hdr_rescale(PL_HDR_NITS, PL_HDR_PQ, MIN); \
390 : : const float _max_ref = pl_hdr_rescale(PL_HDR_NITS, PL_HDR_PQ, MAX); \
391 : : const float _avg_ref = pl_hdr_rescale(PL_HDR_NITS, PL_HDR_PQ, AVG); \
392 : : REQUIRE_FEQ(_min, _min_ref, 1e-5); \
393 : : REQUIRE_FEQ(_max, _max_ref, 1e-5); \
394 : : REQUIRE_FEQ(_avg, _avg_ref, 1e-5); \
395 : : } while (0)
396 : :
397 : 1 : const struct pl_color_space hdr10plus = {
398 : : .primaries = PL_COLOR_PRIM_BT_2020,
399 : : .transfer = PL_COLOR_TRC_PQ,
400 : : .hdr = {
401 : : .min_luma = 0.005,
402 : : .max_luma = 4000,
403 : : .scene_max = {596.69, 1200, 500},
404 : : .scene_avg = 300,
405 : : },
406 : : };
407 : :
408 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&hdr10plus.hdr, PL_HDR_METADATA_ANY));
409 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&hdr10plus.hdr, PL_HDR_METADATA_NONE));
410 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&hdr10plus.hdr, PL_HDR_METADATA_HDR10));
411 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&hdr10plus.hdr, PL_HDR_METADATA_HDR10PLUS));
412 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&hdr10plus.hdr, PL_HDR_METADATA_CIE_Y));
413 : :
414 [ - + - + : 1 : TEST_METADATA(hdr10plus, PL_HDR_METADATA_NONE, PL_COLOR_HDR_BLACK, 10000, 0);
- + ]
415 [ - + - + : 1 : TEST_METADATA(hdr10plus, PL_HDR_METADATA_CIE_Y, PL_COLOR_HDR_BLACK, 4000, 0);
- + ]
416 [ - + - + : 1 : TEST_METADATA(hdr10plus, PL_HDR_METADATA_HDR10, PL_COLOR_HDR_BLACK, 4000, 0);
- + ]
417 [ - + - + : 1 : TEST_METADATA(hdr10plus, PL_HDR_METADATA_HDR10PLUS, PL_COLOR_HDR_BLACK, 1000, 250);
- + ]
418 [ - + - + : 1 : TEST_METADATA(hdr10plus, PL_HDR_METADATA_ANY, PL_COLOR_HDR_BLACK, 1000, 250);
- + ]
419 : :
420 : 3 : const struct pl_color_space dovi = {
421 : : .primaries = PL_COLOR_PRIM_BT_2020,
422 : : .transfer = PL_COLOR_TRC_PQ,
423 : : .hdr = {
424 : : .min_luma = 0.005,
425 : : .max_luma = 4000,
426 : 1 : .max_pq_y = pl_hdr_rescale(PL_HDR_NITS, PL_HDR_PQ, 1000),
427 : 1 : .avg_pq_y = pl_hdr_rescale(PL_HDR_NITS, PL_HDR_PQ, 250),
428 : : },
429 : : };
430 : :
431 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&dovi.hdr, PL_HDR_METADATA_ANY));
432 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&dovi.hdr, PL_HDR_METADATA_NONE));
433 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&dovi.hdr, PL_HDR_METADATA_HDR10));
434 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&dovi.hdr, PL_HDR_METADATA_CIE_Y));
435 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&dovi.hdr, PL_HDR_METADATA_HDR10PLUS));
436 : :
437 [ - + - + : 1 : TEST_METADATA(dovi, PL_HDR_METADATA_NONE, PL_COLOR_HDR_BLACK, 10000, 0);
- + ]
438 [ - + - + : 1 : TEST_METADATA(dovi, PL_HDR_METADATA_HDR10, PL_COLOR_HDR_BLACK, 4000, 0);
- + ]
439 [ - + - + : 1 : TEST_METADATA(dovi, PL_HDR_METADATA_HDR10PLUS, PL_COLOR_HDR_BLACK, 4000, 0);
- + ]
440 [ - + - + : 1 : TEST_METADATA(dovi, PL_HDR_METADATA_CIE_Y, PL_COLOR_HDR_BLACK, 1000, 250);
- + ]
441 [ - + - + : 1 : TEST_METADATA(dovi, PL_HDR_METADATA_ANY, PL_COLOR_HDR_BLACK, 1000, 250);
- + ]
442 : :
443 : 1 : const struct pl_color_space hlg4000 = {
444 : : .primaries = PL_COLOR_PRIM_BT_2020,
445 : : .transfer = PL_COLOR_TRC_HLG,
446 : : .hdr.max_luma = 4000,
447 : : .hdr.min_luma = 0.005,
448 : : };
449 : :
450 [ - + - + : 1 : TEST_METADATA(hlg4000, PL_HDR_METADATA_NONE, PL_COLOR_HDR_BLACK, PL_COLOR_HLG_PEAK, 0);
- + ]
451 [ - + - + : 1 : TEST_METADATA(hlg4000, PL_HDR_METADATA_HDR10, 0.005, 4000, 0);
- + ]
452 [ - + - + : 1 : TEST_METADATA(hlg4000, PL_HDR_METADATA_ANY, 0.005, 4000, 0);
- + ]
453 : :
454 : 1 : const struct pl_color_space untagged = {
455 : : .primaries = PL_COLOR_PRIM_BT_709,
456 : : .transfer = PL_COLOR_TRC_BT_1886,
457 : : };
458 : :
459 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&untagged.hdr, PL_HDR_METADATA_NONE));
460 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&untagged.hdr, PL_HDR_METADATA_ANY));
461 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&untagged.hdr, PL_HDR_METADATA_HDR10));
462 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&untagged.hdr, PL_HDR_METADATA_CIE_Y));
463 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&untagged.hdr, PL_HDR_METADATA_HDR10PLUS));
464 : :
465 : : const float sdr_black = PL_COLOR_SDR_WHITE / PL_COLOR_SDR_CONTRAST;
466 [ - + - + : 1 : TEST_METADATA(untagged, PL_HDR_METADATA_NONE, sdr_black, PL_COLOR_SDR_WHITE, 0);
- + ]
467 [ - + - + : 1 : TEST_METADATA(untagged, PL_HDR_METADATA_ANY, sdr_black, PL_COLOR_SDR_WHITE, 0);
- + ]
468 : :
469 : 1 : const struct pl_color_space sdr50 = {
470 : : .primaries = PL_COLOR_PRIM_BT_709,
471 : : .transfer = PL_COLOR_TRC_BT_1886,
472 : : .hdr.max_luma = 50,
473 : : };
474 : :
475 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&sdr50.hdr, PL_HDR_METADATA_NONE));
476 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&sdr50.hdr, PL_HDR_METADATA_ANY));
477 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&sdr50.hdr, PL_HDR_METADATA_HDR10));
478 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&sdr50.hdr, PL_HDR_METADATA_CIE_Y));
479 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&sdr50.hdr, PL_HDR_METADATA_HDR10PLUS));
480 : :
481 [ - + - + : 1 : TEST_METADATA(sdr50, PL_HDR_METADATA_NONE, sdr_black, PL_COLOR_SDR_WHITE, 0);
- + ]
482 [ - + - + : 1 : TEST_METADATA(sdr50, PL_HDR_METADATA_HDR10, 50 / PL_COLOR_SDR_CONTRAST, 50, 0);
- + ]
483 [ - + - + : 1 : TEST_METADATA(sdr50, PL_HDR_METADATA_ANY, 50 / PL_COLOR_SDR_CONTRAST, 50, 0);
- + ]
484 : :
485 : 1 : const struct pl_color_space sdr10k = {
486 : : .primaries = PL_COLOR_PRIM_BT_709,
487 : : .transfer = PL_COLOR_TRC_BT_1886,
488 : : .hdr.min_luma = PL_COLOR_SDR_WHITE / 10000,
489 : : };
490 : :
491 [ - + ]: 1 : REQUIRE(pl_hdr_metadata_contains(&sdr10k.hdr, PL_HDR_METADATA_NONE));
492 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&sdr10k.hdr, PL_HDR_METADATA_ANY));
493 [ - + ]: 1 : REQUIRE(!pl_hdr_metadata_contains(&sdr10k.hdr, PL_HDR_METADATA_HDR10));
494 [ - + - + : 1 : TEST_METADATA(sdr10k, PL_HDR_METADATA_NONE, sdr_black, PL_COLOR_SDR_WHITE, 0);
- + ]
495 [ - + - + : 1 : TEST_METADATA(sdr10k, PL_HDR_METADATA_HDR10, PL_COLOR_SDR_WHITE / 10000, PL_COLOR_SDR_WHITE, 0);
- + ]
496 [ - + - + : 1 : TEST_METADATA(sdr10k, PL_HDR_METADATA_ANY, PL_COLOR_SDR_WHITE / 10000, PL_COLOR_SDR_WHITE, 0);
- + ]
497 : :
498 : 1 : const struct pl_color_space bogus_vals = {
499 : : .primaries = PL_COLOR_PRIM_BT_2020,
500 : : .transfer = PL_COLOR_TRC_HLG,
501 : : .hdr.min_luma = 1e-9,
502 : : .hdr.max_luma = 1000000,
503 : : };
504 : :
505 : 1 : const struct pl_color_space bogus_flip = {
506 : : .primaries = PL_COLOR_PRIM_BT_2020,
507 : : .transfer = PL_COLOR_TRC_PQ,
508 : : .hdr.min_luma = 4000,
509 : : .hdr.max_luma = 0.05,
510 : : };
511 : :
512 : 1 : const struct pl_color_space bogus_sign = {
513 : : .primaries = PL_COLOR_PRIM_BT_2020,
514 : : .transfer = PL_COLOR_TRC_HLG,
515 : : .hdr.min_luma = -0.5,
516 : : .hdr.max_luma = -4000,
517 : : };
518 : :
519 [ - + - + : 1 : TEST_METADATA(bogus_vals, PL_HDR_METADATA_HDR10, PL_COLOR_HDR_BLACK, 10000, 0);
- + ]
520 [ - + - + : 1 : TEST_METADATA(bogus_flip, PL_HDR_METADATA_HDR10, PL_COLOR_HDR_BLACK, 10000, 0);
- + ]
521 [ - + - + : 1 : TEST_METADATA(bogus_sign, PL_HDR_METADATA_HDR10, PL_COLOR_HDR_BLACK, PL_COLOR_HLG_PEAK, 0);
- + ]
522 : : }
|