1 /*
2 * Copyright 2024 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 #include "ultrahdr/gainmapmath.h"
18 #include "ultrahdr/gainmapmetadata.h"
19
20 namespace ultrahdr {
21
streamWriteU8(std::vector<uint8_t> & data,uint8_t value)22 status_t streamWriteU8(std::vector<uint8_t> &data, uint8_t value) {
23 data.push_back(value);
24 return JPEGR_NO_ERROR;
25 }
26
streamWriteU32(std::vector<uint8_t> & data,uint32_t value)27 status_t streamWriteU32(std::vector<uint8_t> &data, uint32_t value) {
28 data.push_back((value >> 24) & 0xff);
29 data.push_back((value >> 16) & 0xff);
30 data.push_back((value >> 8) & 0xff);
31 data.push_back(value & 0xff);
32 return JPEGR_NO_ERROR;
33 }
34
streamReadU8(const std::vector<uint8_t> & data,uint8_t & value,size_t & pos)35 status_t streamReadU8(const std::vector<uint8_t> &data, uint8_t &value, size_t &pos) {
36 if (pos >= data.size()) {
37 return ERROR_JPEGR_METADATA_ERROR;
38 }
39 value = data[pos++];
40 return JPEGR_NO_ERROR;
41 }
42
streamReadU32(const std::vector<uint8_t> & data,uint32_t & value,size_t & pos)43 status_t streamReadU32(const std::vector<uint8_t> &data, uint32_t &value, size_t &pos) {
44 if (pos + 3 >= data.size()) {
45 return ERROR_JPEGR_METADATA_ERROR;
46 }
47 value = (data[pos] << 24 | data[pos + 1] << 16 | data[pos + 2] << 8 | data[pos + 3]);
48 pos += 4;
49 return JPEGR_NO_ERROR;
50 }
51
encodeGainmapMetadata(const gain_map_metadata * metadata,std::vector<uint8_t> & out_data)52 status_t gain_map_metadata::encodeGainmapMetadata(const gain_map_metadata *metadata,
53 std::vector<uint8_t> &out_data) {
54 if (metadata == nullptr) {
55 return ERROR_JPEGR_METADATA_ERROR;
56 }
57
58 const uint8_t version = 0;
59 streamWriteU8(out_data, version);
60
61 uint8_t flags = 0u;
62 // Always write three channels for now for simplicity.
63 // TODO(maryla): the draft says that this specifies the count of channels of the
64 // gain map. But tone mapping is done in RGB space so there are always three
65 // channels, even if the gain map is grayscale. Should this be revised?
66 const bool allChannelsIdentical =
67 metadata->gainMapMinN[0] == metadata->gainMapMinN[1] &&
68 metadata->gainMapMinN[0] == metadata->gainMapMinN[2] &&
69 metadata->gainMapMinD[0] == metadata->gainMapMinD[1] &&
70 metadata->gainMapMinD[0] == metadata->gainMapMinD[2] &&
71 metadata->gainMapMaxN[0] == metadata->gainMapMaxN[1] &&
72 metadata->gainMapMaxN[0] == metadata->gainMapMaxN[2] &&
73 metadata->gainMapMaxD[0] == metadata->gainMapMaxD[1] &&
74 metadata->gainMapMaxD[0] == metadata->gainMapMaxD[2] &&
75 metadata->gainMapGammaN[0] == metadata->gainMapGammaN[1] &&
76 metadata->gainMapGammaN[0] == metadata->gainMapGammaN[2] &&
77 metadata->gainMapGammaD[0] == metadata->gainMapGammaD[1] &&
78 metadata->gainMapGammaD[0] == metadata->gainMapGammaD[2] &&
79 metadata->baseOffsetN[0] == metadata->baseOffsetN[1] &&
80 metadata->baseOffsetN[0] == metadata->baseOffsetN[2] &&
81 metadata->baseOffsetD[0] == metadata->baseOffsetD[1] &&
82 metadata->baseOffsetD[0] == metadata->baseOffsetD[2] &&
83 metadata->alternateOffsetN[0] == metadata->alternateOffsetN[1] &&
84 metadata->alternateOffsetN[0] == metadata->alternateOffsetN[2] &&
85 metadata->alternateOffsetD[0] == metadata->alternateOffsetD[1] &&
86 metadata->alternateOffsetD[0] == metadata->alternateOffsetD[2];
87 const uint8_t channelCount = allChannelsIdentical ? 1u : 3u;
88
89 if (channelCount == 3) {
90 flags |= 1;
91 }
92 if (metadata->useBaseColorSpace) {
93 flags |= 2;
94 }
95 if (metadata->backwardDirection) {
96 flags |= 4;
97 }
98
99 const uint32_t denom = metadata->baseHdrHeadroomD;
100 bool useCommonDenominator = true;
101 if (metadata->baseHdrHeadroomD != denom || metadata->alternateHdrHeadroomD != denom) {
102 useCommonDenominator = false;
103 }
104 for (int c = 0; c < channelCount; ++c) {
105 if (metadata->gainMapMinD[c] != denom || metadata->gainMapMaxD[c] != denom ||
106 metadata->gainMapGammaD[c] != denom || metadata->baseOffsetD[c] != denom ||
107 metadata->alternateOffsetD[c] != denom) {
108 useCommonDenominator = false;
109 }
110 }
111 if (useCommonDenominator) {
112 flags |= 8;
113 }
114 streamWriteU8(out_data, flags);
115
116 if (useCommonDenominator) {
117 streamWriteU32(out_data, denom);
118 streamWriteU32(out_data, metadata->baseHdrHeadroomN);
119 streamWriteU32(out_data, metadata->alternateHdrHeadroomN);
120 for (int c = 0; c < channelCount; ++c) {
121 streamWriteU32(out_data, (uint32_t)metadata->gainMapMinN[c]);
122 streamWriteU32(out_data, (uint32_t)metadata->gainMapMaxN[c]);
123 streamWriteU32(out_data, metadata->gainMapGammaN[c]);
124 streamWriteU32(out_data, (uint32_t)metadata->baseOffsetN[c]);
125 streamWriteU32(out_data, (uint32_t)metadata->alternateOffsetN[c]);
126 }
127 } else {
128 streamWriteU32(out_data, metadata->baseHdrHeadroomN);
129 streamWriteU32(out_data, metadata->baseHdrHeadroomD);
130 streamWriteU32(out_data, metadata->alternateHdrHeadroomN);
131 streamWriteU32(out_data, metadata->alternateHdrHeadroomD);
132 for (int c = 0; c < channelCount; ++c) {
133 streamWriteU32(out_data, (uint32_t)metadata->gainMapMinN[c]);
134 streamWriteU32(out_data, metadata->gainMapMinD[c]);
135 streamWriteU32(out_data, (uint32_t)metadata->gainMapMaxN[c]);
136 streamWriteU32(out_data, metadata->gainMapMaxD[c]);
137 streamWriteU32(out_data, metadata->gainMapGammaN[c]);
138 streamWriteU32(out_data, metadata->gainMapGammaD[c]);
139 streamWriteU32(out_data, (uint32_t)metadata->baseOffsetN[c]);
140 streamWriteU32(out_data, metadata->baseOffsetD[c]);
141 streamWriteU32(out_data, (uint32_t)metadata->alternateOffsetN[c]);
142 streamWriteU32(out_data, metadata->alternateOffsetD[c]);
143 }
144 }
145
146 return JPEGR_NO_ERROR;
147 }
148
decodeGainmapMetadata(const std::vector<uint8_t> & data,gain_map_metadata * out_metadata)149 status_t gain_map_metadata::decodeGainmapMetadata(const std::vector<uint8_t> &data,
150 gain_map_metadata *out_metadata) {
151 if (out_metadata == nullptr) {
152 return ERROR_JPEGR_BAD_PTR;
153 }
154
155 size_t pos = 0;
156 uint8_t version = 0xff;
157 JPEGR_CHECK(streamReadU8(data, version, pos))
158
159 if (version != 0) {
160 return ERROR_JPEGR_UNSUPPORTED_FEATURE;
161 }
162
163 uint8_t flags = 0xff;
164 JPEGR_CHECK(streamReadU8(data, flags, pos))
165
166 uint8_t channelCount = (flags & 1) * 2 + 1;
167
168 if (!(channelCount == 1 || channelCount == 3)) {
169 return ERROR_JPEGR_UNSUPPORTED_FEATURE;
170 }
171 out_metadata->useBaseColorSpace = (flags & 2) != 0;
172 out_metadata->backwardDirection = (flags & 4) != 0;
173 const bool useCommonDenominator = (flags & 8) != 0;
174
175 if (useCommonDenominator) {
176 uint32_t commonDenominator;
177 JPEGR_CHECK(streamReadU32(data, commonDenominator, pos))
178
179 JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomN, pos))
180 out_metadata->baseHdrHeadroomD = commonDenominator;
181 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomN, pos))
182 out_metadata->alternateHdrHeadroomD = commonDenominator;
183
184 for (int c = 0; c < channelCount; ++c) {
185 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinN[c], pos))
186 out_metadata->gainMapMinD[c] = commonDenominator;
187 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxN[c], pos))
188 out_metadata->gainMapMaxD[c] = commonDenominator;
189 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaN[c], pos))
190 out_metadata->gainMapGammaD[c] = commonDenominator;
191 JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetN[c], pos))
192 out_metadata->baseOffsetD[c] = commonDenominator;
193 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetN[c], pos))
194 out_metadata->alternateOffsetD[c] = commonDenominator;
195 }
196 } else {
197 JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomN, pos))
198 JPEGR_CHECK(streamReadU32(data, out_metadata->baseHdrHeadroomD, pos))
199 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomN, pos))
200 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateHdrHeadroomD, pos))
201 for (int c = 0; c < channelCount; ++c) {
202 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinN[c], pos))
203 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMinD[c], pos))
204 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxN[c], pos))
205 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapMaxD[c], pos))
206 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaN[c], pos))
207 JPEGR_CHECK(streamReadU32(data, out_metadata->gainMapGammaD[c], pos))
208 JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetN[c], pos))
209 JPEGR_CHECK(streamReadU32(data, out_metadata->baseOffsetD[c], pos))
210 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetN[c], pos))
211 JPEGR_CHECK(streamReadU32(data, out_metadata->alternateOffsetD[c], pos))
212 }
213 }
214
215 // Fill the remaining values by copying those from the first channel.
216 for (int c = channelCount; c < 3; ++c) {
217 out_metadata->gainMapMinN[c] = out_metadata->gainMapMinN[0];
218 out_metadata->gainMapMinD[c] = out_metadata->gainMapMinD[0];
219 out_metadata->gainMapMaxN[c] = out_metadata->gainMapMaxN[0];
220 out_metadata->gainMapMaxD[c] = out_metadata->gainMapMaxD[0];
221 out_metadata->gainMapGammaN[c] = out_metadata->gainMapGammaN[0];
222 out_metadata->gainMapGammaD[c] = out_metadata->gainMapGammaD[0];
223 out_metadata->baseOffsetN[c] = out_metadata->baseOffsetN[0];
224 out_metadata->baseOffsetD[c] = out_metadata->baseOffsetD[0];
225 out_metadata->alternateOffsetN[c] = out_metadata->alternateOffsetN[0];
226 out_metadata->alternateOffsetD[c] = out_metadata->alternateOffsetD[0];
227 }
228
229 return JPEGR_NO_ERROR;
230 }
231
232 #define CHECK_NOT_ZERO(x) \
233 do { \
234 if (x == 0) { \
235 return ERROR_JPEGR_METADATA_ERROR; \
236 } \
237 } while (0)
238
gainmapMetadataFractionToFloat(const gain_map_metadata * from,ultrahdr_metadata_ptr to)239 status_t gain_map_metadata::gainmapMetadataFractionToFloat(const gain_map_metadata *from,
240 ultrahdr_metadata_ptr to) {
241 if (from == nullptr || to == nullptr) {
242 return ERROR_JPEGR_BAD_PTR;
243 }
244
245 CHECK_NOT_ZERO(from->baseHdrHeadroomD);
246 CHECK_NOT_ZERO(from->alternateHdrHeadroomD);
247 for (int i = 0; i < 3; ++i) {
248 CHECK_NOT_ZERO(from->gainMapMaxD[i]);
249 CHECK_NOT_ZERO(from->gainMapGammaD[i]);
250 CHECK_NOT_ZERO(from->gainMapMinD[i]);
251 CHECK_NOT_ZERO(from->baseOffsetD[i]);
252 CHECK_NOT_ZERO(from->alternateOffsetD[i]);
253 }
254 to->version = kGainMapVersion;
255 to->maxContentBoost = (float)from->gainMapMaxN[0] / from->gainMapMaxD[0];
256 to->minContentBoost = (float)from->gainMapMinN[0] / from->gainMapMinD[0];
257 to->gamma = (float)from->gainMapGammaN[0] / from->gainMapGammaD[0];
258
259 // BaseRenditionIsHDR is false
260 to->offsetSdr = (float)from->baseOffsetN[0] / from->baseOffsetD[0];
261 to->offsetHdr = (float)from->alternateOffsetN[0] / from->alternateOffsetD[0];
262 to->hdrCapacityMax = (float)from->alternateHdrHeadroomN / from->alternateHdrHeadroomD;
263 to->hdrCapacityMin = (float)from->baseHdrHeadroomN / from->baseHdrHeadroomD;
264
265 return JPEGR_NO_ERROR;
266 }
267
gainmapMetadataFloatToFraction(const ultrahdr_metadata_ptr from,gain_map_metadata * to)268 status_t gain_map_metadata::gainmapMetadataFloatToFraction(const ultrahdr_metadata_ptr from,
269 gain_map_metadata *to) {
270 if (from == nullptr || to == nullptr) {
271 return ERROR_JPEGR_BAD_PTR;
272 }
273
274 to->backwardDirection = false;
275 to->useBaseColorSpace = true;
276
277 floatToUnsignedFraction(from->maxContentBoost, &to->gainMapMaxN[0], &to->gainMapMaxD[0]);
278 to->gainMapMaxN[2] = to->gainMapMaxN[1] = to->gainMapMaxN[0];
279 to->gainMapMaxD[2] = to->gainMapMaxD[1] = to->gainMapMaxD[0];
280
281 floatToUnsignedFraction(from->minContentBoost, &to->gainMapMinN[0], &to->gainMapMinD[0]);
282 to->gainMapMinN[2] = to->gainMapMinN[1] = to->gainMapMinN[0];
283 to->gainMapMinD[2] = to->gainMapMinD[1] = to->gainMapMinD[0];
284
285 floatToUnsignedFraction(from->gamma, &to->gainMapGammaN[0], &to->gainMapGammaD[0]);
286 to->gainMapGammaN[2] = to->gainMapGammaN[1] = to->gainMapGammaN[0];
287 to->gainMapGammaD[2] = to->gainMapGammaD[1] = to->gainMapGammaD[0];
288
289 floatToUnsignedFraction(from->offsetSdr, &to->baseOffsetN[0], &to->baseOffsetD[0]);
290 to->baseOffsetN[2] = to->baseOffsetN[1] = to->baseOffsetN[0];
291 to->baseOffsetD[2] = to->baseOffsetD[1] = to->baseOffsetD[0];
292
293 floatToUnsignedFraction(from->offsetHdr, &to->alternateOffsetN[0], &to->alternateOffsetD[0]);
294 to->alternateOffsetN[2] = to->alternateOffsetN[1] = to->alternateOffsetN[0];
295 to->alternateOffsetD[2] = to->alternateOffsetD[1] = to->alternateOffsetD[0];
296
297 floatToUnsignedFraction(from->hdrCapacityMin, &to->baseHdrHeadroomN, &to->baseHdrHeadroomD);
298
299 floatToUnsignedFraction(from->hdrCapacityMax, &to->alternateHdrHeadroomN,
300 &to->alternateHdrHeadroomD);
301
302 return JPEGR_NO_ERROR;
303 }
304
305 } // namespace ultrahdr
306