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