Coverage Report

Created: 2025-12-05 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libultrahdr/lib/src/jpegrutils.cpp
Line
Count
Source
1
/*
2
 * Copyright 2022 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 <algorithm>
18
#include <cmath>
19
20
#include "ultrahdr/ultrahdrcommon.h"
21
#include "ultrahdr/jpegr.h"
22
#include "ultrahdr/jpegrutils.h"
23
24
#include "image_io/xml/xml_reader.h"
25
#include "image_io/xml/xml_writer.h"
26
#include "image_io/base/message_handler.h"
27
#include "image_io/xml/xml_element_rules.h"
28
#include "image_io/xml/xml_handler.h"
29
#include "image_io/xml/xml_rule.h"
30
31
using namespace photos_editing_formats::image_io;
32
using namespace std;
33
34
namespace ultrahdr {
35
/*
36
 * Helper function used for generating XMP metadata.
37
 *
38
 * @param prefix The prefix part of the name.
39
 * @param suffix The suffix part of the name.
40
 * @return A name of the form "prefix:suffix".
41
 */
42
28
static inline string Name(const string& prefix, const string& suffix) {
43
28
  std::stringstream ss;
44
28
  ss << prefix << ":" << suffix;
45
28
  return ss.str();
46
28
}
47
48
0
DataStruct::DataStruct(size_t s) {
49
0
  data = malloc(s);
50
0
  length = s;
51
0
  memset(data, 0, s);
52
0
  writePos = 0;
53
0
}
54
55
0
DataStruct::~DataStruct() {
56
0
  if (data != nullptr) {
57
0
    free(data);
58
0
  }
59
0
}
60
61
0
void* DataStruct::getData() { return data; }
62
63
0
size_t DataStruct::getLength() { return length; }
64
65
0
size_t DataStruct::getBytesWritten() { return writePos; }
66
67
0
bool DataStruct::write8(uint8_t value) {
68
0
  uint8_t v = value;
69
0
  return write(&v, 1);
70
0
}
71
72
0
bool DataStruct::write16(uint16_t value) {
73
0
  uint16_t v = value;
74
0
  return write(&v, 2);
75
0
}
76
77
0
bool DataStruct::write32(uint32_t value) {
78
0
  uint32_t v = value;
79
0
  return write(&v, 4);
80
0
}
81
82
0
bool DataStruct::write(const void* src, size_t size) {
83
0
  if (writePos + size > length) {
84
0
    ALOGE("Writing out of boundary: write position: %zd, size: %zd, capacity: %zd", writePos, size,
85
0
          length);
86
0
    return false;
87
0
  }
88
0
  memcpy((uint8_t*)data + writePos, src, size);
89
0
  writePos += size;
90
0
  return true;
91
0
}
92
93
/*
94
 * Helper function used for writing data to destination.
95
 */
96
uhdr_error_info_t Write(uhdr_compressed_image_t* destination, const void* source, size_t length,
97
0
                        size_t& position) {
98
0
  if (position + length > destination->capacity) {
99
0
    uhdr_error_info_t status;
100
0
    status.error_code = UHDR_CODEC_MEM_ERROR;
101
0
    status.has_detail = 1;
102
0
    snprintf(status.detail, sizeof status.detail,
103
0
             "output buffer to store compressed data is too small: write position: %zd, size: %zd, "
104
0
             "capacity: %zd",
105
0
             position, length, destination->capacity);
106
0
    return status;
107
0
  }
108
109
0
  memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
110
0
  position += length;
111
0
  return g_no_error;
112
0
}
113
114
// Extremely simple XML Handler - just searches for interesting elements
115
class XMPXmlHandler : public XmlHandler {
116
 public:
117
2.77k
  XMPXmlHandler() : XmlHandler() {
118
2.77k
    state = NotStrarted;
119
2.77k
    versionFound = false;
120
2.77k
    minContentBoostFound = false;
121
2.77k
    maxContentBoostFound = false;
122
2.77k
    gammaFound = false;
123
2.77k
    offsetSdrFound = false;
124
2.77k
    offsetHdrFound = false;
125
2.77k
    hdrCapacityMinFound = false;
126
2.77k
    hdrCapacityMaxFound = false;
127
2.77k
    baseRenditionIsHdrFound = false;
128
2.77k
  }
129
130
  enum ParseState { NotStrarted, Started, Done };
131
132
44.9k
  virtual DataMatchResult StartElement(const XmlTokenContext& context) {
133
44.9k
    string val;
134
44.9k
    if (context.BuildTokenValue(&val)) {
135
44.9k
      if (!val.compare(containerName)) {
136
5.12k
        state = Started;
137
39.7k
      } else {
138
39.7k
        if (state != Done) {
139
34.9k
          state = NotStrarted;
140
34.9k
        }
141
39.7k
      }
142
44.9k
    }
143
44.9k
    return context.GetResult();
144
44.9k
  }
145
146
8.93k
  virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
147
8.93k
    if (state == Started) {
148
1.79k
      state = Done;
149
1.79k
      lastAttributeName = "";
150
1.79k
    }
151
8.93k
    return context.GetResult();
152
8.93k
  }
153
154
44.9k
  virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
155
44.9k
    string val;
156
44.9k
    if (state == Started) {
157
43.6k
      if (context.BuildTokenValue(&val)) {
158
43.6k
        if (!val.compare(versionAttrName)) {
159
442
          lastAttributeName = versionAttrName;
160
43.2k
        } else if (!val.compare(maxContentBoostAttrName)) {
161
2.91k
          lastAttributeName = maxContentBoostAttrName;
162
40.3k
        } else if (!val.compare(minContentBoostAttrName)) {
163
144
          lastAttributeName = minContentBoostAttrName;
164
40.1k
        } else if (!val.compare(gammaAttrName)) {
165
222
          lastAttributeName = gammaAttrName;
166
39.9k
        } else if (!val.compare(offsetSdrAttrName)) {
167
570
          lastAttributeName = offsetSdrAttrName;
168
39.3k
        } else if (!val.compare(offsetHdrAttrName)) {
169
34
          lastAttributeName = offsetHdrAttrName;
170
39.3k
        } else if (!val.compare(hdrCapacityMinAttrName)) {
171
330
          lastAttributeName = hdrCapacityMinAttrName;
172
39.0k
        } else if (!val.compare(hdrCapacityMaxAttrName)) {
173
42
          lastAttributeName = hdrCapacityMaxAttrName;
174
38.9k
        } else if (!val.compare(baseRenditionIsHdrAttrName)) {
175
458
          lastAttributeName = baseRenditionIsHdrAttrName;
176
38.5k
        } else {
177
38.5k
          lastAttributeName = "";
178
38.5k
        }
179
43.6k
      }
180
43.6k
    }
181
44.9k
    return context.GetResult();
182
44.9k
  }
183
184
44.2k
  virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
185
44.2k
    string val;
186
44.2k
    if (state == Started) {
187
43.1k
      if (context.BuildTokenValue(&val, true)) {
188
43.1k
        if (!lastAttributeName.compare(versionAttrName)) {
189
438
          versionStr = val;
190
438
          versionFound = true;
191
42.7k
        } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
192
2.90k
          maxContentBoostStr = val;
193
2.90k
          maxContentBoostFound = true;
194
39.7k
        } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
195
142
          minContentBoostStr = val;
196
142
          minContentBoostFound = true;
197
39.6k
        } else if (!lastAttributeName.compare(gammaAttrName)) {
198
220
          gammaStr = val;
199
220
          gammaFound = true;
200
39.4k
        } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
201
566
          offsetSdrStr = val;
202
566
          offsetSdrFound = true;
203
38.8k
        } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
204
32
          offsetHdrStr = val;
205
32
          offsetHdrFound = true;
206
38.8k
        } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
207
328
          hdrCapacityMinStr = val;
208
328
          hdrCapacityMinFound = true;
209
38.5k
        } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
210
40
          hdrCapacityMaxStr = val;
211
40
          hdrCapacityMaxFound = true;
212
38.4k
        } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
213
456
          baseRenditionIsHdrStr = val;
214
456
          baseRenditionIsHdrFound = true;
215
456
        }
216
43.1k
      }
217
43.1k
    }
218
44.2k
    return context.GetResult();
219
44.2k
  }
220
221
36
  bool getVersion(string* version, bool* present) {
222
36
    if (state == Done) {
223
8
      *version = versionStr;
224
8
      *present = versionFound;
225
8
      return true;
226
28
    } else {
227
28
      return false;
228
28
    }
229
36
  }
230
231
6
  bool getMaxContentBoost(float* max_content_boost, bool* present) {
232
6
    if (state == Done) {
233
6
      *present = maxContentBoostFound;
234
6
      stringstream ss(maxContentBoostStr);
235
6
      float val;
236
6
      if (ss >> val) {
237
4
        *max_content_boost = exp2(val);
238
4
        return true;
239
4
      } else {
240
2
        return false;
241
2
      }
242
6
    } else {
243
0
      return false;
244
0
    }
245
6
  }
246
247
0
  bool getMinContentBoost(float* min_content_boost, bool* present) {
248
0
    if (state == Done) {
249
0
      *present = minContentBoostFound;
250
0
      stringstream ss(minContentBoostStr);
251
0
      float val;
252
0
      if (ss >> val) {
253
0
        *min_content_boost = exp2(val);
254
0
        return true;
255
0
      } else {
256
0
        return false;
257
0
      }
258
0
    } else {
259
0
      return false;
260
0
    }
261
0
  }
262
263
0
  bool getGamma(float* gamma, bool* present) {
264
0
    if (state == Done) {
265
0
      *present = gammaFound;
266
0
      stringstream ss(gammaStr);
267
0
      float val;
268
0
      if (ss >> val) {
269
0
        *gamma = val;
270
0
        return true;
271
0
      } else {
272
0
        return false;
273
0
      }
274
0
    } else {
275
0
      return false;
276
0
    }
277
0
  }
278
279
0
  bool getOffsetSdr(float* offset_sdr, bool* present) {
280
0
    if (state == Done) {
281
0
      *present = offsetSdrFound;
282
0
      stringstream ss(offsetSdrStr);
283
0
      float val;
284
0
      if (ss >> val) {
285
0
        *offset_sdr = val;
286
0
        return true;
287
0
      } else {
288
0
        return false;
289
0
      }
290
0
    } else {
291
0
      return false;
292
0
    }
293
0
  }
294
295
0
  bool getOffsetHdr(float* offset_hdr, bool* present) {
296
0
    if (state == Done) {
297
0
      *present = offsetHdrFound;
298
0
      stringstream ss(offsetHdrStr);
299
0
      float val;
300
0
      if (ss >> val) {
301
0
        *offset_hdr = val;
302
0
        return true;
303
0
      } else {
304
0
        return false;
305
0
      }
306
0
    } else {
307
0
      return false;
308
0
    }
309
0
  }
310
311
0
  bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
312
0
    if (state == Done) {
313
0
      *present = hdrCapacityMinFound;
314
0
      stringstream ss(hdrCapacityMinStr);
315
0
      float val;
316
0
      if (ss >> val) {
317
0
        *hdr_capacity_min = exp2(val);
318
0
        return true;
319
0
      } else {
320
0
        return false;
321
0
      }
322
0
    } else {
323
0
      return false;
324
0
    }
325
0
  }
326
327
4
  bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
328
4
    if (state == Done) {
329
4
      *present = hdrCapacityMaxFound;
330
4
      stringstream ss(hdrCapacityMaxStr);
331
4
      float val;
332
4
      if (ss >> val) {
333
0
        *hdr_capacity_max = exp2(val);
334
0
        return true;
335
4
      } else {
336
4
        return false;
337
4
      }
338
4
    } else {
339
0
      return false;
340
0
    }
341
4
  }
342
343
0
  bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
344
0
    if (state == Done) {
345
0
      *present = baseRenditionIsHdrFound;
346
0
      if (!baseRenditionIsHdrStr.compare("False")) {
347
0
        *base_rendition_is_hdr = false;
348
0
        return true;
349
0
      } else if (!baseRenditionIsHdrStr.compare("True")) {
350
0
        *base_rendition_is_hdr = true;
351
0
        return true;
352
0
      } else {
353
0
        return false;
354
0
      }
355
0
    } else {
356
0
      return false;
357
0
    }
358
0
  }
359
360
 private:
361
  static const string containerName;
362
363
  static const string versionAttrName;
364
  string versionStr;
365
  bool versionFound;
366
  static const string maxContentBoostAttrName;
367
  string maxContentBoostStr;
368
  bool maxContentBoostFound;
369
  static const string minContentBoostAttrName;
370
  string minContentBoostStr;
371
  bool minContentBoostFound;
372
  static const string gammaAttrName;
373
  string gammaStr;
374
  bool gammaFound;
375
  static const string offsetSdrAttrName;
376
  string offsetSdrStr;
377
  bool offsetSdrFound;
378
  static const string offsetHdrAttrName;
379
  string offsetHdrStr;
380
  bool offsetHdrFound;
381
  static const string hdrCapacityMinAttrName;
382
  string hdrCapacityMinStr;
383
  bool hdrCapacityMinFound;
384
  static const string hdrCapacityMaxAttrName;
385
  string hdrCapacityMaxStr;
386
  bool hdrCapacityMaxFound;
387
  static const string baseRenditionIsHdrAttrName;
388
  string baseRenditionIsHdrStr;
389
  bool baseRenditionIsHdrFound;
390
391
  string lastAttributeName;
392
  ParseState state;
393
};
394
395
// GContainer XMP constants - URI and namespace prefix
396
const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
397
const string kContainerPrefix = "Container";
398
399
// GContainer XMP constants - element and attribute names
400
const string kConDirectory = Name(kContainerPrefix, "Directory");
401
const string kConItem = Name(kContainerPrefix, "Item");
402
403
// GContainer XMP constants - names for XMP handlers
404
const string XMPXmlHandler::containerName = "rdf:Description";
405
// Item XMP constants - URI and namespace prefix
406
const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
407
const string kItemPrefix = "Item";
408
409
// Item XMP constants - element and attribute names
410
const string kItemLength = Name(kItemPrefix, "Length");
411
const string kItemMime = Name(kItemPrefix, "Mime");
412
const string kItemSemantic = Name(kItemPrefix, "Semantic");
413
414
// Item XMP constants - element and attribute values
415
const string kSemanticPrimary = "Primary";
416
const string kSemanticGainMap = "GainMap";
417
const string kMimeImageJpeg = "image/jpeg";
418
419
// GainMap XMP constants - URI and namespace prefix
420
const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
421
const string kGainMapPrefix = "hdrgm";
422
423
// GainMap XMP constants - element and attribute names
424
const string kMapVersion = Name(kGainMapPrefix, "Version");
425
const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
426
const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
427
const string kMapGamma = Name(kGainMapPrefix, "Gamma");
428
const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
429
const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
430
const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
431
const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
432
const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
433
434
// GainMap XMP constants - names for XMP handlers
435
const string XMPXmlHandler::versionAttrName = kMapVersion;
436
const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
437
const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
438
const string XMPXmlHandler::gammaAttrName = kMapGamma;
439
const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
440
const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
441
const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
442
const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
443
const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
444
445
uhdr_error_info_t getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size,
446
2.77k
                                     uhdr_gainmap_metadata_ext_t* metadata) {
447
2.77k
  string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
448
449
2.77k
  if (xmp_size < nameSpace.size() + 2) {
450
0
    uhdr_error_info_t status;
451
0
    status.error_code = UHDR_CODEC_ERROR;
452
0
    status.has_detail = 1;
453
0
    snprintf(status.detail, sizeof status.detail,
454
0
             "size of xmp block is expected to be atleast %zd bytes, received only %zd bytes",
455
0
             nameSpace.size() + 2, xmp_size);
456
0
    return status;
457
0
  }
458
459
2.77k
  if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
460
0
    uhdr_error_info_t status;
461
0
    status.error_code = UHDR_CODEC_ERROR;
462
0
    status.has_detail = 1;
463
0
    snprintf(status.detail, sizeof status.detail,
464
0
             "mismatch in namespace of xmp block. Expected %s, Got %.*s", nameSpace.c_str(),
465
0
             (int)nameSpace.size(), reinterpret_cast<char*>(xmp_data));
466
0
    return status;
467
0
  }
468
469
  // Position the pointers to the start of XMP XML portion
470
2.77k
  xmp_data += nameSpace.size() + 1;
471
2.77k
  xmp_size -= nameSpace.size() + 1;
472
2.77k
  XMPXmlHandler handler;
473
474
  // xml parser fails to parse packet header, wrapper. remove them before handing the data to
475
  // parser. if there is no packet header, do nothing otherwise go to the position of '<' without
476
  // '?' after it.
477
2.77k
  size_t offset = 0;
478
451k
  for (size_t i = 0; i < xmp_size - 1; ++i) {
479
451k
    if (xmp_data[i] == '<') {
480
2.76k
      if (xmp_data[i + 1] != '?') {
481
2.68k
        offset = i;
482
2.68k
        break;
483
2.68k
      }
484
2.76k
    }
485
451k
  }
486
2.77k
  xmp_data += offset;
487
2.77k
  xmp_size -= offset;
488
489
  // If there is no packet wrapper, do nothing other wise go to the position of last '>' without '?'
490
  // before it.
491
2.77k
  offset = 0;
492
390k
  for (size_t i = xmp_size - 1; i >= 1; --i) {
493
390k
    if (xmp_data[i] == '>') {
494
2.73k
      if (xmp_data[i - 1] != '?') {
495
2.67k
        offset = xmp_size - (i + 1);
496
2.67k
        break;
497
2.67k
      }
498
2.73k
    }
499
390k
  }
500
2.77k
  xmp_size -= offset;
501
502
  // remove padding
503
268k
  while (xmp_data[xmp_size - 1] != '>' && xmp_size > 1) {
504
265k
    xmp_size--;
505
265k
  }
506
507
2.77k
  string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
508
2.77k
  MessageHandler msg_handler;
509
2.77k
  unique_ptr<XmlRule> rule(new XmlElementRule);
510
2.77k
  XmlReader reader(&handler, &msg_handler);
511
2.77k
  reader.StartParse(std::move(rule));
512
2.77k
  reader.Parse(str);
513
2.77k
  reader.FinishParse();
514
2.77k
  if (reader.HasErrors()) {
515
2.73k
    uhdr_error_info_t status;
516
2.73k
    status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
517
2.73k
    status.has_detail = 1;
518
2.73k
    snprintf(status.detail, sizeof status.detail, "xml parser returned with error");
519
2.73k
    return status;
520
2.73k
  }
521
522
  // Apply default values to any not-present fields, except for Version,
523
  // maxContentBoost, and hdrCapacityMax, which are required. Return false if
524
  // we encounter a present field that couldn't be parsed, since this
525
  // indicates it is invalid (eg. string where there should be a float).
526
36
  bool present = false;
527
36
  if (!handler.getVersion(&metadata->version, &present) || !present) {
528
30
    uhdr_error_info_t status;
529
30
    status.error_code = UHDR_CODEC_ERROR;
530
30
    status.has_detail = 1;
531
30
    snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s",
532
30
             kMapVersion.c_str());
533
30
    return status;
534
30
  }
535
6
  if (!handler.getMaxContentBoost(&metadata->max_content_boost[0], &present) || !present) {
536
2
    uhdr_error_info_t status;
537
2
    status.error_code = UHDR_CODEC_ERROR;
538
2
    status.has_detail = 1;
539
2
    snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s",
540
2
             kMapGainMapMax.c_str());
541
2
    return status;
542
2
  }
543
4
  if (!handler.getHdrCapacityMax(&metadata->hdr_capacity_max, &present) || !present) {
544
4
    uhdr_error_info_t status;
545
4
    status.error_code = UHDR_CODEC_ERROR;
546
4
    status.has_detail = 1;
547
4
    snprintf(status.detail, sizeof status.detail, "xml parse error, could not find attribute %s",
548
4
             kMapHDRCapacityMax.c_str());
549
4
    return status;
550
4
  }
551
0
  if (!handler.getMinContentBoost(&metadata->min_content_boost[0], &present)) {
552
0
    if (present) {
553
0
      uhdr_error_info_t status;
554
0
      status.error_code = UHDR_CODEC_ERROR;
555
0
      status.has_detail = 1;
556
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
557
0
               kMapGainMapMin.c_str());
558
0
      return status;
559
0
    }
560
0
    metadata->min_content_boost[0] = 1.0f;
561
0
  }
562
0
  if (!handler.getGamma(&metadata->gamma[0], &present)) {
563
0
    if (present) {
564
0
      uhdr_error_info_t status;
565
0
      status.error_code = UHDR_CODEC_ERROR;
566
0
      status.has_detail = 1;
567
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
568
0
               kMapGamma.c_str());
569
0
      return status;
570
0
    }
571
0
    metadata->gamma[0] = 1.0f;
572
0
  }
573
0
  if (!handler.getOffsetSdr(&metadata->offset_sdr[0], &present)) {
574
0
    if (present) {
575
0
      uhdr_error_info_t status;
576
0
      status.error_code = UHDR_CODEC_ERROR;
577
0
      status.has_detail = 1;
578
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
579
0
               kMapOffsetSdr.c_str());
580
0
      return status;
581
0
    }
582
0
    metadata->offset_sdr[0] = 1.0f / 64.0f;
583
0
  }
584
0
  if (!handler.getOffsetHdr(&metadata->offset_hdr[0], &present)) {
585
0
    if (present) {
586
0
      uhdr_error_info_t status;
587
0
      status.error_code = UHDR_CODEC_ERROR;
588
0
      status.has_detail = 1;
589
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
590
0
               kMapOffsetHdr.c_str());
591
0
      return status;
592
0
    }
593
0
    metadata->offset_hdr[0] = 1.0f / 64.0f;
594
0
  }
595
0
  if (!handler.getHdrCapacityMin(&metadata->hdr_capacity_min, &present)) {
596
0
    if (present) {
597
0
      uhdr_error_info_t status;
598
0
      status.error_code = UHDR_CODEC_ERROR;
599
0
      status.has_detail = 1;
600
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
601
0
               kMapHDRCapacityMin.c_str());
602
0
      return status;
603
0
    }
604
0
    metadata->hdr_capacity_min = 1.0f;
605
0
  }
606
607
0
  bool base_rendition_is_hdr;
608
0
  if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
609
0
    if (present) {
610
0
      uhdr_error_info_t status;
611
0
      status.error_code = UHDR_CODEC_ERROR;
612
0
      status.has_detail = 1;
613
0
      snprintf(status.detail, sizeof status.detail, "xml parse error, unable to parse attribute %s",
614
0
               kMapBaseRenditionIsHDR.c_str());
615
0
      return status;
616
0
    }
617
0
    base_rendition_is_hdr = false;
618
0
  }
619
0
  if (base_rendition_is_hdr) {
620
0
    uhdr_error_info_t status;
621
0
    status.error_code = UHDR_CODEC_ERROR;
622
0
    status.has_detail = 1;
623
0
    snprintf(status.detail, sizeof status.detail, "hdr intent as base rendition is not supported");
624
0
    return status;
625
0
  }
626
0
  metadata->use_base_cg = true;
627
0
  std::fill_n(metadata->min_content_boost + 1, 2, metadata->min_content_boost[0]);
628
0
  std::fill_n(metadata->max_content_boost + 1, 2, metadata->max_content_boost[0]);
629
0
  std::fill_n(metadata->gamma + 1, 2, metadata->gamma[0]);
630
0
  std::fill_n(metadata->offset_hdr + 1, 2, metadata->offset_hdr[0]);
631
0
  std::fill_n(metadata->offset_sdr + 1, 2, metadata->offset_sdr[0]);
632
633
0
  return g_no_error;
634
0
}
635
636
string generateXmpForPrimaryImage(size_t secondary_image_length,
637
0
                                  uhdr_gainmap_metadata_ext_t& metadata) {
638
0
  const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
639
0
  const vector<string> kLiItem({string("rdf:li"), kConItem});
640
641
0
  std::stringstream ss;
642
0
  photos_editing_formats::image_io::XmlWriter writer(ss);
643
0
  writer.StartWritingElement("x:xmpmeta");
644
0
  writer.WriteXmlns("x", "adobe:ns:meta/");
645
0
  writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
646
0
  writer.StartWritingElement("rdf:RDF");
647
0
  writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
648
0
  writer.StartWritingElement("rdf:Description");
649
0
  writer.WriteXmlns(kContainerPrefix, kContainerUri);
650
0
  writer.WriteXmlns(kItemPrefix, kItemUri);
651
0
  writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
652
0
  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
653
654
0
  writer.StartWritingElements(kConDirSeq);
655
656
0
  size_t item_depth = writer.StartWritingElement("rdf:li");
657
0
  writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
658
0
  writer.StartWritingElement(kConItem);
659
0
  writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
660
0
  writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
661
0
  writer.FinishWritingElementsToDepth(item_depth);
662
663
0
  writer.StartWritingElement("rdf:li");
664
0
  writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
665
0
  writer.StartWritingElement(kConItem);
666
0
  writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
667
0
  writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
668
0
  writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
669
670
0
  writer.FinishWriting();
671
672
0
  return ss.str();
673
0
}
674
675
0
string generateXmpForSecondaryImage(uhdr_gainmap_metadata_ext_t& metadata) {
676
0
  const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
677
678
0
  std::stringstream ss;
679
0
  photos_editing_formats::image_io::XmlWriter writer(ss);
680
0
  writer.StartWritingElement("x:xmpmeta");
681
0
  writer.WriteXmlns("x", "adobe:ns:meta/");
682
0
  writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
683
0
  writer.StartWritingElement("rdf:RDF");
684
0
  writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
685
0
  writer.StartWritingElement("rdf:Description");
686
0
  writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
687
0
  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
688
0
  writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.min_content_boost[0]));
689
0
  writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.max_content_boost[0]));
690
0
  writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma[0]);
691
0
  writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offset_sdr[0]);
692
0
  writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offset_hdr[0]);
693
0
  writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdr_capacity_min));
694
0
  writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdr_capacity_max));
695
0
  writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
696
0
  writer.FinishWriting();
697
698
0
  return ss.str();
699
0
}
700
701
}  // namespace ultrahdr