1 // Copyright 2019 Joe Drago. All rights reserved.
2 // SPDX-License-Identifier: BSD-2-Clause
3 
4 #include "avif/internal.h"
5 
6 #include <math.h>
7 #include <string.h>
8 
9 struct avifColorPrimariesTable
10 {
11     avifColorPrimaries colorPrimariesEnum;
12     const char * name;
13     float primaries[8]; // rX, rY, gX, gY, bX, bY, wX, wY
14 };
15 static const struct avifColorPrimariesTable avifColorPrimariesTables[] = {
16     { AVIF_COLOR_PRIMARIES_BT709, "BT.709", { 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f } },
17     { AVIF_COLOR_PRIMARIES_BT470M, "BT.470-6 System M", { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.310f, 0.316f } },
18     { AVIF_COLOR_PRIMARIES_BT470BG, "BT.470-6 System BG", { 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f } },
19     { AVIF_COLOR_PRIMARIES_BT601, "BT.601", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
20     { AVIF_COLOR_PRIMARIES_SMPTE240, "SMPTE 240M", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } },
21     { AVIF_COLOR_PRIMARIES_GENERIC_FILM, "Generic film", { 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f } },
22     { AVIF_COLOR_PRIMARIES_BT2020, "BT.2020", { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f } },
23     { AVIF_COLOR_PRIMARIES_XYZ, "XYZ", { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.3333f, 0.3333f } },
24     { AVIF_COLOR_PRIMARIES_SMPTE431, "SMPTE RP 431-2", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f } },
25     { AVIF_COLOR_PRIMARIES_SMPTE432, "SMPTE EG 432-1 (DCI P3)", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f } },
26     { AVIF_COLOR_PRIMARIES_EBU3213, "EBU Tech. 3213-E", { 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f } }
27 };
28 static const int avifColorPrimariesTableSize = sizeof(avifColorPrimariesTables) / sizeof(avifColorPrimariesTables[0]);
29 
avifColorPrimariesGetValues(avifColorPrimaries acp,float outPrimaries[8])30 void avifColorPrimariesGetValues(avifColorPrimaries acp, float outPrimaries[8])
31 {
32     for (int i = 0; i < avifColorPrimariesTableSize; ++i) {
33         if (avifColorPrimariesTables[i].colorPrimariesEnum == acp) {
34             memcpy(outPrimaries, avifColorPrimariesTables[i].primaries, sizeof(avifColorPrimariesTables[i].primaries));
35             return;
36         }
37     }
38 
39     // if we get here, the color primaries are unknown. Just return a reasonable default.
40     memcpy(outPrimaries, avifColorPrimariesTables[0].primaries, sizeof(avifColorPrimariesTables[0].primaries));
41 }
42 
matchesTo3RoundedPlaces(float a,float b)43 static avifBool matchesTo3RoundedPlaces(float a, float b)
44 {
45     return (fabsf(a - b) < 0.001f);
46 }
47 
primariesMatch(const float p1[8],const float p2[8])48 static avifBool primariesMatch(const float p1[8], const float p2[8])
49 {
50     return matchesTo3RoundedPlaces(p1[0], p2[0]) && matchesTo3RoundedPlaces(p1[1], p2[1]) &&
51            matchesTo3RoundedPlaces(p1[2], p2[2]) && matchesTo3RoundedPlaces(p1[3], p2[3]) && matchesTo3RoundedPlaces(p1[4], p2[4]) &&
52            matchesTo3RoundedPlaces(p1[5], p2[5]) && matchesTo3RoundedPlaces(p1[6], p2[6]) && matchesTo3RoundedPlaces(p1[7], p2[7]);
53 }
54 
avifColorPrimariesFind(const float inPrimaries[8],const char ** outName)55 avifColorPrimaries avifColorPrimariesFind(const float inPrimaries[8], const char ** outName)
56 {
57     if (outName) {
58         *outName = NULL;
59     }
60 
61     for (int i = 0; i < avifColorPrimariesTableSize; ++i) {
62         if (primariesMatch(inPrimaries, avifColorPrimariesTables[i].primaries)) {
63             if (outName) {
64                 *outName = avifColorPrimariesTables[i].name;
65             }
66             return avifColorPrimariesTables[i].colorPrimariesEnum;
67         }
68     }
69     return AVIF_COLOR_PRIMARIES_UNKNOWN;
70 }
71 
72 struct avifMatrixCoefficientsTable
73 {
74     avifMatrixCoefficients matrixCoefficientsEnum;
75     const char * name;
76     const float kr;
77     const float kb;
78 };
79 
80 // https://www.itu.int/rec/T-REC-H.273-201612-I/en
81 static const struct avifMatrixCoefficientsTable matrixCoefficientsTables[] = {
82     //{ AVIF_MATRIX_COEFFICIENTS_IDENTITY, "Identity", 0.0f, 0.0f, }, // Handled elsewhere
83     { AVIF_MATRIX_COEFFICIENTS_BT709, "BT.709", 0.2126f, 0.0722f },
84     { AVIF_MATRIX_COEFFICIENTS_FCC, "FCC USFC 73.682", 0.30f, 0.11f },
85     { AVIF_MATRIX_COEFFICIENTS_BT470BG, "BT.470-6 System BG", 0.299f, 0.114f },
86     { AVIF_MATRIX_COEFFICIENTS_BT601, "BT.601", 0.299f, 0.114f },
87     { AVIF_MATRIX_COEFFICIENTS_SMPTE240, "SMPTE ST 240", 0.212f, 0.087f },
88     { AVIF_MATRIX_COEFFICIENTS_BT2020_NCL, "BT.2020 (non-constant luminance)", 0.2627f, 0.0593f },
89     //{ AVIF_MATRIX_COEFFICIENTS_BT2020_CL, "BT.2020 (constant luminance)", 0.2627f, 0.0593f }, // FIXME: It is not an linear transformation.
90     //{ AVIF_MATRIX_COEFFICIENTS_SMPTE2085, "ST 2085", 0.0f, 0.0f }, // FIXME: ST2085 can't represent using Kr and Kb.
91     //{ AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL, "Chromaticity-derived constant luminance system", 0.0f, 0.0f } // FIXME: It is not an linear transformation.
92     //{ AVIF_MATRIX_COEFFICIENTS_ICTCP, "BT.2100-0 ICtCp", 0.0f, 0.0f }, // FIXME: This can't represent using Kr and Kb.
93 };
94 
95 static const int avifMatrixCoefficientsTableSize = sizeof(matrixCoefficientsTables) / sizeof(matrixCoefficientsTables[0]);
96 
calcYUVInfoFromCICP(const avifImage * image,float coeffs[3])97 static avifBool calcYUVInfoFromCICP(const avifImage * image, float coeffs[3])
98 {
99     if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL) {
100         float primaries[8];
101         avifColorPrimariesGetValues(image->colorPrimaries, primaries);
102         float const rX = primaries[0];
103         float const rY = primaries[1];
104         float const gX = primaries[2];
105         float const gY = primaries[3];
106         float const bX = primaries[4];
107         float const bY = primaries[5];
108         float const wX = primaries[6];
109         float const wY = primaries[7];
110         float const rZ = 1.0f - (rX + rY); // (Eq. 34)
111         float const gZ = 1.0f - (gX + gY); // (Eq. 35)
112         float const bZ = 1.0f - (bX + bY); // (Eq. 36)
113         float const wZ = 1.0f - (wX + wY); // (Eq. 37)
114         float const kr = (rY * (wX * (gY * bZ - bY * gZ) + wY * (bX * gZ - gX * bZ) + wZ * (gX * bY - bX * gY))) /
115                          (wY * (rX * (gY * bZ - bY * gZ) + gX * (bY * rZ - rY * bZ) + bX * (rY * gZ - gY * rZ)));
116         // (Eq. 32)
117         float const kb = (bY * (wX * (rY * gZ - gY * rZ) + wY * (gX * rZ - rX * gZ) + wZ * (rX * gY - gX * rY))) /
118                          (wY * (rX * (gY * bZ - bY * gZ) + gX * (bY * rZ - rY * bZ) + bX * (rY * gZ - gY * rZ)));
119         // (Eq. 33)
120         coeffs[0] = kr;
121         coeffs[2] = kb;
122         coeffs[1] = 1.0f - coeffs[0] - coeffs[2];
123         return AVIF_TRUE;
124     } else {
125         for (int i = 0; i < avifMatrixCoefficientsTableSize; ++i) {
126             const struct avifMatrixCoefficientsTable * const table = &matrixCoefficientsTables[i];
127             if (table->matrixCoefficientsEnum == image->matrixCoefficients) {
128                 coeffs[0] = table->kr;
129                 coeffs[2] = table->kb;
130                 coeffs[1] = 1.0f - coeffs[0] - coeffs[2];
131                 return AVIF_TRUE;
132             }
133         }
134     }
135     return AVIF_FALSE;
136 }
137 
avifCalcYUVCoefficients(const avifImage * image,float * outR,float * outG,float * outB)138 void avifCalcYUVCoefficients(const avifImage * image, float * outR, float * outG, float * outB)
139 {
140     // (As of ISO/IEC 23000-22:2019 Amendment 2)
141     // MIAF Section 7.3.6.4 "Colour information property":
142     //
143     // If a coded image has no associated colour property, the default property is defined as having
144     // colour_type equal to 'nclx' with properties as follows:
145     // –   colour_primaries equal to 1,
146     // –   transfer_characteristics equal to 13,
147     // –   matrix_coefficients equal to 5 or 6 (which are functionally identical), and
148     // –   full_range_flag equal to 1.
149     // Only if the colour information property of the image matches these default values, the colour
150     // property may be omitted; all other images shall have an explicitly declared colour space via
151     // association with a property of this type.
152     //
153     // See here for the discussion: https://github.com/AOMediaCodec/av1-avif/issues/77#issuecomment-676526097
154 
155     // matrix_coefficients of [5,6] == BT.601:
156     float kr = 0.299f;
157     float kb = 0.114f;
158     float kg = 1.0f - kr - kb;
159 
160     float coeffs[3];
161     if (calcYUVInfoFromCICP(image, coeffs)) {
162         kr = coeffs[0];
163         kg = coeffs[1];
164         kb = coeffs[2];
165     }
166 
167     *outR = kr;
168     *outG = kg;
169     *outB = kb;
170 }
171