1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ColorUtils"
19 
20 #include <inttypes.h>
21 #include <arpa/inet.h>
22 #include <media/stagefright/foundation/ABuffer.h>
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/foundation/ALookup.h>
25 #include <media/stagefright/foundation/ColorUtils.h>
26 #include <media/NdkMediaFormatPriv.h>
27 
28 namespace android {
29 
30 // shortcut names for brevity in the following tables
31 typedef ColorAspects CA;
32 typedef ColorUtils CU;
33 
34 #define HI_UINT16(a) (((a) >> 8) & 0xFF)
35 #define LO_UINT16(a) ((a) & 0xFF)
36 
37 const static
38 ALookup<CU::ColorRange, CA::Range> sRanges{
39     {
40         { CU::kColorRangeLimited, CA::RangeLimited },
41         { CU::kColorRangeFull, CA::RangeFull },
42         { CU::kColorRangeUnspecified, CA::RangeUnspecified },
43     }
44 };
45 
46 const static
47 ALookup<CU::ColorStandard, std::pair<CA::Primaries, CA::MatrixCoeffs>> sStandards {
48     {
49         { CU::kColorStandardUnspecified,    { CA::PrimariesUnspecified, CA::MatrixUnspecified } },
50         { CU::kColorStandardBT709,          { CA::PrimariesBT709_5, CA::MatrixBT709_5 } },
51         { CU::kColorStandardBT601_625,      { CA::PrimariesBT601_6_625, CA::MatrixBT601_6 } },
52         { CU::kColorStandardBT601_625_Unadjusted,
53                                             // this is a really close match
54                                             { CA::PrimariesBT601_6_625, CA::MatrixBT709_5 } },
55         { CU::kColorStandardBT601_525,      { CA::PrimariesBT601_6_525, CA::MatrixBT601_6 } },
56         { CU::kColorStandardBT601_525_Unadjusted,
57                                             { CA::PrimariesBT601_6_525, CA::MatrixSMPTE240M } },
58         { CU::kColorStandardBT2020,         { CA::PrimariesBT2020, CA::MatrixBT2020 } },
59         { CU::kColorStandardBT2020Constant, { CA::PrimariesBT2020, CA::MatrixBT2020Constant } },
60         { CU::kColorStandardBT470M,         { CA::PrimariesBT470_6M, CA::MatrixBT470_6M } },
61         // NOTE: there is no close match to the matrix used by standard film, chose closest
62         { CU::kColorStandardFilm,           { CA::PrimariesGenericFilm, CA::MatrixBT2020 } },
63         // DCI-P3 (in DataSpace that drives this standard) is actually Display P3
64         // ITU does not specify a matrix suitable for P3. The theoretical KR/KB numbers are
65         // 0.229 and 0.079. Assume BT.601 matrix as P3 is commonly used for JPEG with BT.601.
66         { CU::kColorStandardDisplay_P3,     { CA::PrimariesEG432, CA::MatrixBT601_6 } },
67     }
68 };
69 
70 const static
71 ALookup<CU::ColorTransfer, CA::Transfer> sTransfers{
72     {
73         { CU::kColorTransferUnspecified,    CA::TransferUnspecified },
74         { CU::kColorTransferLinear,         CA::TransferLinear },
75         { CU::kColorTransferSRGB,           CA::TransferSRGB },
76         { CU::kColorTransferSMPTE_170M,     CA::TransferSMPTE170M },
77         { CU::kColorTransferGamma22,        CA::TransferGamma22 },
78         { CU::kColorTransferGamma28,        CA::TransferGamma28 },
79         { CU::kColorTransferST2084,         CA::TransferST2084 },
80         { CU::kColorTransferHLG,            CA::TransferHLG },
81     }
82 };
83 
isValid(ColorAspects::Primaries p)84 static bool isValid(ColorAspects::Primaries p) {
85     return p <= ColorAspects::PrimariesOther;
86 }
87 
isDefined(ColorAspects::Primaries p)88 static bool isDefined(ColorAspects::Primaries p) {
89     return p <= ColorAspects::PrimariesBT2020;
90 }
91 
isValid(ColorAspects::MatrixCoeffs c)92 static bool isValid(ColorAspects::MatrixCoeffs c) {
93     return c <= ColorAspects::MatrixOther;
94 }
95 
isDefined(ColorAspects::MatrixCoeffs c)96 static bool isDefined(ColorAspects::MatrixCoeffs c) {
97     return c <= ColorAspects::MatrixBT2020Constant;
98 }
99 
100 //static
wrapColorAspectsIntoColorStandard(ColorAspects::Primaries primaries,ColorAspects::MatrixCoeffs coeffs)101 int32_t ColorUtils::wrapColorAspectsIntoColorStandard(
102         ColorAspects::Primaries primaries, ColorAspects::MatrixCoeffs coeffs) {
103     ColorStandard res;
104     if (sStandards.map(std::make_pair(primaries, coeffs), &res)) {
105         return res;
106     } else if (!isValid(primaries) || !isValid(coeffs)) {
107         return kColorStandardUnspecified;
108     }
109 
110     // check platform media limits
111     uint32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
112     if (isDefined(primaries) && isDefined(coeffs)) {
113         return kColorStandardExtendedStart + primaries + coeffs * numPrimaries;
114     } else {
115         return kColorStandardVendorStart + primaries + coeffs * 0x100;
116     }
117 }
118 
119 //static
unwrapColorAspectsFromColorStandard(int32_t standard,ColorAspects::Primaries * primaries,ColorAspects::MatrixCoeffs * coeffs)120 status_t ColorUtils::unwrapColorAspectsFromColorStandard(
121         int32_t standard,
122         ColorAspects::Primaries *primaries, ColorAspects::MatrixCoeffs *coeffs) {
123     std::pair<ColorAspects::Primaries, ColorAspects::MatrixCoeffs> res;
124     if (sStandards.map((ColorStandard)standard, &res)) {
125         *primaries = res.first;
126         *coeffs = res.second;
127         return OK;
128     }
129 
130     int32_t start = kColorStandardExtendedStart;
131     int32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
132     int32_t numCoeffs = ColorAspects::MatrixBT2020Constant + 1;
133     if (standard >= (int32_t)kColorStandardVendorStart) {
134         start = kColorStandardVendorStart;
135         numPrimaries = ColorAspects::PrimariesOther + 1; // 0x100
136         numCoeffs = ColorAspects::MatrixOther + 1; // 0x100;
137     }
138     if (standard >= start && standard < start + numPrimaries * numCoeffs) {
139         int32_t product = standard - start;
140         *primaries = (ColorAspects::Primaries)(product % numPrimaries);
141         *coeffs = (ColorAspects::MatrixCoeffs)(product / numPrimaries);
142         return OK;
143     }
144     *primaries = ColorAspects::PrimariesOther;
145     *coeffs = ColorAspects::MatrixOther;
146     return BAD_VALUE;
147 }
148 
isValid(ColorAspects::Range r)149 static bool isValid(ColorAspects::Range r) {
150     return r <= ColorAspects::RangeOther;
151 }
152 
isDefined(ColorAspects::Range r)153 static bool isDefined(ColorAspects::Range r) {
154     return r <= ColorAspects::RangeLimited;
155 }
156 
157 //  static
wrapColorAspectsIntoColorRange(ColorAspects::Range range)158 int32_t ColorUtils::wrapColorAspectsIntoColorRange(ColorAspects::Range range) {
159     ColorRange res;
160     if (sRanges.map(range, &res)) {
161         return res;
162     } else if (!isValid(range)) {
163         return kColorRangeUnspecified;
164     } else {
165         CHECK(!isDefined(range));
166         // all platform values are in sRanges
167         return kColorRangeVendorStart + range;
168     }
169 }
170 
171 //static
unwrapColorAspectsFromColorRange(int32_t range,ColorAspects::Range * aspect)172 status_t ColorUtils::unwrapColorAspectsFromColorRange(
173         int32_t range, ColorAspects::Range *aspect) {
174     if (sRanges.map((ColorRange)range, aspect)) {
175         return OK;
176     }
177 
178     int32_t start = kColorRangeVendorStart;
179     int32_t numRanges = ColorAspects::RangeOther + 1; // 0x100
180     if (range >= start && range < start + numRanges) {
181         *aspect = (ColorAspects::Range)(range - start);
182         return OK;
183     }
184     *aspect = ColorAspects::RangeOther;
185     return BAD_VALUE;
186 }
187 
isValid(ColorAspects::Transfer t)188 static bool isValid(ColorAspects::Transfer t) {
189     return t <= ColorAspects::TransferOther;
190 }
191 
isDefined(ColorAspects::Transfer t)192 static bool isDefined(ColorAspects::Transfer t) {
193     return t <= ColorAspects::TransferHLG
194             || (t >= ColorAspects::TransferSMPTE240M && t <= ColorAspects::TransferST428);
195 }
196 
197 //  static
wrapColorAspectsIntoColorTransfer(ColorAspects::Transfer transfer)198 int32_t ColorUtils::wrapColorAspectsIntoColorTransfer(
199         ColorAspects::Transfer transfer) {
200     ColorTransfer res;
201     if (sTransfers.map(transfer, &res)) {
202         return res;
203     } else if (!isValid(transfer)) {
204         return kColorTransferUnspecified;
205     } else if (isDefined(transfer)) {
206         return kColorTransferExtendedStart + transfer;
207     } else {
208         // all platform values are in sRanges
209         return kColorTransferVendorStart + transfer;
210     }
211 }
212 
213 //static
unwrapColorAspectsFromColorTransfer(int32_t transfer,ColorAspects::Transfer * aspect)214 status_t ColorUtils::unwrapColorAspectsFromColorTransfer(
215         int32_t transfer, ColorAspects::Transfer *aspect) {
216     if (sTransfers.map((ColorTransfer)transfer, aspect)) {
217         return OK;
218     }
219 
220     int32_t start = kColorTransferExtendedStart;
221     int32_t numTransfers = ColorAspects::TransferST428 + 1;
222     if (transfer >= (int32_t)kColorTransferVendorStart) {
223         start = kColorTransferVendorStart;
224         numTransfers = ColorAspects::TransferOther + 1; // 0x100
225     }
226     if (transfer >= start && transfer < start + numTransfers) {
227         *aspect = (ColorAspects::Transfer)(transfer - start);
228         return OK;
229     }
230     *aspect = ColorAspects::TransferOther;
231     return BAD_VALUE;
232 }
233 
234 // static
convertPlatformColorAspectsToCodecAspects(int32_t range,int32_t standard,int32_t transfer,ColorAspects & aspects)235 status_t ColorUtils::convertPlatformColorAspectsToCodecAspects(
236     int32_t range, int32_t standard, int32_t transfer, ColorAspects &aspects) {
237     status_t res1 = unwrapColorAspectsFromColorRange(range, &aspects.mRange);
238     status_t res2 = unwrapColorAspectsFromColorStandard(
239             standard, &aspects.mPrimaries, &aspects.mMatrixCoeffs);
240     status_t res3 = unwrapColorAspectsFromColorTransfer(transfer, &aspects.mTransfer);
241     return res1 != OK ? res1 : (res2 != OK ? res2 : res3);
242 }
243 
244 // static
convertCodecColorAspectsToPlatformAspects(const ColorAspects & aspects,int32_t * range,int32_t * standard,int32_t * transfer)245 status_t ColorUtils::convertCodecColorAspectsToPlatformAspects(
246     const ColorAspects &aspects, int32_t *range, int32_t *standard, int32_t *transfer) {
247     *range = wrapColorAspectsIntoColorRange(aspects.mRange);
248     *standard = wrapColorAspectsIntoColorStandard(aspects.mPrimaries, aspects.mMatrixCoeffs);
249     *transfer = wrapColorAspectsIntoColorTransfer(aspects.mTransfer);
250     if (isValid(aspects.mRange) && isValid(aspects.mPrimaries)
251             && isValid(aspects.mMatrixCoeffs) && isValid(aspects.mTransfer)) {
252         return OK;
253     } else {
254         return BAD_VALUE;
255     }
256 }
257 
258 const static
259 ALookup<int32_t, ColorAspects::Primaries> sIsoPrimaries {
260     {
261         { 1, ColorAspects::PrimariesBT709_5 },
262         { 2, ColorAspects::PrimariesUnspecified },
263         { 4, ColorAspects::PrimariesBT470_6M },
264         { 5, ColorAspects::PrimariesBT601_6_625 },
265         { 6, ColorAspects::PrimariesBT601_6_525 /* main */},
266         { 7, ColorAspects::PrimariesBT601_6_525 },
267         // -- ITU T.832 201201 ends here
268         { 8, ColorAspects::PrimariesGenericFilm },
269         { 9, ColorAspects::PrimariesBT2020 },
270         { 10, ColorAspects::PrimariesOther /* XYZ */ },
271         { 11, ColorAspects::PrimariesRP431 },
272         { 12, ColorAspects::PrimariesEG432 },
273     }
274 };
275 
276 const static
277 ALookup<int32_t, ColorAspects::Transfer> sIsoTransfers {
278     {
279         { 1, ColorAspects::TransferSMPTE170M /* main */},
280         { 2, ColorAspects::TransferUnspecified },
281         { 4, ColorAspects::TransferGamma22 },
282         { 5, ColorAspects::TransferGamma28 },
283         { 6, ColorAspects::TransferSMPTE170M },
284         { 7, ColorAspects::TransferSMPTE240M },
285         { 8, ColorAspects::TransferLinear },
286         { 9, ColorAspects::TransferOther /* log 100:1 */ },
287         { 10, ColorAspects::TransferOther /* log 316:1 */ },
288         { 11, ColorAspects::TransferXvYCC },
289         { 12, ColorAspects::TransferBT1361 },
290         { 13, ColorAspects::TransferSRGB },
291         // -- ITU T.832 201201 ends here
292         { 14, ColorAspects::TransferSMPTE170M },
293         { 15, ColorAspects::TransferSMPTE170M },
294         { 16, ColorAspects::TransferST2084 },
295         { 17, ColorAspects::TransferST428 },
296         { 18, ColorAspects::TransferHLG },
297     }
298 };
299 
300 const static
301 ALookup<int32_t, ColorAspects::MatrixCoeffs> sIsoMatrixCoeffs {
302     {
303         { 0, ColorAspects::MatrixOther },
304         { 1, ColorAspects::MatrixBT709_5 },
305         { 2, ColorAspects::MatrixUnspecified },
306         { 4, ColorAspects::MatrixBT470_6M },
307         { 6, ColorAspects::MatrixBT601_6 /* main */ },
308         { 5, ColorAspects::MatrixBT601_6 },
309         { 7, ColorAspects::MatrixSMPTE240M },
310         { 8, ColorAspects::MatrixOther /* YCgCo */ },
311         // -- ITU T.832 201201 ends here
312         { 9, ColorAspects::MatrixBT2020 },
313         { 10, ColorAspects::MatrixBT2020Constant },
314     }
315 };
316 
317 // static
convertCodecColorAspectsToIsoAspects(const ColorAspects & aspects,int32_t * primaries,int32_t * transfer,int32_t * coeffs,bool * fullRange)318 void ColorUtils::convertCodecColorAspectsToIsoAspects(
319         const ColorAspects &aspects,
320         int32_t *primaries, int32_t *transfer, int32_t *coeffs, bool *fullRange) {
321     if (aspects.mPrimaries == ColorAspects::PrimariesOther ||
322             !sIsoPrimaries.map(aspects.mPrimaries, primaries)) {
323         CHECK(sIsoPrimaries.map(ColorAspects::PrimariesUnspecified, primaries));
324     }
325     if (aspects.mTransfer == ColorAspects::TransferOther ||
326             !sIsoTransfers.map(aspects.mTransfer, transfer)) {
327         CHECK(sIsoTransfers.map(ColorAspects::TransferUnspecified, transfer));
328     }
329     if (aspects.mMatrixCoeffs == ColorAspects::MatrixOther ||
330             !sIsoMatrixCoeffs.map(aspects.mMatrixCoeffs, coeffs)) {
331         CHECK(sIsoMatrixCoeffs.map(ColorAspects::MatrixUnspecified, coeffs));
332     }
333     *fullRange = aspects.mRange == ColorAspects::RangeFull;
334 }
335 
336 // static
convertIsoColorAspectsToCodecAspects(int32_t primaries,int32_t transfer,int32_t coeffs,bool fullRange,ColorAspects & aspects)337 void ColorUtils::convertIsoColorAspectsToCodecAspects(
338         int32_t primaries, int32_t transfer, int32_t coeffs, bool fullRange,
339         ColorAspects &aspects) {
340     if (!sIsoPrimaries.map(primaries, &aspects.mPrimaries)) {
341         aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
342     }
343     if (!sIsoTransfers.map(transfer, &aspects.mTransfer)) {
344         aspects.mTransfer = ColorAspects::TransferUnspecified;
345     }
346     if (!sIsoMatrixCoeffs.map(coeffs, &aspects.mMatrixCoeffs)) {
347         aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
348     }
349     aspects.mRange = fullRange ? ColorAspects::RangeFull : ColorAspects::RangeLimited;
350 }
351 
convertIsoColorAspectsToPlatformAspects(int32_t primaries,int32_t intransfer,int32_t coeffs,bool fullRange,int32_t * range,int32_t * standard,int32_t * outtransfer)352 void ColorUtils::convertIsoColorAspectsToPlatformAspects(
353         int32_t primaries, int32_t intransfer, int32_t coeffs, bool fullRange,
354         int32_t *range, int32_t *standard, int32_t *outtransfer) {
355     ColorAspects aspects;
356     convertIsoColorAspectsToCodecAspects(primaries, intransfer, coeffs, fullRange, aspects);
357     convertCodecColorAspectsToPlatformAspects(aspects, range, standard, outtransfer);
358 }
359 
360 // static
unpackToColorAspects(uint32_t packed)361 ColorAspects ColorUtils::unpackToColorAspects(uint32_t packed) {
362     ColorAspects aspects;
363     aspects.mRange        = (ColorAspects::Range)((packed >> 24) & 0xFF);
364     aspects.mPrimaries    = (ColorAspects::Primaries)((packed >> 16) & 0xFF);
365     aspects.mMatrixCoeffs = (ColorAspects::MatrixCoeffs)((packed >> 8) & 0xFF);
366     aspects.mTransfer     = (ColorAspects::Transfer)(packed & 0xFF);
367 
368     return aspects;
369 }
370 
371 // static
packToU32(const ColorAspects & aspects)372 uint32_t ColorUtils::packToU32(const ColorAspects &aspects) {
373     return (aspects.mRange << 24) | (aspects.mPrimaries << 16)
374             | (aspects.mMatrixCoeffs << 8) | aspects.mTransfer;
375 }
376 
377 // static
setDefaultCodecColorAspectsIfNeeded(ColorAspects & aspects,int32_t width,int32_t height)378 void ColorUtils::setDefaultCodecColorAspectsIfNeeded(
379         ColorAspects &aspects, int32_t width, int32_t height) {
380     ColorAspects::MatrixCoeffs coeffs;
381     ColorAspects::Primaries primaries;
382 
383     // Default to BT2020, BT709 or BT601 based on size. Allow 2.35:1 aspect ratio. Limit BT601
384     // to PAL or smaller, BT2020 to 4K or larger, leaving BT709 for all resolutions in between.
385     if (width >= 3840 || height >= 3840 || width * (int64_t)height >= 3840 * 1634) {
386         primaries = ColorAspects::PrimariesBT2020;
387         coeffs = ColorAspects::MatrixBT2020;
388     } else if ((width <= 720 && height > 480 && height <= 576)
389             || (height <= 720 && width > 480 && width <= 576)) {
390         primaries = ColorAspects::PrimariesBT601_6_625;
391         coeffs = ColorAspects::MatrixBT601_6;
392     } else if ((width <= 720 && height <= 480) || (height <= 720 && width <= 480)) {
393         primaries = ColorAspects::PrimariesBT601_6_525;
394         coeffs = ColorAspects::MatrixBT601_6;
395     } else {
396         primaries = ColorAspects::PrimariesBT709_5;
397         coeffs = ColorAspects::MatrixBT709_5;
398     }
399 
400     if (aspects.mRange == ColorAspects::RangeUnspecified) {
401         aspects.mRange = ColorAspects::RangeLimited;
402     }
403 
404     if (aspects.mPrimaries == ColorAspects::PrimariesUnspecified) {
405         aspects.mPrimaries = primaries;
406     }
407     if (aspects.mMatrixCoeffs == ColorAspects::MatrixUnspecified) {
408         aspects.mMatrixCoeffs = coeffs;
409     }
410     if (aspects.mTransfer == ColorAspects::TransferUnspecified) {
411         aspects.mTransfer = ColorAspects::TransferSMPTE170M;
412     }
413 }
414 
415 // TODO: move this into a Video HAL
416 const static
417 ALookup<CU::ColorStandard, std::pair<CA::Primaries, CA::MatrixCoeffs>> sStandardFallbacks {
418     {
419         { CU::kColorStandardBT601_625, { CA::PrimariesBT709_5, CA::MatrixBT470_6M } },
420         { CU::kColorStandardBT601_625, { CA::PrimariesBT709_5, CA::MatrixBT601_6 } },
421         { CU::kColorStandardBT709,     { CA::PrimariesBT709_5, CA::MatrixSMPTE240M } },
422         { CU::kColorStandardBT709,     { CA::PrimariesBT709_5, CA::MatrixBT2020 } },
423         { CU::kColorStandardBT601_525, { CA::PrimariesBT709_5, CA::MatrixBT2020Constant } },
424 
425         { CU::kColorStandardBT2020Constant,
426                                        { CA::PrimariesBT470_6M, CA::MatrixBT2020Constant } },
427 
428         { CU::kColorStandardBT601_625, { CA::PrimariesBT601_6_625, CA::MatrixBT470_6M } },
429         { CU::kColorStandardBT601_525, { CA::PrimariesBT601_6_625, CA::MatrixBT2020Constant } },
430 
431         { CU::kColorStandardBT601_525, { CA::PrimariesBT601_6_525, CA::MatrixBT470_6M } },
432         { CU::kColorStandardBT601_525, { CA::PrimariesBT601_6_525, CA::MatrixBT2020Constant } },
433 
434         { CU::kColorStandardBT2020Constant,
435                                        { CA::PrimariesGenericFilm, CA::MatrixBT2020Constant } },
436     }
437 };
438 
439 const static
440 ALookup<CU::ColorStandard, CA::Primaries> sStandardPrimariesFallbacks {
441     {
442         { CU::kColorStandardFilm,                 CA::PrimariesGenericFilm },
443         { CU::kColorStandardBT470M,               CA::PrimariesBT470_6M },
444         { CU::kColorStandardBT2020,               CA::PrimariesBT2020 },
445         { CU::kColorStandardBT601_525_Unadjusted, CA::PrimariesBT601_6_525 },
446         { CU::kColorStandardBT601_625_Unadjusted, CA::PrimariesBT601_6_625 },
447         { CU::kColorStandardDisplay_P3,           CA::PrimariesEG432 },
448         // fall back DCI P3 primaries to Display P3
449         { CU::kColorStandardDisplay_P3,           CA::PrimariesRP431 },
450     }
451 };
452 
453 const static
454 ALookup<android_dataspace, android_dataspace> sLegacyDataSpaceToV0 {
455     {
456         { HAL_DATASPACE_SRGB, HAL_DATASPACE_V0_SRGB },
457         { HAL_DATASPACE_BT709, HAL_DATASPACE_V0_BT709 },
458         { HAL_DATASPACE_SRGB_LINEAR, HAL_DATASPACE_V0_SRGB_LINEAR },
459         { HAL_DATASPACE_BT601_525, HAL_DATASPACE_V0_BT601_525 },
460         { HAL_DATASPACE_BT601_625, HAL_DATASPACE_V0_BT601_625 },
461         { HAL_DATASPACE_JFIF, HAL_DATASPACE_V0_JFIF },
462     }
463 };
464 
465 #define GET_HAL_ENUM(class, name) HAL_DATASPACE_##class##name
466 #define GET_HAL_BITFIELD(class, name) (GET_HAL_ENUM(class, _##name) >> GET_HAL_ENUM(class, _SHIFT))
467 
468 const static
469 ALookup<CU::ColorStandard, uint32_t> sGfxStandards {
470     {
471         { CU::kColorStandardUnspecified,          GET_HAL_BITFIELD(STANDARD, UNSPECIFIED) },
472         { CU::kColorStandardBT709,                GET_HAL_BITFIELD(STANDARD, BT709) },
473         { CU::kColorStandardBT601_625,            GET_HAL_BITFIELD(STANDARD, BT601_625) },
474         { CU::kColorStandardBT601_625_Unadjusted, GET_HAL_BITFIELD(STANDARD, BT601_625_UNADJUSTED) },
475         { CU::kColorStandardBT601_525,            GET_HAL_BITFIELD(STANDARD, BT601_525) },
476         { CU::kColorStandardBT601_525_Unadjusted, GET_HAL_BITFIELD(STANDARD, BT601_525_UNADJUSTED) },
477         { CU::kColorStandardBT2020,               GET_HAL_BITFIELD(STANDARD, BT2020) },
478         { CU::kColorStandardBT2020Constant,       GET_HAL_BITFIELD(STANDARD, BT2020_CONSTANT_LUMINANCE) },
479         { CU::kColorStandardBT470M,               GET_HAL_BITFIELD(STANDARD, BT470M) },
480         { CU::kColorStandardFilm,                 GET_HAL_BITFIELD(STANDARD, FILM) },
481         // DCI-P3 (in DataSpace that drives this standard) is actually Display P3
482         { CU::kColorStandardDisplay_P3,           GET_HAL_BITFIELD(STANDARD, DCI_P3) },
483     }
484 };
485 
486 // verify public values are stable
487 static_assert(CU::kColorStandardUnspecified == 0, "SDK mismatch"); // N
488 static_assert(CU::kColorStandardBT709 == 1, "SDK mismatch"); // N
489 static_assert(CU::kColorStandardBT601_625 == 2, "SDK mismatch"); // N
490 static_assert(CU::kColorStandardBT601_525 == 4, "SDK mismatch"); // N
491 static_assert(CU::kColorStandardBT2020 == 6, "SDK mismatch"); // N
492 
493 const static
494 ALookup<CU::ColorTransfer, uint32_t> sGfxTransfers {
495     {
496         { CU::kColorTransferUnspecified, GET_HAL_BITFIELD(TRANSFER, UNSPECIFIED) },
497         { CU::kColorTransferLinear,      GET_HAL_BITFIELD(TRANSFER, LINEAR) },
498         { CU::kColorTransferSRGB,        GET_HAL_BITFIELD(TRANSFER, SRGB) },
499         { CU::kColorTransferSMPTE_170M,  GET_HAL_BITFIELD(TRANSFER, SMPTE_170M) },
500         { CU::kColorTransferGamma22,     GET_HAL_BITFIELD(TRANSFER, GAMMA2_2) },
501         { CU::kColorTransferGamma28,     GET_HAL_BITFIELD(TRANSFER, GAMMA2_8) },
502         { CU::kColorTransferST2084,      GET_HAL_BITFIELD(TRANSFER, ST2084) },
503         { CU::kColorTransferHLG,         GET_HAL_BITFIELD(TRANSFER, HLG) },
504     }
505 };
506 
507 // verify public values are stable
508 static_assert(CU::kColorTransferUnspecified == 0, "SDK mismatch"); // N
509 static_assert(CU::kColorTransferLinear == 1, "SDK mismatch"); // N
510 static_assert(CU::kColorTransferSRGB == 2, "SDK mismatch"); // N
511 static_assert(CU::kColorTransferSMPTE_170M == 3, "SDK mismatch"); // N
512 static_assert(CU::kColorTransferST2084 == 6, "SDK mismatch"); // N
513 static_assert(CU::kColorTransferHLG == 7, "SDK mismatch"); // N
514 
515 const static
516 ALookup<CU::ColorRange, uint32_t> sGfxRanges {
517     {
518         { CU::kColorRangeUnspecified, GET_HAL_BITFIELD(RANGE, UNSPECIFIED) },
519         { CU::kColorRangeFull,        GET_HAL_BITFIELD(RANGE, FULL) },
520         { CU::kColorRangeLimited,     GET_HAL_BITFIELD(RANGE, LIMITED) },
521     }
522 };
523 
524 // verify public values are stable
525 static_assert(CU::kColorRangeUnspecified == 0, "SDK mismatch"); // N
526 static_assert(CU::kColorRangeFull == 1, "SDK mismatch"); // N
527 static_assert(CU::kColorRangeLimited == 2, "SDK mismatch"); // N
528 
529 #undef GET_HAL_BITFIELD
530 #undef GET_HAL_ENUM
531 
532 
convertDataSpaceToV0(android_dataspace & dataSpace)533 bool ColorUtils::convertDataSpaceToV0(android_dataspace &dataSpace) {
534     (void)sLegacyDataSpaceToV0.lookup(dataSpace, &dataSpace);
535     return (dataSpace & 0xC000FFFF) == 0;
536 }
537 
checkIfAspectsChangedAndUnspecifyThem(ColorAspects & aspects,const ColorAspects & orig,bool usePlatformAspects)538 bool ColorUtils::checkIfAspectsChangedAndUnspecifyThem(
539         ColorAspects &aspects, const ColorAspects &orig, bool usePlatformAspects) {
540     // remove changed aspects (change them to Unspecified)
541     bool changed = false;
542     if (aspects.mRange && aspects.mRange != orig.mRange) {
543         aspects.mRange = ColorAspects::RangeUnspecified;
544         changed = true;
545     }
546     if (aspects.mPrimaries && aspects.mPrimaries != orig.mPrimaries) {
547         aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
548         if (usePlatformAspects) {
549             aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
550         }
551         changed = true;
552     }
553     if (aspects.mMatrixCoeffs && aspects.mMatrixCoeffs != orig.mMatrixCoeffs) {
554         aspects.mMatrixCoeffs = ColorAspects::MatrixUnspecified;
555         if (usePlatformAspects) {
556             aspects.mPrimaries = ColorAspects::PrimariesUnspecified;
557         }
558         changed = true;
559     }
560     if (aspects.mTransfer && aspects.mTransfer != orig.mTransfer) {
561         aspects.mTransfer = ColorAspects::TransferUnspecified;
562         changed = true;
563     }
564     return changed;
565 }
566 
567 // static
getDataSpaceForColorAspects(ColorAspects & aspects,bool mayExpand)568 android_dataspace ColorUtils::getDataSpaceForColorAspects(ColorAspects &aspects, bool mayExpand) {
569     // This platform implementation never expands color space (e.g. returns an expanded
570     // dataspace to use where the codec does in-the-background color space conversion)
571     mayExpand = false;
572 
573     if (aspects.mRange == ColorAspects::RangeUnspecified
574             || aspects.mPrimaries == ColorAspects::PrimariesUnspecified
575             || aspects.mMatrixCoeffs == ColorAspects::MatrixUnspecified
576             || aspects.mTransfer == ColorAspects::TransferUnspecified) {
577         ALOGW("expected specified color aspects (%u:%u:%u:%u)",
578                 aspects.mRange, aspects.mPrimaries, aspects.mMatrixCoeffs, aspects.mTransfer);
579     }
580 
581     // default to video range and transfer
582     ColorRange range = kColorRangeLimited;
583     ColorTransfer transfer = kColorTransferSMPTE_170M;
584     (void)sRanges.map(aspects.mRange, &range);
585     (void)sTransfers.map(aspects.mTransfer, &transfer);
586 
587     ColorStandard standard = kColorStandardBT709;
588     auto pair = std::make_pair(aspects.mPrimaries, aspects.mMatrixCoeffs);
589     if (!sStandards.map(pair, &standard)) {
590         if (!sStandardFallbacks.map(pair, &standard)) {
591             (void)sStandardPrimariesFallbacks.map(aspects.mPrimaries, &standard);
592 
593             if (aspects.mMatrixCoeffs == ColorAspects::MatrixBT2020Constant) {
594                 range = kColorRangeFull;
595             }
596         }
597     }
598 
599     // assume 1-to-1 mapping to HAL values (to deal with potential vendor extensions)
600     uint32_t gfxRange = range;
601     uint32_t gfxStandard = standard;
602     uint32_t gfxTransfer = transfer;
603     bool mappedRange = sGfxRanges.map(range, &gfxRange);
604     bool mappedStandard = sGfxStandards.map(standard, &gfxStandard);
605     bool mappedTransfer = sGfxTransfers.map(transfer, &gfxTransfer);
606     if (! (mappedRange && mappedStandard && mappedTransfer)) {
607         ALOGW("could not safely map platform color aspects (R:%u(%s) S:%u(%s) T:%u(%s) to "
608               "graphics dataspace (R:%u S:%u T:%u)",
609               range, asString(range), standard, asString(standard), transfer, asString(transfer),
610               gfxRange, gfxStandard, gfxTransfer);
611     }
612 
613     android_dataspace dataSpace = (android_dataspace)(
614             (gfxRange << HAL_DATASPACE_RANGE_SHIFT) |
615             (gfxStandard << HAL_DATASPACE_STANDARD_SHIFT) |
616             (gfxTransfer << HAL_DATASPACE_TRANSFER_SHIFT));
617     (void)sLegacyDataSpaceToV0.rlookup(dataSpace, &dataSpace);
618 
619     if (!mayExpand) {
620         // update codec aspects based on dataspace
621         convertPlatformColorAspectsToCodecAspects(range, standard, transfer, aspects);
622     }
623     return dataSpace;
624 }
625 
626 // static
getColorConfigFromDataSpace(const android_dataspace & dataspace,int32_t * range,int32_t * standard,int32_t * transfer)627 void ColorUtils::getColorConfigFromDataSpace(
628         const android_dataspace &dataspace, int32_t *range, int32_t *standard, int32_t *transfer) {
629     uint32_t gfxRange =
630         (dataspace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
631     uint32_t gfxStandard =
632         (dataspace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
633     uint32_t gfxTransfer =
634         (dataspace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
635 
636     // assume 1-to-1 mapping to HAL values (to deal with potential vendor extensions)
637     CU::ColorRange    cuRange    = CU::kColorRangeUnspecified;
638     CU::ColorStandard cuStandard = CU::kColorStandardUnspecified;
639     CU::ColorTransfer cuTransfer = CU::kColorTransferUnspecified;
640     bool mappedRange = sGfxRanges.map(gfxRange, &cuRange);
641     bool mappedStandard = sGfxStandards.map(gfxStandard, &cuStandard);
642     bool mappedTransfer = sGfxTransfers.map(gfxTransfer, &cuTransfer);
643     if (! (mappedRange && mappedStandard && mappedTransfer)) {
644         ALOGW("could not safely map graphics dataspace (R:%u S:%u T:%u) to "
645               "platform color aspects (R:%u(%s) S:%u(%s) T:%u(%s)",
646               gfxRange, gfxStandard, gfxTransfer,
647               cuRange,    asString(cuRange),
648               cuStandard, asString(cuStandard),
649               cuTransfer, asString(cuTransfer));
650     }
651     *range    = cuRange;
652     *standard = cuStandard;
653     *transfer = cuTransfer;
654 }
655 
656 // static
getColorConfigFromFormat(const sp<AMessage> & format,int32_t * range,int32_t * standard,int32_t * transfer)657 void ColorUtils::getColorConfigFromFormat(
658         const sp<AMessage> &format, int32_t *range, int32_t *standard, int32_t *transfer) {
659     if (!format->findInt32("color-range", range)) {
660         *range = kColorRangeUnspecified;
661     }
662     if (!format->findInt32("color-standard", standard)) {
663         *standard = kColorStandardUnspecified;
664     }
665     if (!format->findInt32("color-transfer", transfer)) {
666         *transfer = kColorTransferUnspecified;
667     }
668 }
669 
670 // static
copyColorConfig(const sp<AMessage> & source,sp<AMessage> & target)671 void ColorUtils::copyColorConfig(const sp<AMessage> &source, sp<AMessage> &target) {
672     // 0 values are unspecified
673     int32_t value;
674     if (source->findInt32("color-range", &value)) {
675         target->setInt32("color-range", value);
676     }
677     if (source->findInt32("color-standard", &value)) {
678         target->setInt32("color-standard", value);
679     }
680     if (source->findInt32("color-transfer", &value)) {
681         target->setInt32("color-transfer", value);
682     }
683 }
684 
685 // static
getColorAspectsFromFormat(const sp<AMessage> & format,ColorAspects & aspects)686 void ColorUtils::getColorAspectsFromFormat(const sp<AMessage> &format, ColorAspects &aspects) {
687     int32_t range, standard, transfer;
688     getColorConfigFromFormat(format, &range, &standard, &transfer);
689 
690     if (convertPlatformColorAspectsToCodecAspects(
691             range, standard, transfer, aspects) != OK) {
692         ALOGW("Ignoring illegal color aspects(R:%d(%s), S:%d(%s), T:%d(%s))",
693                 range, asString((ColorRange)range),
694                 standard, asString((ColorStandard)standard),
695                 transfer, asString((ColorTransfer)transfer));
696         // Invalid values were converted to unspecified !params!, but otherwise were not changed
697         // For encoders, we leave these as is. For decoders, we will use default values.
698     }
699     ALOGV("Got color aspects (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) "
700           "from format (out:R:%d(%s), S:%d(%s), T:%d(%s))",
701             aspects.mRange, asString(aspects.mRange),
702             aspects.mPrimaries, asString(aspects.mPrimaries),
703             aspects.mMatrixCoeffs, asString(aspects.mMatrixCoeffs),
704             aspects.mTransfer, asString(aspects.mTransfer),
705             range, asString((ColorRange)range),
706             standard, asString((ColorStandard)standard),
707             transfer, asString((ColorTransfer)transfer));
708 }
709 
710 // static
setColorAspectsIntoFormat(const ColorAspects & aspects,sp<AMessage> & format,bool force)711 void ColorUtils::setColorAspectsIntoFormat(
712         const ColorAspects &aspects, sp<AMessage> &format, bool force) {
713     int32_t range = 0, standard = 0, transfer = 0;
714     convertCodecColorAspectsToPlatformAspects(aspects, &range, &standard, &transfer);
715     // save set values to base output format
716     // (encoder input format will read back actually supported values by the codec)
717     if (range != 0 || force) {
718         format->setInt32("color-range", range);
719     }
720     if (standard != 0 || force) {
721         format->setInt32("color-standard", standard);
722     }
723     if (transfer != 0 || force) {
724         format->setInt32("color-transfer", transfer);
725     }
726     ALOGV("Setting color aspects (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) "
727           "into format (out:R:%d(%s), S:%d(%s), T:%d(%s))",
728             aspects.mRange, asString(aspects.mRange),
729             aspects.mPrimaries, asString(aspects.mPrimaries),
730             aspects.mMatrixCoeffs, asString(aspects.mMatrixCoeffs),
731             aspects.mTransfer, asString(aspects.mTransfer),
732             range, asString((ColorRange)range),
733             standard, asString((ColorStandard)standard),
734             transfer, asString((ColorTransfer)transfer));
735 }
736 
737 // static
setHDRStaticInfoIntoFormat(const HDRStaticInfo & info,sp<AMessage> & format)738 void ColorUtils::setHDRStaticInfoIntoFormat(
739         const HDRStaticInfo &info, sp<AMessage> &format) {
740     sp<ABuffer> infoBuffer = new ABuffer(25);
741 
742     // Convert the data in infoBuffer to little endian format as defined by CTA-861-3
743     uint8_t *data = infoBuffer->data();
744     fillHdrStaticInfoBuffer(info, data);
745 
746     format->setBuffer("hdr-static-info", infoBuffer);
747 }
748 
749 // a simple method copied from Utils.cpp
U16LE_AT(const uint8_t * ptr)750 static uint16_t U16LE_AT(const uint8_t *ptr) {
751     return ptr[0] | (ptr[1] << 8);
752 }
753 
754 // static
getHDRStaticInfoFromFormat(const sp<AMessage> & format,HDRStaticInfo * info)755 bool ColorUtils::getHDRStaticInfoFromFormat(const sp<AMessage> &format, HDRStaticInfo *info) {
756     sp<ABuffer> buf;
757     if (!format->findBuffer("hdr-static-info", &buf)) {
758         return false;
759     }
760 
761     // TODO: Make this more flexible when adding more members to HDRStaticInfo
762     if (buf->size() != 25 /* static Metadata Type 1 size */) {
763         ALOGW("Ignore invalid HDRStaticInfo with size: %zu", buf->size());
764         return false;
765     }
766 
767     const uint8_t *data = buf->data();
768     if (*data != HDRStaticInfo::kType1) {
769         ALOGW("Unsupported static Metadata Type %u", *data);
770         return false;
771     }
772 
773     info->mID = HDRStaticInfo::kType1;
774     info->sType1.mR.x = U16LE_AT(&data[1]);
775     info->sType1.mR.y = U16LE_AT(&data[3]);
776     info->sType1.mG.x = U16LE_AT(&data[5]);
777     info->sType1.mG.y = U16LE_AT(&data[7]);
778     info->sType1.mB.x = U16LE_AT(&data[9]);
779     info->sType1.mB.y = U16LE_AT(&data[11]);
780     info->sType1.mW.x = U16LE_AT(&data[13]);
781     info->sType1.mW.y = U16LE_AT(&data[15]);
782     info->sType1.mMaxDisplayLuminance = U16LE_AT(&data[17]);
783     info->sType1.mMinDisplayLuminance = U16LE_AT(&data[19]);
784     info->sType1.mMaxContentLightLevel = U16LE_AT(&data[21]);
785     info->sType1.mMaxFrameAverageLightLevel = U16LE_AT(&data[23]);
786 
787     ALOGV("Got HDRStaticInfo from config (R: %u %u, G: %u %u, B: %u, %u, W: %u, %u, "
788             "MaxDispL: %u, MinDispL: %u, MaxContentL: %u, MaxFrameAvgL: %u)",
789             info->sType1.mR.x, info->sType1.mR.y, info->sType1.mG.x, info->sType1.mG.y,
790             info->sType1.mB.x, info->sType1.mB.y, info->sType1.mW.x, info->sType1.mW.y,
791             info->sType1.mMaxDisplayLuminance, info->sType1.mMinDisplayLuminance,
792             info->sType1.mMaxContentLightLevel, info->sType1.mMaxFrameAverageLightLevel);
793     return true;
794 }
795 
796 // static
isHDRStaticInfoValid(HDRStaticInfo * info)797 bool ColorUtils::isHDRStaticInfoValid(HDRStaticInfo *info) {
798     if (info->sType1.mMaxDisplayLuminance > 0.0f
799         && info->sType1.mMinDisplayLuminance > 0.0f)  return true;
800     if (info->sType1.mMaxContentLightLevel > 0.0f
801         && info->sType1.mMaxFrameAverageLightLevel > 0.0f)  return true;
802     return false;
803 }
804 
805 }  // namespace android
806 
807