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