Coverage Report

Created: 2026-05-30 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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