ColorUtils.cpp revision 3a09d8d6f909063990a5681b15a442b2ba8ce54a
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
22#include <media/stagefright/foundation/ADebug.h>
23#include <media/stagefright/foundation/ALookup.h>
24#include <media/stagefright/foundation/ColorUtils.h>
25
26namespace android {
27
28// shortcut names for brevity in the following tables
29typedef ColorAspects CA;
30typedef ColorUtils CU;
31
32ALookup<CU::ColorRange, CA::Range> sRanges{
33    {
34        { CU::kColorRangeLimited, CA::RangeLimited },
35        { CU::kColorRangeFull, CA::RangeFull },
36        { CU::kColorRangeUnspecified, CA::RangeUnspecified },
37    }
38};
39
40ALookup<CU::ColorStandard, std::pair<CA::Primaries, CA::MatrixCoeffs>> sStandards {
41    {
42        { CU::kColorStandardUnspecified,    { CA::PrimariesUnspecified, CA::MatrixUnspecified } },
43        { CU::kColorStandardBT709,          { CA::PrimariesBT709_5, CA::MatrixBT709_5 } },
44        { CU::kColorStandardBT601_625,      { CA::PrimariesBT601_6_625, CA::MatrixBT601_6 } },
45        { CU::kColorStandardBT601_625_Unadjusted,
46                                            // this is a really close match
47                                            { CA::PrimariesBT601_6_625, CA::MatrixBT709_5 } },
48        { CU::kColorStandardBT601_525,      { CA::PrimariesBT601_6_525, CA::MatrixBT601_6 } },
49        { CU::kColorStandardBT601_525_Unadjusted,
50                                            { CA::PrimariesBT601_6_525, CA::MatrixSMPTE240M } },
51        { CU::kColorStandardBT2020,         { CA::PrimariesBT2020, CA::MatrixBT2020 } },
52        { CU::kColorStandardBT2020Constant, { CA::PrimariesBT2020, CA::MatrixBT2020Constant } },
53        { CU::kColorStandardBT470M,         { CA::PrimariesBT470_6M, CA::MatrixBT470_6M } },
54        // NOTE: there is no close match to the matrix used by standard film, chose closest
55        { CU::kColorStandardFilm,           { CA::PrimariesGenericFilm, CA::MatrixBT2020 } },
56    }
57};
58
59ALookup<CU::ColorTransfer, CA::Transfer> sTransfers{
60    {
61        { CU::kColorTransferUnspecified,    CA::TransferUnspecified },
62        { CU::kColorTransferLinear,         CA::TransferLinear },
63        { CU::kColorTransferSRGB,           CA::TransferSRGB },
64        { CU::kColorTransferSMPTE_170M,     CA::TransferSMPTE170M },
65        { CU::kColorTransferGamma22,        CA::TransferGamma22 },
66        { CU::kColorTransferGamma28,        CA::TransferGamma28 },
67        { CU::kColorTransferST2084,         CA::TransferST2084 },
68        { CU::kColorTransferHLG,            CA::TransferHLG },
69    }
70};
71
72static bool isValid(ColorAspects::Primaries p) {
73    return p <= ColorAspects::PrimariesOther;
74}
75
76static bool isDefined(ColorAspects::Primaries p) {
77    return p <= ColorAspects::PrimariesBT2020;
78}
79
80static bool isValid(ColorAspects::MatrixCoeffs c) {
81    return c <= ColorAspects::MatrixOther;
82}
83
84static bool isDefined(ColorAspects::MatrixCoeffs c) {
85    return c <= ColorAspects::MatrixBT2020Constant;
86}
87
88//static
89int32_t ColorUtils::wrapColorAspectsIntoColorStandard(
90        ColorAspects::Primaries primaries, ColorAspects::MatrixCoeffs coeffs) {
91    ColorStandard res;
92    if (sStandards.map(std::make_pair(primaries, coeffs), &res)) {
93        return res;
94    } else if (!isValid(primaries) || !isValid(coeffs)) {
95        return kColorStandardUnspecified;
96    }
97
98    // check platform media limits
99    uint32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
100    if (isDefined(primaries) && isDefined(coeffs)) {
101        return kColorStandardExtendedStart + primaries + coeffs * numPrimaries;
102    } else {
103        return kColorStandardVendorStart + primaries + coeffs * 0x100;
104    }
105}
106
107//static
108status_t ColorUtils::unwrapColorAspectsFromColorStandard(
109        int32_t standard,
110        ColorAspects::Primaries *primaries, ColorAspects::MatrixCoeffs *coeffs) {
111    std::pair<ColorAspects::Primaries, ColorAspects::MatrixCoeffs> res;
112    if (sStandards.map((ColorStandard)standard, &res)) {
113        *primaries = res.first;
114        *coeffs = res.second;
115        return OK;
116    }
117
118    int32_t start = kColorStandardExtendedStart;
119    int32_t numPrimaries = ColorAspects::PrimariesBT2020 + 1;
120    int32_t numCoeffs = ColorAspects::MatrixBT2020Constant + 1;
121    if (standard >= (int32_t)kColorStandardVendorStart) {
122        start = kColorStandardVendorStart;
123        numPrimaries = ColorAspects::PrimariesOther + 1; // 0x100
124        numCoeffs = ColorAspects::MatrixOther + 1; // 0x100;
125    }
126    if (standard >= start && standard < start + numPrimaries * numCoeffs) {
127        int32_t product = standard - start;
128        *primaries = (ColorAspects::Primaries)(product % numPrimaries);
129        *coeffs = (ColorAspects::MatrixCoeffs)(product / numPrimaries);
130        return OK;
131    }
132    *primaries = ColorAspects::PrimariesOther;
133    *coeffs = ColorAspects::MatrixOther;
134    return BAD_VALUE;
135}
136
137static bool isValid(ColorAspects::Range r) {
138    return r <= ColorAspects::RangeOther;
139}
140
141static bool isDefined(ColorAspects::Range r) {
142    return r <= ColorAspects::RangeLimited;
143}
144
145//  static
146int32_t ColorUtils::wrapColorAspectsIntoColorRange(ColorAspects::Range range) {
147    ColorRange res;
148    if (sRanges.map(range, &res)) {
149        return res;
150    } else if (!isValid(range)) {
151        return kColorRangeUnspecified;
152    } else {
153        CHECK(!isDefined(range));
154        // all platform values are in sRanges
155        return kColorRangeVendorStart + range;
156    }
157}
158
159//static
160status_t ColorUtils::unwrapColorAspectsFromColorRange(
161        int32_t range, ColorAspects::Range *aspect) {
162    if (sRanges.map((ColorRange)range, aspect)) {
163        return OK;
164    }
165
166    int32_t start = kColorRangeVendorStart;
167    int32_t numRanges = ColorAspects::RangeOther + 1; // 0x100
168    if (range >= start && range < start + numRanges) {
169        *aspect = (ColorAspects::Range)(range - start);
170        return OK;
171    }
172    *aspect = ColorAspects::RangeOther;
173    return BAD_VALUE;
174}
175
176static bool isValid(ColorAspects::Transfer t) {
177    return t <= ColorAspects::TransferOther;
178}
179
180static bool isDefined(ColorAspects::Transfer t) {
181    return t <= ColorAspects::TransferHLG
182            || (t >= ColorAspects::TransferSMPTE240M && t <= ColorAspects::TransferST428);
183}
184
185//  static
186int32_t ColorUtils::wrapColorAspectsIntoColorTransfer(
187        ColorAspects::Transfer transfer) {
188    ColorTransfer res;
189    if (sTransfers.map(transfer, &res)) {
190        return res;
191    } else if (!isValid(transfer)) {
192        return kColorTransferUnspecified;
193    } else if (isDefined(transfer)) {
194        return kColorTransferExtendedStart + transfer;
195    } else {
196        // all platform values are in sRanges
197        return kColorTransferVendorStart + transfer;
198    }
199}
200
201//static
202status_t ColorUtils::unwrapColorAspectsFromColorTransfer(
203        int32_t transfer, ColorAspects::Transfer *aspect) {
204    if (sTransfers.map((ColorTransfer)transfer, aspect)) {
205        return OK;
206    }
207
208    int32_t start = kColorTransferExtendedStart;
209    int32_t numTransfers = ColorAspects::TransferST428 + 1;
210    if (transfer >= (int32_t)kColorTransferVendorStart) {
211        start = kColorTransferVendorStart;
212        numTransfers = ColorAspects::TransferOther + 1; // 0x100
213    }
214    if (transfer >= start && transfer < start + numTransfers) {
215        *aspect = (ColorAspects::Transfer)(transfer - start);
216        return OK;
217    }
218    *aspect = ColorAspects::TransferOther;
219    return BAD_VALUE;
220}
221
222// static
223status_t ColorUtils::convertPlatformColorAspectsToCodecAspects(
224    int32_t range, int32_t standard, int32_t transfer, ColorAspects &aspects) {
225    status_t res1 = unwrapColorAspectsFromColorRange(range, &aspects.mRange);
226    status_t res2 = unwrapColorAspectsFromColorStandard(
227            standard, &aspects.mPrimaries, &aspects.mMatrixCoeffs);
228    status_t res3 = unwrapColorAspectsFromColorTransfer(transfer, &aspects.mTransfer);
229    return res1 != OK ? res1 : (res2 != OK ? res2 : res3);
230}
231
232// static
233status_t ColorUtils::convertCodecColorAspectsToPlatformAspects(
234    const ColorAspects &aspects, int32_t *range, int32_t *standard, int32_t *transfer) {
235    *range = wrapColorAspectsIntoColorRange(aspects.mRange);
236    *standard = wrapColorAspectsIntoColorStandard(aspects.mPrimaries, aspects.mMatrixCoeffs);
237    *transfer = wrapColorAspectsIntoColorTransfer(aspects.mTransfer);
238    if (isValid(aspects.mRange) && isValid(aspects.mPrimaries)
239            && isValid(aspects.mMatrixCoeffs) && isValid(aspects.mTransfer)) {
240        return OK;
241    } else {
242        return BAD_VALUE;
243    }
244}
245
246// static
247void ColorUtils::setDefaultPlatformColorAspectsIfNeeded(
248        int32_t &range, int32_t &standard, int32_t &transfer,
249        int32_t width, int32_t height) {
250    if (range == ColorUtils::kColorRangeUnspecified) {
251        range = ColorUtils::kColorRangeLimited;
252    }
253
254    if (standard == ColorUtils::kColorStandardUnspecified) {
255        // Default to BT2020, BT709 or BT601 based on size. Allow 2.35:1 aspect ratio. Limit BT601
256        // to PAL or smaller, BT2020 to 4K or larger, leaving BT709 for all resolutions in between.
257        if (width >= 3840 || height >= 3840 || width * (int64_t)height >= 3840 * 1634) {
258            standard = ColorUtils::kColorStandardBT2020;
259        } else if ((width <= 720 && height > 480) || (height <= 720 && width > 480)) {
260            standard = ColorUtils::kColorStandardBT601_625;
261        } else if ((width <= 720 && height <= 480) || (height <= 720 && width <= 480)) {
262            standard = ColorUtils::kColorStandardBT601_525;
263        } else {
264            standard = ColorUtils::kColorStandardBT709;
265        }
266    }
267
268    if (transfer == ColorUtils::kColorTransferUnspecified) {
269        transfer = ColorUtils::kColorTransferSMPTE_170M;
270    }
271}
272
273// static
274void ColorUtils::setDefaultCodecColorAspectsIfNeeded(
275        ColorAspects &aspects, int32_t width, int32_t height) {
276    ColorAspects::MatrixCoeffs coeffs;
277    ColorAspects::Primaries primaries;
278
279    // Default to BT2020, BT709 or BT601 based on size. Allow 2.35:1 aspect ratio. Limit BT601
280    // to PAL or smaller, BT2020 to 4K or larger, leaving BT709 for all resolutions in between.
281    if (width >= 3840 || height >= 3840 || width * (int64_t)height >= 3840 * 1634) {
282        primaries = ColorAspects::PrimariesBT2020;
283        coeffs = ColorAspects::MatrixBT2020;
284    } else if ((width <= 720 && height > 480 && height <= 576)
285            || (height <= 720 && width > 480 && width <= 576)) {
286        primaries = ColorAspects::PrimariesBT601_6_625;
287        coeffs = ColorAspects::MatrixBT601_6;
288    } else if ((width <= 720 && height <= 480) || (height <= 720 && width <= 480)) {
289        primaries = ColorAspects::PrimariesBT601_6_525;
290        coeffs = ColorAspects::MatrixBT601_6;
291    } else {
292        primaries = ColorAspects::PrimariesBT709_5;
293        coeffs = ColorAspects::MatrixBT709_5;
294    }
295
296    if (aspects.mRange == ColorAspects::RangeUnspecified) {
297        aspects.mRange = ColorAspects::RangeLimited;
298    }
299
300    if (aspects.mPrimaries == ColorAspects::PrimariesUnspecified) {
301        aspects.mPrimaries = primaries;
302    }
303    if (aspects.mMatrixCoeffs == ColorAspects::MatrixUnspecified) {
304        aspects.mMatrixCoeffs = coeffs;
305    }
306    if (aspects.mTransfer == ColorAspects::TransferUnspecified) {
307        aspects.mTransfer = ColorAspects::TransferSMPTE170M;
308    }
309}
310
311}  // namespace android
312
313