1*89a0ef05SAndroid Build Coastguard Worker /*
2*89a0ef05SAndroid Build Coastguard Worker * Copyright 2024 The Android Open Source Project
3*89a0ef05SAndroid Build Coastguard Worker *
4*89a0ef05SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*89a0ef05SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*89a0ef05SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*89a0ef05SAndroid Build Coastguard Worker *
8*89a0ef05SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*89a0ef05SAndroid Build Coastguard Worker *
10*89a0ef05SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*89a0ef05SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*89a0ef05SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*89a0ef05SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*89a0ef05SAndroid Build Coastguard Worker * limitations under the License.
15*89a0ef05SAndroid Build Coastguard Worker */
16*89a0ef05SAndroid Build Coastguard Worker
17*89a0ef05SAndroid Build Coastguard Worker #include <algorithm>
18*89a0ef05SAndroid Build Coastguard Worker #include <cmath>
19*89a0ef05SAndroid Build Coastguard Worker
20*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/gainmapmath.h"
21*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/gainmapmetadata.h"
22*89a0ef05SAndroid Build Coastguard Worker
23*89a0ef05SAndroid Build Coastguard Worker namespace ultrahdr {
24*89a0ef05SAndroid Build Coastguard Worker
streamWriteU8(std::vector<uint8_t> & data,uint8_t value)25*89a0ef05SAndroid Build Coastguard Worker void streamWriteU8(std::vector<uint8_t> &data, uint8_t value) { data.push_back(value); }
26*89a0ef05SAndroid Build Coastguard Worker
streamWriteU16(std::vector<uint8_t> & data,uint16_t value)27*89a0ef05SAndroid Build Coastguard Worker void streamWriteU16(std::vector<uint8_t> &data, uint16_t value) {
28*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 8) & 0xff);
29*89a0ef05SAndroid Build Coastguard Worker data.push_back(value & 0xff);
30*89a0ef05SAndroid Build Coastguard Worker }
31*89a0ef05SAndroid Build Coastguard Worker
streamWriteU32(std::vector<uint8_t> & data,uint32_t value)32*89a0ef05SAndroid Build Coastguard Worker void streamWriteU32(std::vector<uint8_t> &data, uint32_t value) {
33*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 24) & 0xff);
34*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 16) & 0xff);
35*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 8) & 0xff);
36*89a0ef05SAndroid Build Coastguard Worker data.push_back(value & 0xff);
37*89a0ef05SAndroid Build Coastguard Worker }
38*89a0ef05SAndroid Build Coastguard Worker
streamWriteS32(std::vector<uint8_t> & data,int32_t value)39*89a0ef05SAndroid Build Coastguard Worker void streamWriteS32(std::vector<uint8_t> &data, int32_t value) {
40*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 24) & 0xff);
41*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 16) & 0xff);
42*89a0ef05SAndroid Build Coastguard Worker data.push_back((value >> 8) & 0xff);
43*89a0ef05SAndroid Build Coastguard Worker data.push_back(value & 0xff);
44*89a0ef05SAndroid Build Coastguard Worker }
45*89a0ef05SAndroid Build Coastguard Worker
streamReadU8(const std::vector<uint8_t> & data,uint8_t & value,size_t & pos)46*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t streamReadU8(const std::vector<uint8_t> &data, uint8_t &value, size_t &pos) {
47*89a0ef05SAndroid Build Coastguard Worker if (pos >= data.size()) {
48*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
49*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_MEM_ERROR;
50*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
51*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
52*89a0ef05SAndroid Build Coastguard Worker "attempting to read byte at position %d when the buffer size is %d", (int)pos,
53*89a0ef05SAndroid Build Coastguard Worker (int)data.size());
54*89a0ef05SAndroid Build Coastguard Worker return status;
55*89a0ef05SAndroid Build Coastguard Worker }
56*89a0ef05SAndroid Build Coastguard Worker value = data[pos++];
57*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
58*89a0ef05SAndroid Build Coastguard Worker }
59*89a0ef05SAndroid Build Coastguard Worker
streamReadU16(const std::vector<uint8_t> & data,uint16_t & value,size_t & pos)60*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t streamReadU16(const std::vector<uint8_t> &data, uint16_t &value, size_t &pos) {
61*89a0ef05SAndroid Build Coastguard Worker if (pos + 1 >= data.size()) {
62*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
63*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_MEM_ERROR;
64*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
65*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
66*89a0ef05SAndroid Build Coastguard Worker "attempting to read 2 bytes from position %d when the buffer size is %d", (int)pos,
67*89a0ef05SAndroid Build Coastguard Worker (int)data.size());
68*89a0ef05SAndroid Build Coastguard Worker return status;
69*89a0ef05SAndroid Build Coastguard Worker }
70*89a0ef05SAndroid Build Coastguard Worker value = (data[pos] << 8 | data[pos + 1]);
71*89a0ef05SAndroid Build Coastguard Worker pos += 2;
72*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
73*89a0ef05SAndroid Build Coastguard Worker }
74*89a0ef05SAndroid Build Coastguard Worker
streamReadU32(const std::vector<uint8_t> & data,uint32_t & value,size_t & pos)75*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t streamReadU32(const std::vector<uint8_t> &data, uint32_t &value, size_t &pos) {
76*89a0ef05SAndroid Build Coastguard Worker if (pos + 3 >= data.size()) {
77*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
78*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_MEM_ERROR;
79*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
80*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
81*89a0ef05SAndroid Build Coastguard Worker "attempting to read 4 bytes from position %d when the buffer size is %d", (int)pos,
82*89a0ef05SAndroid Build Coastguard Worker (int)data.size());
83*89a0ef05SAndroid Build Coastguard Worker return status;
84*89a0ef05SAndroid Build Coastguard Worker }
85*89a0ef05SAndroid Build Coastguard Worker value = (data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3]);
86*89a0ef05SAndroid Build Coastguard Worker pos += 4;
87*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
88*89a0ef05SAndroid Build Coastguard Worker }
89*89a0ef05SAndroid Build Coastguard Worker
streamReadS32(const std::vector<uint8_t> & data,int32_t & value,size_t & pos)90*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t streamReadS32(const std::vector<uint8_t> &data, int32_t &value, size_t &pos) {
91*89a0ef05SAndroid Build Coastguard Worker if (pos + 3 >= data.size()) {
92*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
93*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_MEM_ERROR;
94*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
95*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
96*89a0ef05SAndroid Build Coastguard Worker "attempting to read 4 bytes from position %d when the buffer size is %d", (int)pos,
97*89a0ef05SAndroid Build Coastguard Worker (int)data.size());
98*89a0ef05SAndroid Build Coastguard Worker return status;
99*89a0ef05SAndroid Build Coastguard Worker }
100*89a0ef05SAndroid Build Coastguard Worker value = (data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3]);
101*89a0ef05SAndroid Build Coastguard Worker pos += 4;
102*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
103*89a0ef05SAndroid Build Coastguard Worker }
104*89a0ef05SAndroid Build Coastguard Worker
allChannelsIdentical() const105*89a0ef05SAndroid Build Coastguard Worker bool uhdr_gainmap_metadata_frac::allChannelsIdentical() const {
106*89a0ef05SAndroid Build Coastguard Worker return gainMapMinN[0] == gainMapMinN[1] && gainMapMinN[0] == gainMapMinN[2] &&
107*89a0ef05SAndroid Build Coastguard Worker gainMapMinD[0] == gainMapMinD[1] && gainMapMinD[0] == gainMapMinD[2] &&
108*89a0ef05SAndroid Build Coastguard Worker gainMapMaxN[0] == gainMapMaxN[1] && gainMapMaxN[0] == gainMapMaxN[2] &&
109*89a0ef05SAndroid Build Coastguard Worker gainMapMaxD[0] == gainMapMaxD[1] && gainMapMaxD[0] == gainMapMaxD[2] &&
110*89a0ef05SAndroid Build Coastguard Worker gainMapGammaN[0] == gainMapGammaN[1] && gainMapGammaN[0] == gainMapGammaN[2] &&
111*89a0ef05SAndroid Build Coastguard Worker gainMapGammaD[0] == gainMapGammaD[1] && gainMapGammaD[0] == gainMapGammaD[2] &&
112*89a0ef05SAndroid Build Coastguard Worker baseOffsetN[0] == baseOffsetN[1] && baseOffsetN[0] == baseOffsetN[2] &&
113*89a0ef05SAndroid Build Coastguard Worker baseOffsetD[0] == baseOffsetD[1] && baseOffsetD[0] == baseOffsetD[2] &&
114*89a0ef05SAndroid Build Coastguard Worker alternateOffsetN[0] == alternateOffsetN[1] && alternateOffsetN[0] == alternateOffsetN[2] &&
115*89a0ef05SAndroid Build Coastguard Worker alternateOffsetD[0] == alternateOffsetD[1] && alternateOffsetD[0] == alternateOffsetD[2];
116*89a0ef05SAndroid Build Coastguard Worker }
117*89a0ef05SAndroid Build Coastguard Worker
encodeGainmapMetadata(const uhdr_gainmap_metadata_frac * in_metadata,std::vector<uint8_t> & out_data)118*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t uhdr_gainmap_metadata_frac::encodeGainmapMetadata(
119*89a0ef05SAndroid Build Coastguard Worker const uhdr_gainmap_metadata_frac *in_metadata, std::vector<uint8_t> &out_data) {
120*89a0ef05SAndroid Build Coastguard Worker if (in_metadata == nullptr) {
121*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
122*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM;
123*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
124*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
125*89a0ef05SAndroid Build Coastguard Worker "received nullptr for gain map metadata descriptor");
126*89a0ef05SAndroid Build Coastguard Worker return status;
127*89a0ef05SAndroid Build Coastguard Worker }
128*89a0ef05SAndroid Build Coastguard Worker
129*89a0ef05SAndroid Build Coastguard Worker const uint16_t min_version = 0, writer_version = 0;
130*89a0ef05SAndroid Build Coastguard Worker streamWriteU16(out_data, min_version);
131*89a0ef05SAndroid Build Coastguard Worker streamWriteU16(out_data, writer_version);
132*89a0ef05SAndroid Build Coastguard Worker
133*89a0ef05SAndroid Build Coastguard Worker uint8_t flags = 0u;
134*89a0ef05SAndroid Build Coastguard Worker // Always write three channels for now for simplicity.
135*89a0ef05SAndroid Build Coastguard Worker // TODO(maryla): the draft says that this specifies the count of channels of the
136*89a0ef05SAndroid Build Coastguard Worker // gain map. But tone mapping is done in RGB space so there are always three
137*89a0ef05SAndroid Build Coastguard Worker // channels, even if the gain map is grayscale. Should this be revised?
138*89a0ef05SAndroid Build Coastguard Worker const uint8_t channelCount = in_metadata->allChannelsIdentical() ? 1u : 3u;
139*89a0ef05SAndroid Build Coastguard Worker
140*89a0ef05SAndroid Build Coastguard Worker if (channelCount == 3) {
141*89a0ef05SAndroid Build Coastguard Worker flags |= kIsMultiChannelMask;
142*89a0ef05SAndroid Build Coastguard Worker }
143*89a0ef05SAndroid Build Coastguard Worker if (in_metadata->useBaseColorSpace) {
144*89a0ef05SAndroid Build Coastguard Worker flags |= kUseBaseColorSpaceMask;
145*89a0ef05SAndroid Build Coastguard Worker }
146*89a0ef05SAndroid Build Coastguard Worker if (in_metadata->backwardDirection) {
147*89a0ef05SAndroid Build Coastguard Worker flags |= 4;
148*89a0ef05SAndroid Build Coastguard Worker }
149*89a0ef05SAndroid Build Coastguard Worker
150*89a0ef05SAndroid Build Coastguard Worker const uint32_t denom = in_metadata->baseHdrHeadroomD;
151*89a0ef05SAndroid Build Coastguard Worker bool useCommonDenominator = true;
152*89a0ef05SAndroid Build Coastguard Worker if (in_metadata->baseHdrHeadroomD != denom || in_metadata->alternateHdrHeadroomD != denom) {
153*89a0ef05SAndroid Build Coastguard Worker useCommonDenominator = false;
154*89a0ef05SAndroid Build Coastguard Worker }
155*89a0ef05SAndroid Build Coastguard Worker for (int c = 0; c < channelCount; ++c) {
156*89a0ef05SAndroid Build Coastguard Worker if (in_metadata->gainMapMinD[c] != denom || in_metadata->gainMapMaxD[c] != denom ||
157*89a0ef05SAndroid Build Coastguard Worker in_metadata->gainMapGammaD[c] != denom || in_metadata->baseOffsetD[c] != denom ||
158*89a0ef05SAndroid Build Coastguard Worker in_metadata->alternateOffsetD[c] != denom) {
159*89a0ef05SAndroid Build Coastguard Worker useCommonDenominator = false;
160*89a0ef05SAndroid Build Coastguard Worker }
161*89a0ef05SAndroid Build Coastguard Worker }
162*89a0ef05SAndroid Build Coastguard Worker if (useCommonDenominator) {
163*89a0ef05SAndroid Build Coastguard Worker flags |= 8;
164*89a0ef05SAndroid Build Coastguard Worker }
165*89a0ef05SAndroid Build Coastguard Worker streamWriteU8(out_data, flags);
166*89a0ef05SAndroid Build Coastguard Worker
167*89a0ef05SAndroid Build Coastguard Worker if (useCommonDenominator) {
168*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, denom);
169*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->baseHdrHeadroomN);
170*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->alternateHdrHeadroomN);
171*89a0ef05SAndroid Build Coastguard Worker for (int c = 0; c < channelCount; ++c) {
172*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->gainMapMinN[c]);
173*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->gainMapMaxN[c]);
174*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->gainMapGammaN[c]);
175*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->baseOffsetN[c]);
176*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->alternateOffsetN[c]);
177*89a0ef05SAndroid Build Coastguard Worker }
178*89a0ef05SAndroid Build Coastguard Worker } else {
179*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->baseHdrHeadroomN);
180*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->baseHdrHeadroomD);
181*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->alternateHdrHeadroomN);
182*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->alternateHdrHeadroomD);
183*89a0ef05SAndroid Build Coastguard Worker for (int c = 0; c < channelCount; ++c) {
184*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->gainMapMinN[c]);
185*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->gainMapMinD[c]);
186*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->gainMapMaxN[c]);
187*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->gainMapMaxD[c]);
188*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->gainMapGammaN[c]);
189*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->gainMapGammaD[c]);
190*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->baseOffsetN[c]);
191*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->baseOffsetD[c]);
192*89a0ef05SAndroid Build Coastguard Worker streamWriteS32(out_data, in_metadata->alternateOffsetN[c]);
193*89a0ef05SAndroid Build Coastguard Worker streamWriteU32(out_data, in_metadata->alternateOffsetD[c]);
194*89a0ef05SAndroid Build Coastguard Worker }
195*89a0ef05SAndroid Build Coastguard Worker }
196*89a0ef05SAndroid Build Coastguard Worker
197*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
198*89a0ef05SAndroid Build Coastguard Worker }
199*89a0ef05SAndroid Build Coastguard Worker
decodeGainmapMetadata(const std::vector<uint8_t> & in_data,uhdr_gainmap_metadata_frac * out_metadata)200*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t uhdr_gainmap_metadata_frac::decodeGainmapMetadata(
201*89a0ef05SAndroid Build Coastguard Worker const std::vector<uint8_t> &in_data, uhdr_gainmap_metadata_frac *out_metadata) {
202*89a0ef05SAndroid Build Coastguard Worker if (out_metadata == nullptr) {
203*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
204*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM;
205*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
206*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
207*89a0ef05SAndroid Build Coastguard Worker "received nullptr for gain map metadata descriptor");
208*89a0ef05SAndroid Build Coastguard Worker return status;
209*89a0ef05SAndroid Build Coastguard Worker }
210*89a0ef05SAndroid Build Coastguard Worker
211*89a0ef05SAndroid Build Coastguard Worker size_t pos = 0;
212*89a0ef05SAndroid Build Coastguard Worker uint16_t min_version = 0xffff;
213*89a0ef05SAndroid Build Coastguard Worker uint16_t writer_version = 0xffff;
214*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU16(in_data, min_version, pos))
215*89a0ef05SAndroid Build Coastguard Worker if (min_version != 0) {
216*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
217*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
218*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
219*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
220*89a0ef05SAndroid Build Coastguard Worker "received unexpected minimum version %d, expected 0", min_version);
221*89a0ef05SAndroid Build Coastguard Worker return status;
222*89a0ef05SAndroid Build Coastguard Worker }
223*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU16(in_data, writer_version, pos))
224*89a0ef05SAndroid Build Coastguard Worker
225*89a0ef05SAndroid Build Coastguard Worker uint8_t flags = 0xff;
226*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU8(in_data, flags, pos))
227*89a0ef05SAndroid Build Coastguard Worker uint8_t channelCount = ((flags & kIsMultiChannelMask) != 0) * 2 + 1;
228*89a0ef05SAndroid Build Coastguard Worker if (!(channelCount == 1 || channelCount == 3)) {
229*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
230*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
231*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
232*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
233*89a0ef05SAndroid Build Coastguard Worker "received unexpected channel count %d, expects one of {1, 3}", channelCount);
234*89a0ef05SAndroid Build Coastguard Worker return status;
235*89a0ef05SAndroid Build Coastguard Worker }
236*89a0ef05SAndroid Build Coastguard Worker out_metadata->useBaseColorSpace = (flags & kUseBaseColorSpaceMask) != 0;
237*89a0ef05SAndroid Build Coastguard Worker out_metadata->backwardDirection = (flags & 4) != 0;
238*89a0ef05SAndroid Build Coastguard Worker const bool useCommonDenominator = (flags & 8) != 0;
239*89a0ef05SAndroid Build Coastguard Worker
240*89a0ef05SAndroid Build Coastguard Worker if (useCommonDenominator) {
241*89a0ef05SAndroid Build Coastguard Worker uint32_t commonDenominator = 1u;
242*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, commonDenominator, pos))
243*89a0ef05SAndroid Build Coastguard Worker
244*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->baseHdrHeadroomN, pos))
245*89a0ef05SAndroid Build Coastguard Worker out_metadata->baseHdrHeadroomD = commonDenominator;
246*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->alternateHdrHeadroomN, pos))
247*89a0ef05SAndroid Build Coastguard Worker out_metadata->alternateHdrHeadroomD = commonDenominator;
248*89a0ef05SAndroid Build Coastguard Worker
249*89a0ef05SAndroid Build Coastguard Worker for (int c = 0; c < channelCount; ++c) {
250*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->gainMapMinN[c], pos))
251*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMinD[c] = commonDenominator;
252*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->gainMapMaxN[c], pos))
253*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMaxD[c] = commonDenominator;
254*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->gainMapGammaN[c], pos))
255*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapGammaD[c] = commonDenominator;
256*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->baseOffsetN[c], pos))
257*89a0ef05SAndroid Build Coastguard Worker out_metadata->baseOffsetD[c] = commonDenominator;
258*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->alternateOffsetN[c], pos))
259*89a0ef05SAndroid Build Coastguard Worker out_metadata->alternateOffsetD[c] = commonDenominator;
260*89a0ef05SAndroid Build Coastguard Worker }
261*89a0ef05SAndroid Build Coastguard Worker } else {
262*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->baseHdrHeadroomN, pos))
263*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->baseHdrHeadroomD, pos))
264*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->alternateHdrHeadroomN, pos))
265*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->alternateHdrHeadroomD, pos))
266*89a0ef05SAndroid Build Coastguard Worker for (int c = 0; c < channelCount; ++c) {
267*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->gainMapMinN[c], pos))
268*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->gainMapMinD[c], pos))
269*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->gainMapMaxN[c], pos))
270*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->gainMapMaxD[c], pos))
271*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->gainMapGammaN[c], pos))
272*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->gainMapGammaD[c], pos))
273*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->baseOffsetN[c], pos))
274*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->baseOffsetD[c], pos))
275*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadS32(in_data, out_metadata->alternateOffsetN[c], pos))
276*89a0ef05SAndroid Build Coastguard Worker UHDR_ERR_CHECK(streamReadU32(in_data, out_metadata->alternateOffsetD[c], pos))
277*89a0ef05SAndroid Build Coastguard Worker }
278*89a0ef05SAndroid Build Coastguard Worker }
279*89a0ef05SAndroid Build Coastguard Worker
280*89a0ef05SAndroid Build Coastguard Worker // Fill the remaining values by copying those from the first channel.
281*89a0ef05SAndroid Build Coastguard Worker for (int c = channelCount; c < 3; ++c) {
282*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMinN[c] = out_metadata->gainMapMinN[0];
283*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMinD[c] = out_metadata->gainMapMinD[0];
284*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMaxN[c] = out_metadata->gainMapMaxN[0];
285*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapMaxD[c] = out_metadata->gainMapMaxD[0];
286*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapGammaN[c] = out_metadata->gainMapGammaN[0];
287*89a0ef05SAndroid Build Coastguard Worker out_metadata->gainMapGammaD[c] = out_metadata->gainMapGammaD[0];
288*89a0ef05SAndroid Build Coastguard Worker out_metadata->baseOffsetN[c] = out_metadata->baseOffsetN[0];
289*89a0ef05SAndroid Build Coastguard Worker out_metadata->baseOffsetD[c] = out_metadata->baseOffsetD[0];
290*89a0ef05SAndroid Build Coastguard Worker out_metadata->alternateOffsetN[c] = out_metadata->alternateOffsetN[0];
291*89a0ef05SAndroid Build Coastguard Worker out_metadata->alternateOffsetD[c] = out_metadata->alternateOffsetD[0];
292*89a0ef05SAndroid Build Coastguard Worker }
293*89a0ef05SAndroid Build Coastguard Worker
294*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
295*89a0ef05SAndroid Build Coastguard Worker }
296*89a0ef05SAndroid Build Coastguard Worker
297*89a0ef05SAndroid Build Coastguard Worker #define UHDR_CHECK_NON_ZERO(x, message) \
298*89a0ef05SAndroid Build Coastguard Worker if (x == 0) { \
299*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status; \
300*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM; \
301*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1; \
302*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail, "received 0 (bad value) for field %s", message); \
303*89a0ef05SAndroid Build Coastguard Worker return status; \
304*89a0ef05SAndroid Build Coastguard Worker }
305*89a0ef05SAndroid Build Coastguard Worker
gainmapMetadataFractionToFloat(const uhdr_gainmap_metadata_frac * from,uhdr_gainmap_metadata_ext_t * to)306*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t uhdr_gainmap_metadata_frac::gainmapMetadataFractionToFloat(
307*89a0ef05SAndroid Build Coastguard Worker const uhdr_gainmap_metadata_frac *from, uhdr_gainmap_metadata_ext_t *to) {
308*89a0ef05SAndroid Build Coastguard Worker if (from == nullptr || to == nullptr) {
309*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
310*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM;
311*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
312*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
313*89a0ef05SAndroid Build Coastguard Worker "received nullptr for gain map metadata descriptor");
314*89a0ef05SAndroid Build Coastguard Worker return status;
315*89a0ef05SAndroid Build Coastguard Worker }
316*89a0ef05SAndroid Build Coastguard Worker
317*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->baseHdrHeadroomD, "baseHdrHeadroom denominator");
318*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->alternateHdrHeadroomD, "alternateHdrHeadroom denominator");
319*89a0ef05SAndroid Build Coastguard Worker for (int i = 0; i < 3; ++i) {
320*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->gainMapMaxD[i], "gainMapMax denominator");
321*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->gainMapGammaD[i], "gainMapGamma denominator");
322*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->gainMapMinD[i], "gainMapMin denominator");
323*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->baseOffsetD[i], "baseOffset denominator");
324*89a0ef05SAndroid Build Coastguard Worker UHDR_CHECK_NON_ZERO(from->alternateOffsetD[i], "alternateOffset denominator");
325*89a0ef05SAndroid Build Coastguard Worker }
326*89a0ef05SAndroid Build Coastguard Worker
327*89a0ef05SAndroid Build Coastguard Worker // TODO: extend uhdr_gainmap_metadata_ext_t to cover multi-channel
328*89a0ef05SAndroid Build Coastguard Worker if (!from->allChannelsIdentical()) {
329*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
330*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
331*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
332*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
333*89a0ef05SAndroid Build Coastguard Worker "current implementation does not handle images with gainmap metadata different "
334*89a0ef05SAndroid Build Coastguard Worker "across r/g/b channels");
335*89a0ef05SAndroid Build Coastguard Worker return status;
336*89a0ef05SAndroid Build Coastguard Worker }
337*89a0ef05SAndroid Build Coastguard Worker
338*89a0ef05SAndroid Build Coastguard Worker // jpeg supports only 8 bits per component, applying gainmap in inverse direction is unexpected
339*89a0ef05SAndroid Build Coastguard Worker if (from->backwardDirection) {
340*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
341*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
342*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
343*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail, "hdr intent as base rendition is not supported");
344*89a0ef05SAndroid Build Coastguard Worker return status;
345*89a0ef05SAndroid Build Coastguard Worker }
346*89a0ef05SAndroid Build Coastguard Worker
347*89a0ef05SAndroid Build Coastguard Worker // TODO: parse gainmap image icc and use it for color conversion during applygainmap
348*89a0ef05SAndroid Build Coastguard Worker if (!from->useBaseColorSpace) {
349*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
350*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
351*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
352*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
353*89a0ef05SAndroid Build Coastguard Worker "current implementation requires gainmap application space to match base color space");
354*89a0ef05SAndroid Build Coastguard Worker return status;
355*89a0ef05SAndroid Build Coastguard Worker }
356*89a0ef05SAndroid Build Coastguard Worker
357*89a0ef05SAndroid Build Coastguard Worker to->version = kJpegrVersion;
358*89a0ef05SAndroid Build Coastguard Worker to->max_content_boost = exp2((float)from->gainMapMaxN[0] / from->gainMapMaxD[0]);
359*89a0ef05SAndroid Build Coastguard Worker to->min_content_boost = exp2((float)from->gainMapMinN[0] / from->gainMapMinD[0]);
360*89a0ef05SAndroid Build Coastguard Worker
361*89a0ef05SAndroid Build Coastguard Worker to->gamma = (float)from->gainMapGammaN[0] / from->gainMapGammaD[0];
362*89a0ef05SAndroid Build Coastguard Worker
363*89a0ef05SAndroid Build Coastguard Worker // BaseRenditionIsHDR is false
364*89a0ef05SAndroid Build Coastguard Worker to->offset_sdr = (float)from->baseOffsetN[0] / from->baseOffsetD[0];
365*89a0ef05SAndroid Build Coastguard Worker to->offset_hdr = (float)from->alternateOffsetN[0] / from->alternateOffsetD[0];
366*89a0ef05SAndroid Build Coastguard Worker to->hdr_capacity_max = exp2((float)from->alternateHdrHeadroomN / from->alternateHdrHeadroomD);
367*89a0ef05SAndroid Build Coastguard Worker to->hdr_capacity_min = exp2((float)from->baseHdrHeadroomN / from->baseHdrHeadroomD);
368*89a0ef05SAndroid Build Coastguard Worker
369*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
370*89a0ef05SAndroid Build Coastguard Worker }
371*89a0ef05SAndroid Build Coastguard Worker
gainmapMetadataFloatToFraction(const uhdr_gainmap_metadata_ext_t * from,uhdr_gainmap_metadata_frac * to)372*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t uhdr_gainmap_metadata_frac::gainmapMetadataFloatToFraction(
373*89a0ef05SAndroid Build Coastguard Worker const uhdr_gainmap_metadata_ext_t *from, uhdr_gainmap_metadata_frac *to) {
374*89a0ef05SAndroid Build Coastguard Worker if (from == nullptr || to == nullptr) {
375*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
376*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM;
377*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
378*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
379*89a0ef05SAndroid Build Coastguard Worker "received nullptr for gain map metadata descriptor");
380*89a0ef05SAndroid Build Coastguard Worker return status;
381*89a0ef05SAndroid Build Coastguard Worker }
382*89a0ef05SAndroid Build Coastguard Worker
383*89a0ef05SAndroid Build Coastguard Worker to->backwardDirection = false;
384*89a0ef05SAndroid Build Coastguard Worker to->useBaseColorSpace = true;
385*89a0ef05SAndroid Build Coastguard Worker
386*89a0ef05SAndroid Build Coastguard Worker #define CONVERT_FLT_TO_UNSIGNED_FRACTION(flt, numerator, denominator) \
387*89a0ef05SAndroid Build Coastguard Worker if (!floatToUnsignedFraction(flt, numerator, denominator)) { \
388*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status; \
389*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM; \
390*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1; \
391*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail, \
392*89a0ef05SAndroid Build Coastguard Worker "encountered error while representing float %f as a rational number (p/q form) ", \
393*89a0ef05SAndroid Build Coastguard Worker flt); \
394*89a0ef05SAndroid Build Coastguard Worker return status; \
395*89a0ef05SAndroid Build Coastguard Worker }
396*89a0ef05SAndroid Build Coastguard Worker
397*89a0ef05SAndroid Build Coastguard Worker #define CONVERT_FLT_TO_SIGNED_FRACTION(flt, numerator, denominator) \
398*89a0ef05SAndroid Build Coastguard Worker if (!floatToSignedFraction(flt, numerator, denominator)) { \
399*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status; \
400*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM; \
401*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1; \
402*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail, \
403*89a0ef05SAndroid Build Coastguard Worker "encountered error while representing float %f as a rational number (p/q form) ", \
404*89a0ef05SAndroid Build Coastguard Worker flt); \
405*89a0ef05SAndroid Build Coastguard Worker return status; \
406*89a0ef05SAndroid Build Coastguard Worker }
407*89a0ef05SAndroid Build Coastguard Worker
408*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_SIGNED_FRACTION(log2(from->max_content_boost), &to->gainMapMaxN[0],
409*89a0ef05SAndroid Build Coastguard Worker &to->gainMapMaxD[0])
410*89a0ef05SAndroid Build Coastguard Worker to->gainMapMaxN[2] = to->gainMapMaxN[1] = to->gainMapMaxN[0];
411*89a0ef05SAndroid Build Coastguard Worker to->gainMapMaxD[2] = to->gainMapMaxD[1] = to->gainMapMaxD[0];
412*89a0ef05SAndroid Build Coastguard Worker
413*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_SIGNED_FRACTION(log2(from->min_content_boost), &to->gainMapMinN[0],
414*89a0ef05SAndroid Build Coastguard Worker &to->gainMapMinD[0]);
415*89a0ef05SAndroid Build Coastguard Worker to->gainMapMinN[2] = to->gainMapMinN[1] = to->gainMapMinN[0];
416*89a0ef05SAndroid Build Coastguard Worker to->gainMapMinD[2] = to->gainMapMinD[1] = to->gainMapMinD[0];
417*89a0ef05SAndroid Build Coastguard Worker
418*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_UNSIGNED_FRACTION(from->gamma, &to->gainMapGammaN[0], &to->gainMapGammaD[0]);
419*89a0ef05SAndroid Build Coastguard Worker to->gainMapGammaN[2] = to->gainMapGammaN[1] = to->gainMapGammaN[0];
420*89a0ef05SAndroid Build Coastguard Worker to->gainMapGammaD[2] = to->gainMapGammaD[1] = to->gainMapGammaD[0];
421*89a0ef05SAndroid Build Coastguard Worker
422*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_SIGNED_FRACTION(from->offset_sdr, &to->baseOffsetN[0], &to->baseOffsetD[0]);
423*89a0ef05SAndroid Build Coastguard Worker to->baseOffsetN[2] = to->baseOffsetN[1] = to->baseOffsetN[0];
424*89a0ef05SAndroid Build Coastguard Worker to->baseOffsetD[2] = to->baseOffsetD[1] = to->baseOffsetD[0];
425*89a0ef05SAndroid Build Coastguard Worker
426*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_SIGNED_FRACTION(from->offset_hdr, &to->alternateOffsetN[0],
427*89a0ef05SAndroid Build Coastguard Worker &to->alternateOffsetD[0]);
428*89a0ef05SAndroid Build Coastguard Worker to->alternateOffsetN[2] = to->alternateOffsetN[1] = to->alternateOffsetN[0];
429*89a0ef05SAndroid Build Coastguard Worker to->alternateOffsetD[2] = to->alternateOffsetD[1] = to->alternateOffsetD[0];
430*89a0ef05SAndroid Build Coastguard Worker
431*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->hdr_capacity_min), &to->baseHdrHeadroomN,
432*89a0ef05SAndroid Build Coastguard Worker &to->baseHdrHeadroomD);
433*89a0ef05SAndroid Build Coastguard Worker
434*89a0ef05SAndroid Build Coastguard Worker CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->hdr_capacity_max), &to->alternateHdrHeadroomN,
435*89a0ef05SAndroid Build Coastguard Worker &to->alternateHdrHeadroomD);
436*89a0ef05SAndroid Build Coastguard Worker
437*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
438*89a0ef05SAndroid Build Coastguard Worker }
439*89a0ef05SAndroid Build Coastguard Worker
440*89a0ef05SAndroid Build Coastguard Worker } // namespace ultrahdr
441