LCOV - code coverage report
Current view: top level - src/tests - colorspace.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 280 280 100.0 %
Date: 2025-03-29 09:04:10 Functions: 1 1 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 281 492 57.1 %

           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                 :            : }

Generated by: LCOV version 1.16