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