Coverage Report

Created: 2026-10-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/box.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
#include "libheif/heif.h"
21
#include <cstddef>
22
#include <cstdint>
23
#include "box.h"
24
#include "security_limits.h"
25
#include "nclx.h"
26
#include "codecs/jpeg_boxes.h"
27
#include "codecs/jpeg2000_boxes.h"
28
#include "codecs/hevc_boxes.h"
29
#include "image-items/mask_image.h"
30
#include "codecs/vvc_boxes.h"
31
#include "codecs/avc_boxes.h"
32
#include "codecs/avif_boxes.h"
33
#include "image-items/tiled.h"
34
#include "sequences/seq_boxes.h"
35
36
#include <iomanip>
37
#include <utility>
38
#include <iostream>
39
#include <algorithm>
40
#include <cstring>
41
#include <set>
42
#include <cassert>
43
#include <array>
44
#include <mutex>
45
46
47
#if WITH_UNCOMPRESSED_CODEC
48
#include "codecs/uncompressed/unc_boxes.h"
49
#endif
50
51
#ifndef M_PI
52
#define M_PI 3.14159265358979323846
53
#endif
54
55
#if !defined(_WIN32)
56
#include <unistd.h>
57
#else
58
#include <fcntl.h>
59
#include <io.h>
60
#endif
61
62
63
Fraction::Fraction(int32_t num, int32_t den)
64
2.34k
{
65
  // Reduce resolution of fraction until we are in a safe range.
66
  // We need this as adding fractions may lead to very large denominators
67
  // (e.g. 0x10000 * 0x10000 > 0x100000000 -> overflow, leading to integer 0)
68
69
2.34k
  numerator = num;
70
2.34k
  denominator = den;
71
72
17.4k
  while (denominator > MAX_FRACTION_VALUE || denominator < -MAX_FRACTION_VALUE) {
73
15.0k
    numerator /= 2;
74
15.0k
    denominator /= 2;
75
15.0k
  }
76
77
7.29k
  while (denominator > 1 && (numerator > MAX_FRACTION_VALUE || numerator < -MAX_FRACTION_VALUE)) {
78
4.95k
    numerator /= 2;
79
4.95k
    denominator /= 2;
80
4.95k
  }
81
2.34k
}
82
83
Result<Fraction> Fraction::from_signed(int64_t num, int64_t den)
84
2.49k
{
85
2.49k
  if (num < std::numeric_limits<int32_t>::min() || num > std::numeric_limits<int32_t>::max() ||
86
2.46k
      den < std::numeric_limits<int32_t>::min() || den > std::numeric_limits<int32_t>::max()) {
87
149
    return Error(heif_error_Invalid_input,
88
149
                 heif_suberror_Invalid_fractional_number,
89
149
                 "Fraction value exceeds the supported range");
90
149
  }
91
92
2.34k
  Fraction f(static_cast<int32_t>(num), static_cast<int32_t>(den));
93
2.34k
  if (!f.is_valid()) {
94
159
    return Error(heif_error_Invalid_input,
95
159
                 heif_suberror_Invalid_fractional_number,
96
159
                 "Fraction with zero denominator");
97
159
  }
98
99
2.18k
  return f;
100
2.34k
}
101
102
Result<Fraction> Fraction::from_unsigned(uint32_t num, uint32_t den)
103
1.35k
{
104
1.35k
  return from_signed(num, den);
105
1.35k
}
106
107
Fraction::Fraction(int64_t num, int64_t den)
108
1.35k
{
109
2.43k
  while (num < std::numeric_limits<int32_t>::min() || num > std::numeric_limits<int32_t>::max() ||
110
1.40k
         den < std::numeric_limits<int32_t>::min() || den > std::numeric_limits<int32_t>::max()) {
111
1.07k
    num = (num + (num>=0 ? 1 : -1)) / 2;
112
1.07k
    den = (den + (den>=0 ? 1 : -1)) / 2;
113
1.07k
  }
114
115
1.35k
  numerator = static_cast<int32_t>(num);
116
1.35k
  denominator = static_cast<int32_t>(den);
117
1.35k
}
118
119
Fraction Fraction::operator+(const Fraction& b) const
120
226
{
121
226
  if (denominator == b.denominator) {
122
26
    int64_t n = int64_t{numerator} + b.numerator;
123
26
    int64_t d = denominator;
124
26
    return Fraction{n,d};
125
26
  }
126
200
  else {
127
200
    int64_t n = int64_t{numerator} * b.denominator + int64_t{b.numerator} * denominator;
128
200
    int64_t d = int64_t{denominator} * b.denominator;
129
200
    return Fraction{n,d};
130
200
  }
131
226
}
132
133
Fraction Fraction::operator-(const Fraction& b) const
134
226
{
135
226
  if (denominator == b.denominator) {
136
32
    int64_t n = int64_t{numerator} - b.numerator;
137
32
    int64_t d = denominator;
138
32
    return Fraction{n,d};
139
32
  }
140
194
  else {
141
194
    int64_t n = int64_t{numerator} * b.denominator - int64_t{b.numerator} * denominator;
142
194
    int64_t d = int64_t{denominator} * b.denominator;
143
194
    return Fraction{n,d};
144
194
  }
145
226
}
146
147
Fraction Fraction::operator+(int v) const
148
226
{
149
226
  return Fraction{numerator + v * int64_t(denominator), int64_t(denominator)};
150
226
}
151
152
Fraction Fraction::operator-(int v) const
153
452
{
154
452
  return Fraction{numerator - v * int64_t(denominator), int64_t(denominator)};
155
452
}
156
157
Fraction Fraction::operator/(int v) const
158
226
{
159
226
  return Fraction{int64_t(numerator), int64_t(denominator) * v};
160
226
}
161
162
int32_t Fraction::round_down() const
163
113
{
164
113
  return numerator / denominator;
165
113
}
166
167
int32_t Fraction::round_up() const
168
0
{
169
0
  return int32_t((numerator + int64_t(denominator) - 1) / denominator);
170
0
}
171
172
int32_t Fraction::round() const
173
1.14k
{
174
1.14k
  return int32_t((numerator + int64_t(denominator) / 2) / denominator);
175
1.14k
}
176
177
bool Fraction::is_valid() const
178
2.34k
{
179
2.34k
  return denominator != 0;
180
2.34k
}
181
182
183
1.00M
BoxHeader::BoxHeader() = default;
184
185
186
std::vector<uint8_t> BoxHeader::get_type() const
187
0
{
188
0
  if (m_type == fourcc("uuid")) {
189
0
    return m_uuid_type;
190
0
  }
191
0
  else {
192
0
    std::vector<uint8_t> type(4);
193
0
    type[0] = static_cast<uint8_t>((m_type >> 24) & 0xFF);
194
0
    type[1] = static_cast<uint8_t>((m_type >> 16) & 0xFF);
195
0
    type[2] = static_cast<uint8_t>((m_type >> 8) & 0xFF);
196
0
    type[3] = static_cast<uint8_t>((m_type >> 0) & 0xFF);
197
0
    return type;
198
0
  }
199
0
}
200
201
202
std::string BoxHeader::get_type_string() const
203
390k
{
204
390k
  if (m_type == fourcc("uuid")) {
205
    // 8-4-4-4-12
206
207
340
    std::ostringstream sstr;
208
340
    sstr << std::hex;
209
340
    sstr << std::setfill('0');
210
211
5.78k
    for (int i = 0; i < 16; i++) {
212
5.44k
      if (i == 4 || i == 6 || i == 8 || i == 10) {
213
1.36k
        sstr << '-';
214
1.36k
      }
215
216
5.44k
      sstr << std::setw(2);
217
5.44k
      sstr << ((int) m_uuid_type[i]);
218
5.44k
    }
219
220
340
    return sstr.str();
221
340
  }
222
390k
  else {
223
390k
    return fourcc_to_string(m_type);
224
390k
  }
225
390k
}
226
227
228
std::vector<uint8_t> BoxHeader::get_uuid_type() const
229
950
{
230
950
  if (m_type != fourcc("uuid")) {
231
0
    return {};
232
0
  }
233
234
950
  return m_uuid_type;
235
950
}
236
237
238
void BoxHeader::set_uuid_type(const std::vector<uint8_t>& type)
239
39
{
240
39
  m_type = fourcc("uuid");
241
39
  m_uuid_type = type;
242
39
}
243
244
245
Error BoxHeader::parse_header(BitstreamRange& range)
246
477k
{
247
477k
  StreamReader::grow_status status;
248
477k
  status = range.wait_for_available_bytes(8);
249
477k
  if (status != StreamReader::grow_status::size_reached) {
250
    // TODO: return recoverable error at timeout
251
36
    return Error(heif_error_Invalid_input,
252
36
                 heif_suberror_End_of_data);
253
36
  }
254
255
477k
  m_size = range.read32();
256
477k
  m_type = range.read32();
257
258
477k
  m_header_size = 8;
259
260
477k
  if (m_size == 1) {
261
1.43k
    status = range.wait_for_available_bytes(8);
262
1.43k
    if (status != StreamReader::grow_status::size_reached) {
263
      // TODO: return recoverable error at timeout
264
3
      return Error(heif_error_Invalid_input,
265
3
                   heif_suberror_End_of_data);
266
3
    }
267
268
1.43k
    uint64_t high = range.read32();
269
1.43k
    uint64_t low = range.read32();
270
271
1.43k
    m_size = (high << 32) | low;
272
1.43k
    m_header_size += 8;
273
274
1.43k
    std::stringstream sstr;
275
1.43k
    sstr << "Box size " << m_size << " exceeds security limit.";
276
277
1.43k
    if (m_size > MAX_LARGE_BOX_SIZE) {
278
40
      return Error(heif_error_Memory_allocation_error,
279
40
                   heif_suberror_Security_limit_exceeded,
280
40
                   sstr.str());
281
40
    }
282
1.43k
  }
283
284
477k
  if (m_type == fourcc("uuid")) {
285
528
    status = range.wait_for_available_bytes(16);
286
528
    if (status != StreamReader::grow_status::size_reached) {
287
      // TODO: return recoverable error at timeout
288
2
      return Error(heif_error_Invalid_input,
289
2
                   heif_suberror_End_of_data);
290
2
    }
291
292
526
    if (range.prepare_read(16)) {
293
524
      m_uuid_type.resize(16);
294
524
      bool success = range.get_istream()->read((char*) m_uuid_type.data(), 16);
295
524
      assert(success);
296
524
      (void) success;
297
524
    }
298
299
526
    m_header_size += 16;
300
526
  }
301
302
477k
  return range.get_error();
303
477k
}
304
305
306
int Box::calculate_header_size(bool data64bit) const
307
84.3k
{
308
84.3k
  int header_size = 8;  // does not include "FullBox" fields.
309
310
84.3k
  if (get_short_type() == fourcc("uuid")) {
311
0
    header_size += 16;
312
0
  }
313
314
84.3k
  if (data64bit) {
315
0
    header_size += 8;
316
0
  }
317
318
84.3k
  return header_size;
319
84.3k
}
320
321
322
size_t Box::reserve_box_header_space(StreamWriter& writer, bool data64bit) const
323
84.3k
{
324
84.3k
  size_t start_pos = writer.get_position();
325
326
84.3k
  int header_size = calculate_header_size(data64bit);
327
328
84.3k
  writer.skip(header_size);
329
330
84.3k
  return start_pos;
331
84.3k
}
332
333
334
size_t FullBox::reserve_box_header_space(StreamWriter& writer, bool data64bit) const
335
0
{
336
0
  size_t start_pos = Box::reserve_box_header_space(writer, data64bit);
337
338
0
  writer.skip(4);
339
340
0
  return start_pos;
341
0
}
342
343
344
Error FullBox::write_header(StreamWriter& writer, size_t total_size, bool data64bit) const
345
0
{
346
0
  auto err = Box::write_header(writer, total_size, data64bit);
347
0
  if (err) {
348
0
    return err;
349
0
  }
350
351
0
  assert((get_flags() & ~0x00FFFFFFU) == 0);
352
353
0
  writer.write32((get_version() << 24) | get_flags());
354
355
0
  return Error::Ok;
356
0
}
357
358
359
Error Box::prepend_header(StreamWriter& writer, size_t box_start, bool data64bit) const
360
84.3k
{
361
84.3k
  size_t total_size = writer.data_size() - box_start;
362
363
84.3k
  writer.set_position(box_start);
364
365
84.3k
  auto err = write_header(writer, total_size, data64bit);
366
367
84.3k
  writer.set_position_to_end();  // Note: should we move to the end of the box after writing the header?
368
369
84.3k
  return err;
370
84.3k
}
371
372
373
Error Box::write_header(StreamWriter& writer, size_t total_size, bool data64bit) const
374
84.3k
{
375
84.3k
  bool large_size = (total_size > 0xFFFFFFFF);
376
377
  // --- write header
378
379
84.3k
  if (large_size && !data64bit) {
380
    // Note: as an alternative, we could return an error here. If it fails, the user has to try again with 64 bit.
381
0
    writer.insert(8);
382
0
  }
383
384
84.3k
  if (large_size) {
385
0
    writer.write32(1);
386
0
  }
387
84.3k
  else {
388
84.3k
    assert(total_size <= 0xFFFFFFFF);
389
84.3k
    writer.write32((uint32_t) total_size);
390
84.3k
  }
391
392
84.3k
  writer.write32(get_short_type());
393
394
84.3k
  if (large_size) {
395
0
    writer.write64(total_size);
396
0
  }
397
398
84.3k
  if (get_short_type() == fourcc("uuid")) {
399
0
    assert(get_type().size() == 16);
400
0
    writer.write(get_type());
401
0
  }
402
403
84.3k
  return Error::Ok;
404
84.3k
}
405
406
407
void BoxHeader::dump(std::ostream& sstr, Indent& indent, bool full_log) const
408
0
{
409
0
  sstr << indent << "Box: " << get_type_string();
410
0
  const char* debug_name = debug_box_name();
411
0
  if (debug_name) {
412
0
    sstr << " ----- (" << debug_name << ")\n";
413
0
  }
414
0
  else {
415
0
    sstr << " -----\n";
416
0
  }
417
418
0
  sstr << indent << "size: " << get_box_size() << "   (header size: " << get_header_size() << ")\n";
419
420
0
  return;
421
0
}
422
423
424
Error Box::parse(BitstreamRange& range, const heif_security_limits* limits)
425
46
{
426
  // skip box
427
428
46
  if (get_box_size() == size_until_end_of_file) {
429
29
    range.skip_to_end_of_file();
430
29
  }
431
17
  else {
432
17
    uint64_t content_size = get_box_size() - get_header_size();
433
434
17
    assert(MAX_BOX_SIZE <= SIZE_MAX);
435
436
17
    if (content_size > MAX_BOX_SIZE) {
437
0
      return Error(heif_error_Invalid_input,
438
0
                   heif_suberror_Invalid_box_size);
439
0
    }
440
441
17
    if (range.prepare_read(static_cast<size_t>(content_size))) {
442
17
      range.get_istream()->seek_cur(get_box_size() - get_header_size());
443
17
    }
444
17
  }
445
446
  // Note: seekg() clears the eof flag and it will not be set again afterwards,
447
  // hence we have to test for the fail flag.
448
449
46
  return range.get_error();
450
46
}
451
452
453
Error FullBox::parse_full_box_header(BitstreamRange& range)
454
251k
{
455
251k
  uint32_t data = range.read32();
456
251k
  m_version = static_cast<uint8_t>(data >> 24);
457
251k
  m_flags = data & 0x00FFFFFF;
458
  //m_is_full_box = true;
459
460
251k
  m_header_size += 4;
461
462
251k
  return range.get_error();
463
251k
}
464
465
466
Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_security_limits* limits)
467
390k
{
468
390k
  BoxHeader hdr;
469
390k
  Error err = hdr.parse_header(range);
470
390k
  if (err) {
471
100
    return err;
472
100
  }
473
474
390k
  if (range.error()) {
475
0
    return range.get_error();
476
0
  }
477
478
390k
  result->reset();
479
480
390k
  std::shared_ptr<Box> box;
481
482
390k
  switch (hdr.get_short_type()) {
483
25.5k
    case fourcc("ftyp"):
484
25.5k
      box = std::make_shared<Box_ftyp>();
485
25.5k
      break;
486
487
1
    case fourcc("free"):
488
19
    case fourcc("skip"):
489
19
      box = std::make_shared<Box_free>();
490
19
      break;
491
492
25.4k
    case fourcc("meta"):
493
25.4k
      box = std::make_shared<Box_meta>();
494
25.4k
      break;
495
496
21.5k
    case fourcc("hdlr"):
497
21.5k
      box = std::make_shared<Box_hdlr>();
498
21.5k
      break;
499
500
21.6k
    case fourcc("pitm"):
501
21.6k
      box = std::make_shared<Box_pitm>();
502
21.6k
      break;
503
504
21.6k
    case fourcc("iloc"):
505
21.6k
      box = std::make_shared<Box_iloc>();
506
21.6k
      break;
507
508
21.3k
    case fourcc("iinf"):
509
21.3k
      box = std::make_shared<Box_iinf>();
510
21.3k
      break;
511
512
69.0k
    case fourcc("infe"):
513
69.0k
      box = std::make_shared<Box_infe>();
514
69.0k
      break;
515
516
21.2k
    case fourcc("iprp"):
517
21.2k
      box = std::make_shared<Box_iprp>();
518
21.2k
      break;
519
520
21.0k
    case fourcc("ipco"):
521
21.0k
      box = std::make_shared<Box_ipco>();
522
21.0k
      break;
523
524
20.7k
    case fourcc("ipma"):
525
20.7k
      box = std::make_shared<Box_ipma>();
526
20.7k
      break;
527
528
23.8k
    case fourcc("ispe"):
529
23.8k
      box = std::make_shared<Box_ispe>();
530
23.8k
      break;
531
532
2.63k
    case fourcc("auxC"):
533
2.63k
      box = std::make_shared<Box_auxC>();
534
2.63k
      break;
535
536
140
    case fourcc("irot"):
537
140
      box = std::make_shared<Box_irot>();
538
140
      break;
539
540
195
    case fourcc("imir"):
541
195
      box = std::make_shared<Box_imir>();
542
195
      break;
543
544
567
    case fourcc("clap"):
545
567
      box = std::make_shared<Box_clap>();
546
567
      break;
547
548
13
    case fourcc("iscl"):
549
13
      box = std::make_shared<Box_iscl>();
550
13
      break;
551
552
6.69k
    case fourcc("iref"):
553
6.69k
      box = std::make_shared<Box_iref>();
554
6.69k
      break;
555
556
29
    case fourcc("rref"):
557
29
      box = std::make_shared<Box_rref>();
558
29
      break;
559
560
7.39k
    case fourcc("hvcC"):
561
7.39k
      box = std::make_shared<Box_hvcC>();
562
7.39k
      break;
563
564
5
    case fourcc("hvc1"):
565
5
      box = std::make_shared<Box_hvc1>();
566
5
      break;
567
568
8.47k
    case fourcc("av1C"):
569
8.47k
      box = std::make_shared<Box_av1C>();
570
8.47k
      break;
571
572
25
    case fourcc("av01"):
573
25
      box = std::make_shared<Box_av01>();
574
25
      break;
575
576
212
    case fourcc("vvcC"):
577
212
      box = std::make_shared<Box_vvcC>();
578
212
      break;
579
580
4
    case fourcc("vvc1"):
581
4
      box = std::make_shared<Box_vvc1>();
582
4
      break;
583
584
916
    case fourcc("idat"):
585
916
      box = std::make_shared<Box_idat>();
586
916
      break;
587
588
118
    case fourcc("grpl"):
589
118
      box = std::make_shared<Box_grpl>();
590
118
      break;
591
592
231
    case fourcc("pymd"):
593
231
      box = std::make_shared<Box_pymd>();
594
231
      break;
595
596
38
    case fourcc("altr"):
597
38
      box = std::make_shared<Box_EntityToGroup>();
598
38
      break;
599
600
6
    case fourcc("ster"):
601
6
      box = std::make_shared<Box_ster>();
602
6
      break;
603
604
45
    case fourcc("dinf"):
605
45
      box = std::make_shared<Box_dinf>();
606
45
      break;
607
608
32
    case fourcc("dref"):
609
32
      box = std::make_shared<Box_dref>();
610
32
      break;
611
612
29
    case fourcc("url "):
613
29
      box = std::make_shared<Box_url>();
614
29
      break;
615
616
8.62k
    case fourcc("colr"):
617
8.62k
      box = std::make_shared<Box_colr>();
618
8.62k
      break;
619
620
12.7k
    case fourcc("pixi"):
621
12.7k
      box = std::make_shared<Box_pixi>();
622
12.7k
      break;
623
624
1.01k
    case fourcc("pasp"):
625
1.01k
      box = std::make_shared<Box_pasp>();
626
1.01k
      break;
627
628
30
    case fourcc("lsel"):
629
30
      box = std::make_shared<Box_lsel>();
630
30
      break;
631
632
23
    case fourcc("a1op"):
633
23
      box = std::make_shared<Box_a1op>();
634
23
      break;
635
636
165
    case fourcc("a1lx"):
637
165
      box = std::make_shared<Box_a1lx>();
638
165
      break;
639
640
13
    case fourcc("clli"):
641
13
      box = std::make_shared<Box_clli>();
642
13
      break;
643
644
13
    case fourcc("mdcv"):
645
13
      box = std::make_shared<Box_mdcv>();
646
13
      break;
647
648
40
    case fourcc("amve"):
649
40
      box = std::make_shared<Box_amve>();
650
40
      break;
651
652
18
    case fourcc("ndwt"):
653
18
      box = std::make_shared<Box_ndwt>();
654
18
      break;
655
656
30
    case fourcc("cmin"):
657
30
      box = std::make_shared<Box_cmin>();
658
30
      break;
659
660
1
    case fourcc("cmex"):
661
1
      box = std::make_shared<Box_cmex>();
662
1
      break;
663
664
89
    case fourcc("udes"):
665
89
      box = std::make_shared<Box_udes>();
666
89
      break;
667
668
65
    case fourcc("jpgC"):
669
65
      box = std::make_shared<Box_jpgC>();
670
65
      break;
671
672
3
    case fourcc("mjpg"):
673
3
      box = std::make_shared<Box_mjpg>();
674
3
      break;
675
676
18
    case fourcc("elng"):
677
18
      box = std::make_shared<Box_elng>();
678
18
      break;
679
680
681
0
#if WITH_UNCOMPRESSED_CODEC
682
52
    case fourcc("cmpd"):
683
52
      box = std::make_shared<Box_cmpd>();
684
52
      break;
685
686
1.15k
    case fourcc("uncC"):
687
1.15k
      box = std::make_shared<Box_uncC>();
688
1.15k
      break;
689
690
1.04k
    case fourcc("cmpC"):
691
1.04k
      box = std::make_shared<Box_cmpC>();
692
1.04k
      break;
693
694
44
    case fourcc("icef"):
695
44
      box = std::make_shared<Box_icef>();
696
44
      break;
697
698
19
    case fourcc("cpat"):
699
19
      box = std::make_shared<Box_cpat>();
700
19
      break;
701
702
30
    case fourcc("splz"):
703
30
      box = std::make_shared<Box_splz>();
704
30
      break;
705
706
0
    case fourcc("sbpm"):
707
0
      box = std::make_shared<Box_sbpm>();
708
0
      break;
709
710
0
    case fourcc("snuc"):
711
0
      box = std::make_shared<Box_snuc>();
712
0
      break;
713
714
9
    case fourcc("cloc"):
715
9
      box = std::make_shared<Box_cloc>();
716
9
      break;
717
718
2
    case fourcc("uncv"):
719
2
      box = std::make_shared<Box_uncv>();
720
2
      break;
721
0
#endif
722
723
    // --- JPEG 2000
724
725
5.04k
    case fourcc("j2kH"):
726
5.04k
      box = std::make_shared<Box_j2kH>();
727
5.04k
      break;
728
729
5.00k
    case fourcc("cdef"):
730
5.00k
      box = std::make_shared<Box_cdef>();
731
5.00k
      break;
732
733
97
    case fourcc("cmap"):
734
97
      box = std::make_shared<Box_cmap>();
735
97
      break;
736
737
88
    case fourcc("pclr"):
738
88
      box = std::make_shared<Box_pclr>();
739
88
      break;
740
741
18
    case fourcc("j2kL"):
742
18
      box = std::make_shared<Box_j2kL>();
743
18
      break;
744
745
30
    case fourcc("j2ki"):
746
30
      box = std::make_shared<Box_j2ki>();
747
30
      break;
748
749
750
    // --- mski
751
752
10
    case fourcc("mskC"):
753
10
      box = std::make_shared<Box_mskC>();
754
10
      break;
755
756
    // --- TAI timestamps
757
758
6
    case fourcc("itai"):
759
6
      box = std::make_shared<Box_itai>();
760
6
      break;
761
762
11
    case fourcc("taic"):
763
11
      box = std::make_shared<Box_taic>();
764
11
      break;
765
766
    // --- AVC (H.264)
767
768
1.21k
    case fourcc("avcC"):
769
1.21k
      box = std::make_shared<Box_avcC>();
770
1.21k
      break;
771
772
12
    case fourcc("avc1"):
773
12
      box = std::make_shared<Box_avc1>();
774
12
      break;
775
776
#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
777
    case fourcc("tilC"):
778
      box = std::make_shared<Box_tilC>();
779
      break;
780
#endif
781
782
1.82k
    case fourcc("mini"):
783
1.82k
      box = std::make_shared<Box_mini>();
784
1.82k
      break;
785
786
47
    case fourcc("mdat"):
787
      // avoid generating a 'Box_other'
788
47
      box = std::make_shared<Box>();
789
47
      break;
790
791
339
    case fourcc("uuid"):
792
339
      if (hdr.get_uuid_type() == std::vector<uint8_t>{0x22, 0xcc, 0x04, 0xc7, 0xd6, 0xd9, 0x4e, 0x07, 0x9d, 0x90, 0x4e, 0xb6, 0xec, 0xba, 0xf3, 0xa3}) {
793
79
        box = std::make_shared<Box_cmin>();
794
79
      }
795
260
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x43, 0x63, 0xe9, 0x14, 0x5b, 0x7d, 0x4a, 0xab, 0x97, 0xae, 0xbe, 0xa6, 0x98, 0x03, 0xb4, 0x34}) {
796
76
        box = std::make_shared<Box_cmex>();
797
76
      }
798
184
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x26, 0x1e, 0xf3, 0x74, 0x1d, 0x97, 0x5b, 0xba, 0xac, 0xbd, 0x9d, 0x2c, 0x8e, 0xa7, 0x35, 0x22}) {
799
17
        box = std::make_shared<Box_gimi_content_id>();
800
17
      }
801
167
#if WITH_UNCOMPRESSED_CODEC
802
167
      else if (hdr.get_uuid_type() == std::vector<uint8_t>{0x9d, 0xb9, 0xdd, 0x6e, 0x37, 0x3c, 0x5a, 0x4e, 0x81, 0x10, 0x21, 0xfc, 0x83, 0xa9, 0x11, 0xfd}) {
803
22
        box = std::make_shared<Box_gimi_component_content_ids>();
804
22
      }
805
145
#endif
806
145
      else {
807
145
        box = std::make_shared<Box_other>(hdr.get_short_type());
808
145
      }
809
339
      break;
810
811
    // --- sequences
812
813
772
    case fourcc("moov"):
814
772
      box = std::make_shared<Box_moov>();
815
772
      break;
816
817
34
    case fourcc("mvhd"):
818
34
      box = std::make_shared<Box_mvhd>();
819
34
      break;
820
821
15
    case fourcc("trak"):
822
15
      box = std::make_shared<Box_trak>();
823
15
      break;
824
825
15
    case fourcc("tkhd"):
826
15
      box = std::make_shared<Box_tkhd>();
827
15
      break;
828
829
4
    case fourcc("mdia"):
830
4
      box = std::make_shared<Box_mdia>();
831
4
      break;
832
833
11
    case fourcc("mdhd"):
834
11
      box = std::make_shared<Box_mdhd>();
835
11
      break;
836
837
4
    case fourcc("minf"):
838
4
      box = std::make_shared<Box_minf>();
839
4
      break;
840
841
150
    case fourcc("vmhd"):
842
150
      box = std::make_shared<Box_vmhd>();
843
150
      break;
844
845
3
    case fourcc("stbl"):
846
3
      box = std::make_shared<Box_stbl>();
847
3
      break;
848
849
32
    case fourcc("stsd"):
850
32
      box = std::make_shared<Box_stsd>();
851
32
      break;
852
853
31
    case fourcc("stts"):
854
31
      box = std::make_shared<Box_stts>();
855
31
      break;
856
857
46
    case fourcc("ctts"):
858
46
      box = std::make_shared<Box_ctts>();
859
46
      break;
860
861
47
    case fourcc("stsc"):
862
47
      box = std::make_shared<Box_stsc>();
863
47
      break;
864
865
44
    case fourcc("stco"):
866
44
      box = std::make_shared<Box_stco>();
867
44
      break;
868
869
22
    case fourcc("stsz"):
870
22
      box = std::make_shared<Box_stsz>();
871
22
      break;
872
873
25
    case fourcc("stss"):
874
25
      box = std::make_shared<Box_stss>();
875
25
      break;
876
877
14
    case fourcc("ccst"):
878
14
      box = std::make_shared<Box_ccst>();
879
14
      break;
880
881
25
    case fourcc("auxi"):
882
25
      box = std::make_shared<Box_auxi>();
883
25
      break;
884
885
2
    case fourcc("edts"):
886
2
      box = std::make_shared<Box_edts>();
887
2
      break;
888
889
25
    case fourcc("elst"):
890
25
      box = std::make_shared<Box_elst>();
891
25
      break;
892
893
48
    case fourcc("sbgp"):
894
48
      box = std::make_shared<Box_sbgp>();
895
48
      break;
896
897
3
    case fourcc("sgpd"):
898
3
      box = std::make_shared<Box_sgpd>();
899
3
      break;
900
901
20
    case fourcc("btrt"):
902
20
      box = std::make_shared<Box_btrt>();
903
20
      break;
904
905
36
    case fourcc("saiz"):
906
36
      box = std::make_shared<Box_saiz>();
907
36
      break;
908
909
43
    case fourcc("saio"):
910
43
      box = std::make_shared<Box_saio>();
911
43
      break;
912
913
7
    case fourcc("urim"):
914
7
      box = std::make_shared<Box_URIMetaSampleEntry>();
915
7
      break;
916
917
20
    case fourcc("uri "):
918
20
      box = std::make_shared<Box_uri>();
919
20
      break;
920
921
6
    case fourcc("nmhd"):
922
6
      box = std::make_shared<Box_nmhd>();
923
6
      break;
924
925
202
    case fourcc("tref"):
926
202
      box = std::make_shared<Box_tref>();
927
202
      break;
928
929
3
    case fourcc("sdtp"):
930
3
      box = std::make_shared<Box_sdtp>();
931
3
      break;
932
933
    // OMAF
934
246
    case fourcc("prfr"):
935
246
      box = std::make_shared<Box_prfr>();
936
246
      break;
937
938
28.4k
    default:
939
28.4k
      box = std::make_shared<Box_other>(hdr.get_short_type());
940
28.4k
      break;
941
390k
  }
942
943
390k
  box->set_short_header(hdr);
944
945
390k
  box->m_debug_box_type = hdr.get_type_string(); // only for debugging
946
947
948
390k
  if (range.get_nesting_level() > MAX_BOX_NESTING_LEVEL) {
949
0
    return Error(heif_error_Memory_allocation_error,
950
0
                 heif_suberror_Security_limit_exceeded,
951
0
                 "Security limit for maximum nesting of boxes has been exceeded");
952
0
  }
953
954
390k
  if (hdr.has_fixed_box_size()) {
955
    // Sanity checks
956
386k
    if (hdr.get_box_size() < hdr.get_header_size()) {
957
8
      std::stringstream sstr;
958
8
      sstr << "Box size (" << hdr.get_box_size() << " bytes) smaller than header size ("
959
8
           << hdr.get_header_size() << " bytes)";
960
961
8
      return {heif_error_Invalid_input,
962
8
              heif_suberror_Invalid_box_size,
963
8
              sstr.str()};
964
8
    }
965
966
    // this is >= 0 because of above condition
967
386k
    auto nBytes = static_cast<uint64_t>(hdr.get_box_size() - hdr.get_header_size());
968
386k
    if (nBytes > SIZE_MAX) {
969
0
      return {heif_error_Memory_allocation_error,
970
0
              heif_suberror_Invalid_box_size,
971
0
              "Box size too large"};
972
0
    }
973
974
    // Security check: make sure that box size does not exceed int64 size.
975
976
386k
    if (hdr.get_box_size() > (uint64_t) std::numeric_limits<int64_t>::max()) {
977
0
      return {heif_error_Invalid_input,
978
0
              heif_suberror_Invalid_box_size};
979
0
    }
980
981
    // --- wait for data to arrive
982
983
386k
    auto status = range.wait_for_available_bytes(static_cast<size_t>(nBytes));
984
386k
    if (status != StreamReader::grow_status::size_reached) {
985
      // TODO: return recoverable error at timeout
986
1.08k
      return {heif_error_Invalid_input,
987
1.08k
              heif_suberror_End_of_data};
988
1.08k
    }
989
386k
  }
990
991
389k
  auto box_size = static_cast<int64_t>(hdr.get_box_size());
992
389k
  int64_t box_size_without_header = hdr.has_fixed_box_size() ? (box_size - hdr.get_header_size()) : (int64_t)range.get_remaining_bytes();
993
994
  // Box size may not be larger than remaining bytes in parent box.
995
996
389k
  if ((int64_t)range.get_remaining_bytes() < box_size_without_header) {
997
32
    return {heif_error_Invalid_input,
998
32
            heif_suberror_Invalid_box_size};
999
32
  }
1000
1001
1002
  // Create child bitstream range and read box from that range.
1003
1004
389k
  BitstreamRange boxrange(range.get_istream(),
1005
389k
                          box_size_without_header,
1006
389k
                          &range);
1007
1008
389k
  err = box->parse(boxrange, limits);
1009
389k
  boxrange.skip_to_end_of_box();
1010
1011
389k
  if (err == Error::Ok) {
1012
383k
    *result = std::move(box);
1013
383k
  }
1014
5.83k
  else {
1015
5.83k
    parse_error_fatality fatality = box->get_parse_error_fatality();
1016
1017
5.83k
    box = std::make_shared<Box_Error>(box->get_short_type(), err, fatality);
1018
1019
    // We return a Box_Error that represents the parse error.
1020
5.83k
    *result = std::move(box);
1021
5.83k
  }
1022
1023
389k
  return err;
1024
389k
}
1025
1026
1027
void Box::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1028
0
{
1029
1030
0
  BoxHeader::dump(sstr, indent, full_log);
1031
1032
0
  return;
1033
0
}
1034
1035
1036
void FullBox::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1037
0
{
1038
1039
0
  Box::dump(sstr, indent, full_log);
1040
1041
0
  sstr << indent << "version: " << ((int) m_version) << "\n"
1042
0
       << indent << "flags: " << std::hex << m_flags << std::dec << "\n";
1043
1044
0
  return;
1045
0
}
1046
1047
1048
Error Box::write(StreamWriter& writer) const
1049
0
{
1050
0
  size_t box_start = reserve_box_header_space(writer);
1051
1052
0
  Error err = write_children(writer);
1053
1054
0
  prepend_header(writer, box_start);
1055
1056
0
  return err;
1057
0
}
1058
1059
1060
bool Box::operator==(const Box& other) const
1061
81.0k
{
1062
81.0k
  if (this->get_short_type() != other.get_short_type()) {
1063
38.8k
    return false;
1064
38.8k
  }
1065
1066
42.1k
  StreamWriter writer1;
1067
42.1k
  StreamWriter writer2;
1068
1069
42.1k
  this->write(writer1);
1070
42.1k
  other.write(writer2);
1071
1072
42.1k
  return writer1.get_data() == writer2.get_data();
1073
81.0k
}
1074
1075
1076
bool Box::remove_child_box(const std::shared_ptr<const Box>& box)
1077
0
{
1078
0
  for (int i=0; i<(int)m_children.size(); i++) {
1079
0
    if (m_children[i].get() == box.get()) {
1080
0
      m_children.erase(m_children.begin() + i);
1081
0
      return true;
1082
0
    }
1083
0
  }
1084
1085
0
  return false;
1086
0
}
1087
1088
1089
bool Box::equal(const std::shared_ptr<Box>& box1, const std::shared_ptr<Box>& box2)
1090
119k
{
1091
119k
    if (!box1 || !box2) {
1092
0
        return false;
1093
0
    }
1094
1095
    // This was introduced because of j2kH having child boxes.
1096
    // TODO: we might also deduplicate them by comparing all child boxes.
1097
119k
    if (box1->has_child_boxes() || box2->has_child_boxes()) {
1098
3.11k
      return false;
1099
3.11k
    }
1100
1101
116k
    return *box1 == *box2;
1102
119k
}
1103
1104
1105
Error Box::read_children(BitstreamRange& range, uint32_t max_number, const heif_security_limits* limits)
1106
94.9k
{
1107
  // A freshly parsed box has no children yet, so m_children.size() below tracks
1108
  // the number of children read so far.
1109
94.9k
  assert(m_children.empty());
1110
1111
  // Determine the applicable limit on the number of child boxes.
1112
94.9k
  uint32_t max_children;
1113
94.9k
  if (get_short_type() == fourcc("iinf")) {
1114
21.1k
    max_children = limits->max_items;
1115
21.1k
  }
1116
73.8k
  else {
1117
73.8k
    max_children = limits->max_children_per_box;
1118
73.8k
  }
1119
1120
  // If the number of children is known in advance, reject an excessive count
1121
  // directly instead of reading children until the accumulated count reaches
1122
  // the limit.
1123
94.9k
  if (max_number != READ_CHILDREN_ALL) {
1124
21.2k
    if (max_children && max_number > max_children) {
1125
15
      std::stringstream sstr;
1126
15
      sstr << "Number of child boxes (" << max_number << ") in '" << get_type_string()
1127
15
           << "' box exceeds the security limit of " << max_children << ".";
1128
1129
15
      return Error(heif_error_Memory_allocation_error,
1130
15
                   heif_suberror_Security_limit_exceeded,
1131
15
                   sstr.str());
1132
15
    }
1133
1134
    // The declared number of children cannot exceed what the remaining input
1135
    // could possibly hold (each box occupies at least an 8-byte header). A
1136
    // larger count means the box is truncated or malformed, so reject it
1137
    // before reading anything. This also bounds the reservation below, which
1138
    // matters when security limits are disabled and 'max_number' is otherwise
1139
    // unbounded.
1140
21.1k
    size_t max_possible_children = range.get_remaining_bytes() / 8;
1141
21.1k
    if (max_number > max_possible_children) {
1142
26
      std::stringstream sstr;
1143
26
      sstr << "'" << get_type_string() << "' box declares " << max_number
1144
26
           << " child boxes, but the remaining data can hold at most "
1145
26
           << max_possible_children << ".";
1146
26
      return Error(heif_error_Invalid_input,
1147
26
                   heif_suberror_End_of_data,
1148
26
                   sstr.str());
1149
26
    }
1150
1151
21.1k
    m_children.reserve(max_number);
1152
21.1k
  }
1153
1154
408k
  while (!range.eof() && !range.error()) {
1155
337k
    std::shared_ptr<Box> box;
1156
337k
    Error error = Box::read(range, &box, limits);
1157
337k
    if (error != Error::Ok && (!box || box->get_parse_error_fatality() == parse_error_fatality::fatal)) {
1158
3.12k
      return error;
1159
3.12k
    }
1160
1161
334k
    if (max_number == READ_CHILDREN_ALL) {
1162
263k
      if (max_children && m_children.size() > max_children) {
1163
0
        std::stringstream sstr;
1164
0
        sstr << "Maximum number of child boxes (" << max_children << ") in '" << get_type_string() << "' box exceeded.";
1165
1166
        // Sanity check.
1167
0
        return Error(heif_error_Memory_allocation_error,
1168
0
                     heif_suberror_Security_limit_exceeded,
1169
0
                     sstr.str());
1170
0
      }
1171
263k
    }
1172
1173
334k
    m_children.push_back(std::move(box));
1174
1175
    // Stop once we have read the expected number of children.
1176
334k
    if (max_number != READ_CHILDREN_ALL &&
1177
71.2k
        m_children.size() == max_number) {
1178
21.1k
      break;
1179
21.1k
    }
1180
334k
  }
1181
1182
  // If a specific number of children was expected, we must have read exactly
1183
  // that many. A short read means the box is truncated or malformed. Prefer
1184
  // this specific error over the lower-level range error below, which would
1185
  // otherwise mask it.
1186
91.8k
  if (max_number != READ_CHILDREN_ALL && m_children.size() != max_number) {
1187
10
    std::stringstream sstr;
1188
10
    sstr << "'" << get_type_string() << "' box declares " << max_number
1189
10
         << " child boxes, but only " << m_children.size() << " could be read.";
1190
10
    return Error(heif_error_Invalid_input,
1191
10
                 heif_suberror_End_of_data,
1192
10
                 sstr.str());
1193
10
  }
1194
1195
91.8k
  return range.get_error();
1196
91.8k
}
1197
1198
1199
Error Box::write_children(StreamWriter& writer) const
1200
0
{
1201
0
  for (const auto& child : m_children) {
1202
0
    Error err = child->write(writer);
1203
0
    if (err) {
1204
0
      return err;
1205
0
    }
1206
0
  }
1207
1208
0
  return Error::Ok;
1209
0
}
1210
1211
1212
void Box::dump_children(std::ostream& sstr, Indent& indent, bool full_log, bool with_index) const
1213
0
{
1214
0
  bool first = true;
1215
0
  int idx=1;
1216
1217
0
  indent++;
1218
0
  for (const auto& childBox : m_children) {
1219
0
    if (first) {
1220
0
      first = false;
1221
0
    }
1222
0
    else {
1223
0
      sstr << indent << "\n";
1224
0
    }
1225
1226
0
    if (with_index) {
1227
0
      sstr << indent << "index: " << idx << "\n";
1228
0
      idx++;
1229
0
    }
1230
1231
0
    childBox->dump(sstr, indent, full_log);
1232
0
    reset_stream_format(sstr);
1233
0
  }
1234
0
  indent--;
1235
0
}
1236
1237
1238
void Box::derive_box_version_recursive()
1239
0
{
1240
0
  derive_box_version();
1241
1242
0
  for (auto& child : m_children) {
1243
0
    child->derive_box_version_recursive();
1244
0
  }
1245
0
}
1246
1247
1248
void Box::patch_file_pointers_recursively(StreamWriter& writer, size_t offset)
1249
0
{
1250
0
  patch_file_pointers(writer, offset);
1251
1252
0
  for (auto& child : m_children) {
1253
0
    child->patch_file_pointers_recursively(writer, offset);
1254
0
  }
1255
0
}
1256
1257
1258
Error Box_other::parse(BitstreamRange& range, const heif_security_limits* limits)
1259
27.5k
{
1260
27.5k
  if (has_fixed_box_size()) {
1261
25.8k
    size_t len;
1262
25.8k
    if (get_box_size() >= get_header_size()) {
1263
25.8k
      auto len64 = get_box_size() - get_header_size();
1264
25.8k
      if (len64 > MAX_BOX_SIZE) {
1265
0
        return {heif_error_Invalid_input,
1266
0
                heif_suberror_Security_limit_exceeded,
1267
0
                "Box size too large"};
1268
0
      }
1269
1270
25.8k
      len = static_cast<size_t>(len64);
1271
1272
25.8k
      m_data.resize(len);
1273
25.8k
      range.read(m_data.data(), len);
1274
25.8k
    }
1275
0
    else {
1276
0
      return {heif_error_Invalid_input,
1277
0
              heif_suberror_Invalid_box_size};
1278
0
    }
1279
25.8k
  }
1280
1.74k
  else {
1281
    // TODO: boxes until end of file (we will probably never need this)
1282
1.74k
  }
1283
1284
27.5k
  return range.get_error();
1285
27.5k
}
1286
1287
1288
Error Box_other::write(StreamWriter& writer) const
1289
0
{
1290
0
  size_t box_start = reserve_box_header_space(writer);
1291
1292
0
  if (get_box_size() >= get_header_size()) {
1293
0
    writer.write(m_data);
1294
0
    prepend_header(writer, box_start);
1295
0
    return Error::Ok;
1296
0
  }
1297
0
  else {
1298
0
    return Error(heif_error_Invalid_input,
1299
0
                 heif_suberror_Invalid_box_size);
1300
0
  }
1301
0
}
1302
1303
1304
void Box_other::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1305
0
{
1306
1307
0
  BoxHeader::dump(sstr, indent, full_log);
1308
1309
  // --- show raw box content
1310
1311
0
  size_t len = 0;
1312
0
  if (get_box_size() >= get_header_size()) {
1313
    // We can cast because if it does not fit, it would fail during parsing.
1314
0
    len = static_cast<size_t>(get_box_size() - get_header_size());
1315
0
  }
1316
0
  else {
1317
0
    sstr << indent << "invalid box size " << get_box_size() << " (smaller than header)\n";
1318
0
    return;
1319
0
  }
1320
1321
0
  sstr << write_raw_data_as_hex(m_data.data(), len,
1322
0
                                indent.get_string() + "data: ",
1323
0
                                indent.get_string() + "      ");
1324
1325
0
  return;
1326
0
}
1327
1328
1329
void Box_Error::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1330
0
{
1331
0
  sstr << indent << '\'' << fourcc_to_string(m_box_type_with_parse_error) << "' parse error: " << m_error.message << "\n";
1332
0
  sstr << indent << "fatality: ";
1333
0
  switch (m_fatality) {
1334
0
    case parse_error_fatality::fatal: sstr << "fatal\n"; break;
1335
0
    case parse_error_fatality::ignorable: sstr << "ignorable\n"; break;
1336
0
    case parse_error_fatality::optional: sstr << "optional\n"; break;
1337
0
  }
1338
1339
0
  return;
1340
0
}
1341
1342
parse_error_fatality Box_Error::get_parse_error_fatality() const
1343
3.74k
{
1344
3.74k
  return m_fatality;
1345
3.74k
}
1346
1347
1348
Error Box_ftyp::parse(BitstreamRange& range, const heif_security_limits* limits)
1349
25.5k
{
1350
25.5k
  m_major_brand = range.read32();
1351
25.5k
  m_minor_version = range.read32();
1352
1353
25.5k
  uint64_t box_size = get_box_size();
1354
25.5k
  if (box_size < 8 || box_size - 8 < get_header_size()) {
1355
    // Sanity check.
1356
5
    return Error(heif_error_Invalid_input,
1357
5
                 heif_suberror_Invalid_box_size,
1358
5
                 "ftyp box too small (less than 8 bytes)");
1359
5
  }
1360
1361
25.5k
  uint64_t n_minor_brands = (get_box_size() - get_header_size() - 8) / 4;
1362
1363
25.5k
  if (limits->max_number_of_file_brands && n_minor_brands > limits->max_number_of_file_brands) {
1364
1
    return {
1365
1
      heif_error_Memory_allocation_error,
1366
1
      heif_suberror_Security_limit_exceeded,
1367
1
      "Number of minor brands in file exceeds security limit"
1368
1
    };
1369
1
  }
1370
1371
95.9k
  for (uint64_t i = 0; i < n_minor_brands && !range.error(); i++) {
1372
70.4k
    m_compatible_brands.push_back(range.read32());
1373
70.4k
  }
1374
1375
25.5k
  return range.get_error();
1376
25.5k
}
1377
1378
1379
bool Box_ftyp::has_compatible_brand(heif_brand2 brand) const
1380
192k
{
1381
192k
  return std::find(m_compatible_brands.begin(),
1382
192k
                   m_compatible_brands.end(),
1383
192k
                   brand) !=
1384
192k
         m_compatible_brands.end();
1385
192k
}
1386
1387
1388
void Box_ftyp::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1389
0
{
1390
1391
0
  BoxHeader::dump(sstr, indent, full_log);
1392
1393
0
  sstr << indent << "major brand: " << fourcc_to_string(m_major_brand) << "\n"
1394
0
       << indent << "minor version: ";
1395
0
  if (m_minor_version < ('A' << 24)) {
1396
    // This is probably a version number
1397
0
    sstr << m_minor_version;
1398
0
  } else {
1399
    // probably a 4CC, as used for mif3
1400
0
    sstr << fourcc_to_string(m_minor_version);
1401
0
  }
1402
0
  sstr << "\n" << indent << "compatible brands: ";
1403
1404
0
  bool first = true;
1405
0
  for (uint32_t brand : m_compatible_brands) {
1406
0
    if (first) { first = false; }
1407
0
    else { sstr << ','; }
1408
1409
0
    sstr << fourcc_to_string(brand);
1410
0
  }
1411
0
  sstr << "\n";
1412
1413
0
  return;
1414
0
}
1415
1416
1417
void Box_ftyp::add_compatible_brand(heif_brand2 brand)
1418
0
{
1419
0
  if (!has_compatible_brand(brand)) {
1420
0
    m_compatible_brands.push_back(brand);
1421
0
  }
1422
0
}
1423
1424
1425
Error Box_ftyp::write(StreamWriter& writer) const
1426
0
{
1427
0
  size_t box_start = reserve_box_header_space(writer);
1428
1429
0
  writer.write32(m_major_brand);
1430
0
  writer.write32(m_minor_version);
1431
1432
0
  for (uint32_t b : m_compatible_brands) {
1433
0
    writer.write32(b);
1434
0
  }
1435
1436
0
  prepend_header(writer, box_start);
1437
1438
0
  return Error::Ok;
1439
0
}
1440
1441
1442
Error Box_free::parse(BitstreamRange& range, const heif_security_limits* limits)
1443
16
{
1444
16
  range.skip_to_end_of_box();
1445
16
  return range.get_error();
1446
16
}
1447
1448
1449
void Box_free::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1450
0
{
1451
0
  BoxHeader::dump(sstr, indent, full_log);
1452
0
  return;
1453
0
}
1454
1455
1456
Error Box_free::write(StreamWriter& writer) const
1457
0
{
1458
0
  size_t box_start = reserve_box_header_space(writer);
1459
0
  prepend_header(writer, box_start);
1460
0
  return Error::Ok;
1461
0
}
1462
1463
1464
Error Box_meta::parse(BitstreamRange& range, const heif_security_limits* limits)
1465
25.4k
{
1466
25.4k
  parse_full_box_header(range);
1467
1468
25.4k
  if (get_version() != 0) {
1469
46
    return unsupported_version_error("meta");
1470
46
  }
1471
1472
  /*
1473
  uint64_t boxSizeLimit;
1474
  if (get_box_size() == BoxHeader::size_until_end_of_file) {
1475
    boxSizeLimit = sizeLimit;
1476
  }
1477
  else {
1478
    boxSizeLimit = get_box_size() - get_header_size();
1479
  }
1480
  */
1481
1482
25.4k
  return read_children(range, READ_CHILDREN_ALL, limits);
1483
25.4k
}
1484
1485
1486
void Box_meta::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1487
0
{
1488
0
  Box::dump(sstr, indent, full_log);
1489
0
  dump_children(sstr, indent, full_log);
1490
1491
0
  return;
1492
0
}
1493
1494
1495
Error FullBox::unsupported_version_error(const char* box) const
1496
584
{
1497
584
  std::stringstream sstr;
1498
584
  sstr << box << " box data version " << ((int) m_version) << " is not implemented yet";
1499
1500
584
  return {heif_error_Unsupported_feature,
1501
584
          heif_suberror_Unsupported_data_version,
1502
584
          sstr.str()};
1503
584
}
1504
1505
1506
Error Box_hdlr::parse(BitstreamRange& range, const heif_security_limits* limits)
1507
21.5k
{
1508
21.5k
  parse_full_box_header(range);
1509
1510
21.5k
  if (get_version() != 0) {
1511
2
    return unsupported_version_error("hdlr");
1512
2
  }
1513
1514
21.5k
  m_pre_defined = range.read32();
1515
21.5k
  m_handler_type = range.read32();
1516
1517
86.0k
  for (int i = 0; i < 3; i++) {
1518
64.5k
    m_reserved[i] = range.read32();
1519
64.5k
  }
1520
1521
21.5k
  m_name = range.read_string();
1522
1523
21.5k
  return range.get_error();
1524
21.5k
}
1525
1526
1527
void Box_hdlr::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1528
0
{
1529
0
  Box::dump(sstr, indent, full_log);
1530
0
  sstr << indent << "pre_defined: " << m_pre_defined << "\n"
1531
0
       << indent << "handler_type: " << fourcc_to_string(m_handler_type) << "\n"
1532
0
       << indent << "name: " << m_name << "\n";
1533
1534
0
  return;
1535
0
}
1536
1537
1538
Error Box_hdlr::write(StreamWriter& writer) const
1539
0
{
1540
0
  size_t box_start = reserve_box_header_space(writer);
1541
1542
0
  writer.write32(m_pre_defined);
1543
0
  writer.write32(m_handler_type);
1544
1545
0
  for (int i = 0; i < 3; i++) {
1546
0
    writer.write32(m_reserved[i]);
1547
0
  }
1548
1549
0
  writer.write(m_name);
1550
1551
0
  prepend_header(writer, box_start);
1552
1553
0
  return Error::Ok;
1554
0
}
1555
1556
1557
Error Box_pitm::parse(BitstreamRange& range, const heif_security_limits* limits)
1558
21.6k
{
1559
21.6k
  parse_full_box_header(range);
1560
1561
21.6k
  if (get_version() > 1) {
1562
2
    return unsupported_version_error("pitm");
1563
2
  }
1564
1565
1566
21.5k
  if (get_version() == 0) {
1567
21.5k
    m_item_ID = range.read16();
1568
21.5k
  }
1569
1
  else {
1570
1
    m_item_ID = range.read32();
1571
1
  }
1572
1573
21.5k
  return range.get_error();
1574
21.6k
}
1575
1576
1577
void Box_pitm::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1578
0
{
1579
0
  Box::dump(sstr, indent, full_log);
1580
0
  sstr << indent << "item_ID: " << m_item_ID << "\n";
1581
1582
0
  return;
1583
0
}
1584
1585
1586
void Box_pitm::derive_box_version()
1587
0
{
1588
0
  if (m_item_ID <= 0xFFFF) {
1589
0
    set_version(0);
1590
0
  }
1591
0
  else {
1592
0
    set_version(1);
1593
0
  }
1594
0
}
1595
1596
1597
Error Box_pitm::write(StreamWriter& writer) const
1598
0
{
1599
0
  size_t box_start = reserve_box_header_space(writer);
1600
1601
0
  if (get_version() == 0) {
1602
0
    assert(m_item_ID <= 0xFFFF);
1603
0
    writer.write16((uint16_t) m_item_ID);
1604
0
  }
1605
0
  else {
1606
0
    writer.write32(m_item_ID);
1607
0
  }
1608
1609
0
  prepend_header(writer, box_start);
1610
1611
0
  return Error::Ok;
1612
0
}
1613
1614
1615
Error Box_iloc::parse(BitstreamRange& range, const heif_security_limits* limits)
1616
21.6k
{
1617
21.6k
  parse_full_box_header(range);
1618
1619
21.6k
  if (get_version() > 2) {
1620
2
    return unsupported_version_error("iloc");
1621
2
  }
1622
1623
21.6k
  const int version = get_version();
1624
1625
21.6k
  uint16_t values4 = range.read16();
1626
1627
21.6k
  int offset_size = (values4 >> 12) & 0xF;
1628
21.6k
  int length_size = (values4 >> 8) & 0xF;
1629
21.6k
  int base_offset_size = (values4 >> 4) & 0xF;
1630
21.6k
  int index_size = 0;
1631
1632
21.6k
  if (version == 1 || version == 2) {
1633
1.00k
    index_size = (values4 & 0xF);
1634
1.00k
  }
1635
1636
21.6k
  uint32_t item_count = 0;
1637
21.6k
  if (version < 2) {
1638
21.5k
    item_count = range.read16();
1639
21.5k
  }
1640
62
  else if (version == 2) {
1641
62
    item_count = range.read32();
1642
62
  }
1643
1644
  // Sanity check. (This might be obsolete now as we check for range.error() below).
1645
21.6k
  if (limits->max_items && item_count > limits->max_items) {
1646
20
    std::stringstream sstr;
1647
20
    sstr << "iloc box contains " << item_count << " items, which exceeds the security limit of "
1648
20
         << limits->max_items << " items.";
1649
1650
20
    return Error(heif_error_Memory_allocation_error,
1651
20
                 heif_suberror_Security_limit_exceeded,
1652
20
                 sstr.str());
1653
20
  }
1654
1655
93.3k
  for (uint32_t i = 0; i < item_count; i++) {
1656
71.8k
    Item item;
1657
1658
71.8k
    if (range.eof()) {
1659
22
      std::stringstream sstr;
1660
22
      sstr << "iloc box should contain " << item_count << " items, but we can only read " << i << " items.";
1661
1662
22
      return {heif_error_Invalid_input,
1663
22
              heif_suberror_End_of_data,
1664
22
              sstr.str()};
1665
22
    }
1666
1667
71.8k
    if (version < 2) {
1668
71.6k
      item.item_ID = range.read16();
1669
71.6k
    }
1670
170
    else if (version == 2) {
1671
170
      item.item_ID = range.read32();
1672
170
    }
1673
1674
71.8k
    if (version >= 1) {
1675
40.5k
      values4 = range.read16();
1676
40.5k
      item.construction_method = (values4 & 0xF);
1677
40.5k
    }
1678
1679
71.8k
    item.data_reference_index = range.read16();
1680
1681
71.8k
    item.base_offset = 0;
1682
71.8k
    if (base_offset_size == 4) {
1683
19.6k
      item.base_offset = range.read32();
1684
19.6k
    }
1685
52.1k
    else if (base_offset_size == 8) {
1686
84
      item.base_offset = ((uint64_t) range.read32()) << 32;
1687
84
      item.base_offset |= range.read32();
1688
84
    }
1689
1690
71.8k
    uint16_t extent_count = range.read16();
1691
1692
    // Sanity check.
1693
71.8k
    auto max_iloc_extents = limits->max_iloc_extents_per_item;
1694
71.8k
    if (max_iloc_extents && extent_count > max_iloc_extents) {
1695
41
      std::stringstream sstr;
1696
41
      sstr << "Number of extents in iloc box (" << extent_count << ") exceeds security limit ("
1697
41
           << max_iloc_extents << ")\n";
1698
1699
41
      return Error(heif_error_Memory_allocation_error,
1700
41
                   heif_suberror_Security_limit_exceeded,
1701
41
                   sstr.str());
1702
41
    }
1703
1704
143k
    for (int e = 0; e < extent_count; e++) {
1705
71.4k
      Extent extent;
1706
1707
71.4k
      if (range.eof()) {
1708
22
        std::stringstream sstr;
1709
22
        sstr << "iloc item should contain " << extent_count << " extents, but we can only read " << e << " extents.";
1710
1711
22
        return {heif_error_Invalid_input,
1712
22
                heif_suberror_End_of_data,
1713
22
                sstr.str()};
1714
22
      }
1715
1716
71.4k
      if ((version == 1 || version == 2) && index_size > 0) {
1717
338
        if (index_size == 4) {
1718
84
          extent.index = range.read32();
1719
84
        }
1720
254
        else if (index_size == 8) {
1721
101
          extent.index = ((uint64_t) range.read32()) << 32;
1722
101
          extent.index |= range.read32();
1723
101
        }
1724
338
      }
1725
1726
71.4k
      extent.offset = 0;
1727
71.4k
      if (offset_size == 4) {
1728
68.5k
        extent.offset = range.read32();
1729
68.5k
      }
1730
2.85k
      else if (offset_size == 8) {
1731
239
        extent.offset = ((uint64_t) range.read32()) << 32;
1732
239
        extent.offset |= range.read32();
1733
239
      }
1734
1735
1736
71.4k
      extent.length = 0;
1737
71.4k
      if (length_size == 4) {
1738
69.3k
        extent.length = range.read32();
1739
69.3k
      }
1740
2.06k
      else if (length_size == 8) {
1741
443
        extent.length = ((uint64_t) range.read32()) << 32;
1742
443
        extent.length |= range.read32();
1743
443
      }
1744
1745
71.4k
      item.extents.push_back(extent);
1746
71.4k
    }
1747
1748
71.7k
    if (!range.error()) {
1749
71.7k
      m_items.push_back(item);
1750
71.7k
    }
1751
71.7k
  }
1752
1753
21.5k
  return range.get_error();
1754
21.5k
}
1755
1756
1757
Box_iloc::Box_iloc()
1758
22.0k
{
1759
22.0k
  set_short_type(fourcc("iloc"));
1760
1761
22.0k
  set_use_tmp_file(false);
1762
22.0k
}
1763
1764
1765
Box_iloc::~Box_iloc()
1766
22.0k
{
1767
22.0k
  if (m_use_tmpfile) {
1768
0
    unlink(m_tmp_filename);
1769
0
  }
1770
22.0k
}
1771
1772
1773
void Box_iloc::set_use_tmp_file(bool flag)
1774
22.0k
{
1775
22.0k
  m_use_tmpfile = flag;
1776
22.0k
  if (flag) {
1777
0
#if !defined(_WIN32)
1778
0
    strcpy(m_tmp_filename, "/tmp/libheif-XXXXXX");
1779
0
    m_tmpfile_fd = mkstemp(m_tmp_filename);
1780
#else
1781
    // TODO Currently unused code. Implement when needed.
1782
    assert(false);
1783
#  if 0
1784
    char tmpname[L_tmpnam_s];
1785
    // TODO: check return value (errno_t)
1786
    tmpnam_s(tmpname, L_tmpnam_s);
1787
    _sopen_s(&m_tmpfile_fd, tmpname, _O_CREAT | _O_TEMPORARY | _O_TRUNC | _O_RDWR, _SH_DENYRW, _S_IREAD | _S_IWRITE);
1788
#  endif
1789
#endif
1790
0
  }
1791
22.0k
}
1792
1793
1794
void Box_iloc::dump(std::ostream& sstr, Indent& indent, bool full_log) const
1795
0
{
1796
0
  Box::dump(sstr, indent, full_log);
1797
1798
0
  for (const Item& item : m_items) {
1799
0
    sstr << indent << "item ID: " << item.item_ID << "\n"
1800
0
         << indent << "  construction method: " << ((int) item.construction_method) << "\n"
1801
0
         << indent << "  data_reference_index: " << std::hex
1802
0
         << item.data_reference_index << std::dec << "\n"
1803
0
         << indent << "  base_offset: " << item.base_offset << "\n";
1804
1805
0
    sstr << indent << "  extents: ";
1806
0
    for (const Extent& extent : item.extents) {
1807
0
      sstr << extent.offset << "," << extent.length;
1808
0
      if (extent.index != 0) {
1809
0
        sstr << ";index=" << extent.index;
1810
0
      }
1811
0
      sstr << " ";
1812
0
    }
1813
0
    sstr << "\n";
1814
0
  }
1815
1816
0
  return;
1817
0
}
1818
1819
1820
Error Box_iloc::read_data(heif_item_id item,
1821
                          const std::shared_ptr<StreamReader>& istr,
1822
                          const std::shared_ptr<Box_idat>& idat,
1823
                          std::vector<uint8_t>* dest,
1824
                          const heif_security_limits* limits) const
1825
9.37k
{
1826
9.37k
  return read_data(item, istr, idat, dest, 0, std::numeric_limits<uint64_t>::max(), limits);
1827
9.37k
}
1828
1829
1830
Error Box_iloc::read_data(heif_item_id item_id,
1831
                          const std::shared_ptr<StreamReader>& istr,
1832
                          const std::shared_ptr<Box_idat>& idat,
1833
                          std::vector<uint8_t>* dest,
1834
                          uint64_t offset, uint64_t size,
1835
                          const heif_security_limits* limits) const
1836
54.7k
{
1837
54.7k
  const Item* item = nullptr;
1838
736k
  for (auto& i : m_items) {
1839
736k
    if (i.item_ID == item_id) {
1840
53.5k
      item = &i;
1841
53.5k
      break;
1842
53.5k
    }
1843
736k
  }
1844
1845
54.7k
  if (!item) {
1846
1.19k
    std::stringstream sstr;
1847
1.19k
    sstr << "Item with ID " << item_id << " has no compressed data";
1848
1849
1.19k
    return Error(heif_error_Invalid_input,
1850
1.19k
                 heif_suberror_No_item_data,
1851
1.19k
                 sstr.str());
1852
1.19k
  }
1853
1854
53.5k
#if ENABLE_MULTITHREADING_SUPPORT
1855
53.5k
  static std::mutex read_mutex;
1856
1857
53.5k
  std::lock_guard<std::mutex> lock(read_mutex);
1858
53.5k
#endif
1859
1860
53.5k
  bool limited_size = (size != std::numeric_limits<uint64_t>::max());
1861
1862
1863
  // TODO: this function should always append the data to the output vector as this is used when
1864
  //       the image data is concatenated with data in a configuration box. However, it seems that
1865
  //       this function clears the array in some cases. This should be corrected.
1866
1867
53.5k
  for (const auto& extent : item->extents) {
1868
1869
    // --- data that was appended in memory (append_data()) and not written to a file yet
1870
1871
    // This is the case for items added to a context that is being written. There may be no
1872
    // input stream at all (writer-only context), and even if there is one (context read from
1873
    // a file, then modified), the stream does not contain this data.
1874
1875
53.0k
    if (!extent.data.empty() || extent.length == 0) {
1876
1.01k
      uint64_t skip_len = std::min(offset, extent.length);
1877
1.01k
      offset -= skip_len;
1878
1879
1.01k
      uint64_t read_len = std::min(extent.length - skip_len, size);
1880
1881
1.01k
      if (offset > 0 || read_len == 0) {
1882
1.01k
        continue;
1883
1.01k
      }
1884
1885
0
      auto max_memory_block_size = limits->max_memory_block_size;
1886
0
      if (max_memory_block_size && max_memory_block_size - dest->size() < read_len) {
1887
0
        return {heif_error_Memory_allocation_error,
1888
0
                heif_suberror_Security_limit_exceeded,
1889
0
                "iloc item data exceeds the maximum memory block size"};
1890
0
      }
1891
1892
0
      dest->insert(dest->end(),
1893
0
                   extent.data.begin() + static_cast<size_t>(skip_len),
1894
0
                   extent.data.begin() + static_cast<size_t>(skip_len + read_len));
1895
1896
0
      size -= read_len;
1897
0
      continue;
1898
0
    }
1899
1900
52.0k
    if (item->construction_method == 0) {
1901
1902
50.3k
      if (!istr) {
1903
0
        return {heif_error_Invalid_input,
1904
0
                heif_suberror_No_item_data,
1905
0
                "Item data is not available: the context has no input file"};
1906
0
      }
1907
1908
      // --- make sure that all data is available
1909
1910
50.3k
      if (extent.offset > MAX_FILE_POS ||
1911
50.3k
          item->base_offset > MAX_FILE_POS ||
1912
50.3k
          extent.length > MAX_FILE_POS) {
1913
16
        return {heif_error_Invalid_input,
1914
16
                heif_suberror_Security_limit_exceeded,
1915
16
                "iloc data pointers out of allowed range"};
1916
16
      }
1917
1918
50.3k
      StreamReader::grow_status status = istr->wait_for_file_size(extent.offset + item->base_offset + extent.length);
1919
50.3k
      if (status == StreamReader::grow_status::size_beyond_eof) {
1920
        // Out-of-bounds
1921
        // TODO: I think we should not clear this. Maybe we want to try reading again later and
1922
        // hence should not lose the data already read.
1923
18.8k
        dest->clear();
1924
1925
18.8k
        std::stringstream sstr;
1926
18.8k
        sstr << "Extent in iloc box references data outside of file bounds "
1927
18.8k
             << "(points to file position " << extent.offset + item->base_offset << ")\n";
1928
1929
18.8k
        return {heif_error_Invalid_input,
1930
18.8k
                heif_suberror_End_of_data,
1931
18.8k
                sstr.str()};
1932
18.8k
      }
1933
31.4k
      else if (status == StreamReader::grow_status::timeout) {
1934
        // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
1935
0
        return {heif_error_Invalid_input,
1936
0
                heif_suberror_End_of_data};
1937
0
      }
1938
1939
1940
      // skip to reading offset
1941
1942
31.4k
      uint64_t skip_len = std::min(offset, extent.length);
1943
31.4k
      offset -= skip_len;
1944
1945
31.4k
      uint64_t read_len = std::min(extent.length - skip_len, size);
1946
1947
31.4k
      if (offset > 0) {
1948
0
        continue;
1949
0
      }
1950
1951
31.4k
      if (read_len == 0) {
1952
5
        continue;
1953
5
      }
1954
1955
31.4k
      size_t old_size = dest->size();
1956
1957
      // --- security check that we do not allocate too much memory
1958
1959
31.4k
      auto max_memory_block_size = limits->max_memory_block_size;
1960
31.4k
      if (max_memory_block_size && max_memory_block_size - old_size < read_len) {
1961
0
        std::stringstream sstr;
1962
0
        sstr << "iloc box contained " << extent.length << " bytes, total memory size would be "
1963
0
             << (old_size + extent.length) << " bytes, exceeding the security limit of "
1964
0
             << max_memory_block_size << " bytes";
1965
1966
0
        return {heif_error_Memory_allocation_error,
1967
0
                heif_suberror_Security_limit_exceeded,
1968
0
                sstr.str()};
1969
0
      }
1970
1971
1972
      // --- request file range
1973
1974
31.4k
      uint64_t data_start_pos = extent.offset + item->base_offset + skip_len;
1975
31.4k
      uint64_t rangeRequestEndPos = istr->request_range(data_start_pos, data_start_pos + read_len);
1976
31.4k
      if (rangeRequestEndPos == 0) {
1977
0
        return istr->get_error();
1978
0
      }
1979
1980
      // --- move file pointer to start of data
1981
1982
31.4k
      bool success = istr->seek(data_start_pos);
1983
31.4k
      if (!success) {
1984
0
        return {heif_error_Invalid_input,
1985
0
                heif_suberror_Unspecified,
1986
0
                "Error setting input file position"};
1987
0
      }
1988
1989
1990
      // --- read data
1991
1992
31.4k
      dest->resize(static_cast<size_t>(old_size + read_len));
1993
31.4k
      success = istr->read((char*) dest->data() + old_size, static_cast<size_t>(read_len));
1994
31.4k
      if (!success) {
1995
0
        return {heif_error_Invalid_input,
1996
0
                heif_suberror_Unspecified,
1997
0
                "Error reading input file"};
1998
0
      }
1999
2000
31.4k
      size -= read_len;
2001
31.4k
    }
2002
1.73k
    else if (item->construction_method == 1) {
2003
1.61k
      if (!idat) {
2004
14
        return {heif_error_Invalid_input,
2005
14
                heif_suberror_No_idat_box,
2006
14
                "idat box referenced in iref box is not present in file"};
2007
14
      }
2008
2009
      // Honor the requested (offset, size) window, exactly like the in-memory
2010
      // and construction_method==0 branches above. Reading the whole extent and
2011
      // doing `size -= extent.length` unconditionally ignored the caller's
2012
      // sub-range: any partial read (size < extent.length) underflowed `size`
2013
      // and fell into the "Not enough data" check below, which broke per-tile
2014
      // and per-icef-unit reads of uncompressed items stored in 'idat'
2015
      // (unc_decoder::get_compressed_image_data_uncompressed()).
2016
1.59k
      uint64_t skip_len = std::min(offset, extent.length);
2017
1.59k
      offset -= skip_len;
2018
2019
1.59k
      uint64_t read_len = std::min(extent.length - skip_len, size);
2020
2021
1.59k
      if (offset > 0) {
2022
0
        continue;
2023
0
      }
2024
2025
1.59k
      if (read_len == 0) {
2026
0
        continue;
2027
0
      }
2028
2029
1.59k
      Error err = idat->read_data(istr,
2030
1.59k
                                  extent.offset + item->base_offset + skip_len,
2031
1.59k
                                  read_len,
2032
1.59k
                                  *dest, limits);
2033
1.59k
      if (err) {
2034
99
        return err;
2035
99
      }
2036
2037
1.50k
      size -= read_len;
2038
1.50k
    }
2039
126
    else {
2040
126
      std::stringstream sstr;
2041
126
      sstr << "Item construction method " << (int) item->construction_method << " not implemented";
2042
126
      return {heif_error_Unsupported_feature,
2043
126
              heif_suberror_Unsupported_item_construction_method,
2044
126
              sstr.str()};
2045
126
    }
2046
52.0k
  }
2047
2048
  // --- we could not read all data
2049
2050
34.3k
  if (limited_size && size > 0) {
2051
3
    return {heif_error_Invalid_input,
2052
3
            heif_suberror_End_of_data,
2053
3
            "Not enough data present in 'iloc' to satisfy request."};
2054
3
  }
2055
2056
34.3k
  return Error::Ok;
2057
34.3k
}
2058
2059
2060
Error Box_iloc::append_data(heif_item_id item_ID,
2061
                            const std::vector<uint8_t>& data,
2062
                            uint8_t construction_method)
2063
0
{
2064
  // check whether this item ID already exists
2065
2066
0
  size_t idx;
2067
0
  for (idx = 0; idx < m_items.size(); idx++) {
2068
0
    if (m_items[idx].item_ID == item_ID) {
2069
0
      break;
2070
0
    }
2071
0
  }
2072
2073
  // item does not exist -> add a new one to the end
2074
2075
0
  if (idx == m_items.size()) {
2076
0
    Item item;
2077
0
    item.item_ID = item_ID;
2078
0
    item.construction_method = construction_method;
2079
2080
0
    m_items.push_back(item);
2081
0
  }
2082
2083
0
  if (m_items[idx].construction_method != construction_method) {
2084
    // TODO: return error: construction methods do not match
2085
0
  }
2086
2087
0
  Extent extent;
2088
0
  extent.length = data.size();
2089
2090
0
  if (m_use_tmpfile && construction_method==0) {
2091
0
#if !defined(_WIN32)
2092
0
    ssize_t cnt = ::write(m_tmpfile_fd, data.data(), data.size());
2093
#else
2094
    // TODO Currently unused code. Implement when needed.
2095
    assert(false);
2096
#  if 0
2097
    int cnt = _write(m_tmpfile_fd, data.data(), data.size());
2098
#  else
2099
    int cnt = -1;
2100
#  endif
2101
#endif
2102
0
    if (cnt < 0) {
2103
0
      std::stringstream sstr;
2104
0
      sstr << "Could not write to tmp file: error " << errno;
2105
0
      return {heif_error_Encoding_error,
2106
0
              heif_suberror_Unspecified,
2107
0
              sstr.str()};
2108
0
    }
2109
0
    else if ((size_t)cnt != data.size()) {
2110
0
      return {heif_error_Encoding_error,
2111
0
              heif_suberror_Unspecified,
2112
0
              "Could not write to tmp file (storage full?)"};
2113
0
    }
2114
0
  }
2115
0
  else {
2116
0
    if (!m_items[idx].extents.empty()) {
2117
0
      Extent& e = m_items[idx].extents.back();
2118
0
      e.data.insert(e.data.end(), data.begin(), data.end());
2119
0
      e.length = e.data.size();
2120
0
      return Error::Ok;
2121
0
    }
2122
2123
0
    extent.data = data;
2124
0
  }
2125
2126
0
  if (construction_method == 1) {
2127
0
    extent.offset = m_idat_offset;
2128
0
    extent.length = data.size();
2129
2130
0
    m_idat_offset += (int) data.size();
2131
0
  }
2132
2133
0
  m_items[idx].extents.push_back(std::move(extent));
2134
2135
0
  return Error::Ok;
2136
0
}
2137
2138
2139
Error Box_iloc::replace_data(heif_item_id item_ID,
2140
                             uint64_t output_offset,
2141
                             const std::vector<uint8_t>& data,
2142
                             uint8_t construction_method)
2143
0
{
2144
0
  assert(construction_method == 0); // TODO
2145
2146
  // check whether this item ID already exists
2147
2148
0
  size_t idx;
2149
0
  for (idx = 0; idx < m_items.size(); idx++) {
2150
0
    if (m_items[idx].item_ID == item_ID) {
2151
0
      break;
2152
0
    }
2153
0
  }
2154
2155
0
  assert(idx != m_items.size());
2156
2157
0
  uint64_t data_start = 0;
2158
0
  for (auto& extent : m_items[idx].extents) {
2159
0
    if (output_offset >= extent.data.size()) {
2160
0
      output_offset -= extent.data.size();
2161
0
    }
2162
0
    else {
2163
0
      uint64_t write_n = std::min(extent.data.size() - output_offset,
2164
0
                                  data.size() - data_start);
2165
0
      assert(write_n > 0);
2166
2167
0
      memcpy(extent.data.data() + output_offset, data.data() + data_start, write_n);
2168
2169
0
      data_start += write_n;
2170
0
      output_offset = 0;
2171
0
    }
2172
2173
0
    if (data_start == data.size()) {
2174
0
      break;
2175
0
    }
2176
0
  }
2177
2178
0
  return Error::Ok;
2179
0
}
2180
2181
2182
void Box_iloc::derive_box_version()
2183
0
{
2184
0
  int min_version = m_user_defined_min_version;
2185
2186
0
  if (m_items.size() > 0xFFFF) {
2187
0
    min_version = std::max(min_version, 2);
2188
0
  }
2189
2190
0
  m_offset_size = 0;
2191
0
  m_length_size = 0;
2192
0
  m_base_offset_size = 0;
2193
0
  m_index_size = 0;
2194
2195
0
  uint64_t total_data_size = 0;
2196
2197
0
  for (const auto& item : m_items) {
2198
    // check item_ID size
2199
0
    if (item.item_ID > 0xFFFF) {
2200
0
      min_version = std::max(min_version, 2);
2201
0
    }
2202
2203
    // check construction method
2204
0
    if (item.construction_method != 0) {
2205
0
      min_version = std::max(min_version, 1);
2206
0
    }
2207
2208
0
    total_data_size += item.extents[0].length;
2209
2210
    /* cannot compute this here because values are not set yet
2211
    // base offset size
2212
    if (item.base_offset > 0xFFFFFFFF) {
2213
      m_base_offset_size = std::max(m_base_offset_size, (uint8_t)8);
2214
    }
2215
    else if (item.base_offset > 0) {
2216
      m_base_offset_size = std::max(m_base_offset_size, (uint8_t)4);
2217
    }
2218
*/
2219
2220
    /*
2221
    for (const auto& extent : item.extents) {
2222
      // extent index size
2223
2224
      if (extent.index != 0) {
2225
        min_version = std::max(min_version, 1);
2226
        m_index_size = 4;
2227
      }
2228
2229
      if (extent.index > 0xFFFFFFFF) {
2230
        m_index_size = 8;
2231
      }
2232
2233
      // extent offset size
2234
      if (extent.offset > 0xFFFFFFFF) {
2235
        m_offset_size = 8;
2236
      }
2237
      else {
2238
        m_offset_size = 4;
2239
      }
2240
2241
      // extent length size
2242
      if (extent.length > 0xFFFFFFFF) {
2243
        m_length_size = 8;
2244
      }
2245
      else {
2246
        m_length_size = 4;
2247
      }
2248
    }
2249
      */
2250
0
  }
2251
2252
0
  uint64_t maximum_meta_box_size_guess = 0x10000000; // 256 MB
2253
0
  if (total_data_size + maximum_meta_box_size_guess > 0xFFFFFFFF) {
2254
0
    m_base_offset_size = 8;
2255
0
  }
2256
0
  else {
2257
0
    m_base_offset_size = 4;
2258
0
  }
2259
2260
0
  m_offset_size = 4;
2261
0
  m_length_size = 4;
2262
0
  m_index_size = 0;
2263
2264
  // extent.length is already known; extent.offset within an item equals
2265
  // the cumulative length of all preceding extents (they are written sequentially).
2266
  // base_offset absorbs the absolute file position, so extent.offset is relative.
2267
0
  for (const auto& item : m_items) {
2268
0
    uint64_t cumulative_offset = 0;
2269
0
    for (const auto& extent : item.extents) {
2270
0
      if (cumulative_offset > 0xFFFFFFFF) {
2271
0
        m_offset_size = 8;
2272
0
      }
2273
0
      if (extent.length > 0xFFFFFFFF) {
2274
0
        m_length_size = 8;
2275
0
      }
2276
0
      cumulative_offset += extent.length;
2277
0
    }
2278
0
  }
2279
2280
0
  set_version((uint8_t) min_version);
2281
0
}
2282
2283
2284
Error Box_iloc::write(StreamWriter& writer) const
2285
0
{
2286
  // --- write idat
2287
2288
0
  size_t sum_idat_size = 0;
2289
2290
0
  for (const auto& item : m_items) {
2291
0
    if (item.construction_method == 1) {
2292
0
      for (const auto& extent : item.extents) {
2293
0
        sum_idat_size += extent.data.size();
2294
0
      }
2295
0
    }
2296
0
  }
2297
2298
0
  if (sum_idat_size > 0) {
2299
0
    writer.write32((uint32_t) (sum_idat_size + 8));
2300
0
    writer.write32(fourcc("idat"));
2301
2302
0
    for (auto& item : m_items) {
2303
0
      if (item.construction_method == 1) {
2304
0
        for (auto& extent : item.extents) {
2305
0
          writer.write(extent.data);
2306
0
        }
2307
0
      }
2308
0
    }
2309
0
  }
2310
2311
2312
  // --- write iloc box
2313
2314
0
  size_t box_start = reserve_box_header_space(writer);
2315
2316
0
  m_iloc_box_start = writer.get_position();
2317
2318
0
  int nSkip = 0;
2319
2320
0
  nSkip += 2;
2321
0
  nSkip += (get_version() < 2) ? 2 : 4; // item_count
2322
2323
0
  for (const auto& item : m_items) {
2324
0
    nSkip += (get_version() < 2) ? 2 : 4; // item_ID
2325
0
    nSkip += (get_version() >= 1) ? 2 : 0; // construction method
2326
0
    nSkip += 4 + m_base_offset_size;
2327
2328
0
    for (const auto& extent : item.extents) {
2329
0
      (void) extent;
2330
2331
0
      if (get_version() >= 1) {
2332
0
        nSkip += m_index_size;
2333
0
      }
2334
2335
0
      nSkip += m_offset_size + m_length_size;
2336
0
    }
2337
0
  }
2338
2339
0
  writer.skip(nSkip);
2340
0
  prepend_header(writer, box_start);
2341
2342
0
  patch_iloc_header(writer); // Write iloc box. If there is an mdat, it will later be overwritten.
2343
2344
0
  return Error::Ok;
2345
0
}
2346
2347
2348
Error Box_iloc::write_mdat_after_iloc(StreamWriter& writer)
2349
0
{
2350
  // --- compute sum of all mdat data
2351
2352
0
  size_t sum_mdat_size = 0;
2353
2354
0
  for (const auto& item : m_items) {
2355
0
    if (item.construction_method == 0) {
2356
0
      for (const auto& extent : item.extents) {
2357
0
        sum_mdat_size += extent.length;
2358
0
      }
2359
0
    }
2360
0
  }
2361
2362
  // --- write mdat box
2363
2364
0
  if (sum_mdat_size <= 0xFFFFFFFF) {
2365
0
    writer.write32((uint32_t) (sum_mdat_size + 8));
2366
0
    writer.write32(fourcc("mdat"));
2367
0
  }
2368
0
  else {
2369
    // box size > 4 GB
2370
2371
0
    writer.write32(1);
2372
0
    writer.write32(fourcc("mdat"));
2373
0
    writer.write64(sum_mdat_size+8+8);
2374
0
  }
2375
2376
0
  if (m_use_tmpfile) {
2377
0
    ::lseek(m_tmpfile_fd, 0, SEEK_SET);
2378
0
  }
2379
2380
0
  for (auto& item : m_items) {
2381
0
    if (item.construction_method == 0) {
2382
0
      item.base_offset = writer.get_position();
2383
2384
0
      for (auto& extent : item.extents) {
2385
0
        extent.offset = writer.get_position() - item.base_offset;
2386
        //extent.length = extent.data.size();
2387
2388
0
        if (m_use_tmpfile) {
2389
0
          std::vector<uint8_t> data(extent.length);
2390
0
#if !defined(_WIN32)
2391
0
          ssize_t cnt = ::read(m_tmpfile_fd, data.data(), extent.length);
2392
#else
2393
          // TODO Currently unused code. Implement when needed.
2394
          assert(false);
2395
# if 0
2396
          int cnt = _read(m_tmpfile_fd, data.data(), extent.length);
2397
# else
2398
          int cnt = -1;
2399
# endif
2400
#endif
2401
0
          if (cnt<0) {
2402
0
            std::stringstream sstr;
2403
0
            sstr << "Cannot read tmp data file, error " << errno;
2404
0
            return {heif_error_Encoding_error,
2405
0
                    heif_suberror_Unspecified,
2406
0
                    sstr.str()};
2407
0
          }
2408
0
          else if ((uint64_t)cnt != extent.length) {
2409
0
            return {heif_error_Encoding_error,
2410
0
                    heif_suberror_Unspecified,
2411
0
                    "Tmp data could not be read completely"};
2412
0
          }
2413
0
          writer.write(data);
2414
0
        }
2415
0
        else {
2416
0
          writer.write(extent.data);
2417
0
        }
2418
0
      }
2419
0
    }
2420
0
  }
2421
2422
2423
  // --- patch iloc box
2424
2425
0
  patch_iloc_header(writer);
2426
2427
0
  return Error::Ok;
2428
0
}
2429
2430
2431
void Box_iloc::patch_iloc_header(StreamWriter& writer) const
2432
0
{
2433
0
  size_t old_pos = writer.get_position();
2434
0
  writer.set_position(m_iloc_box_start);
2435
2436
0
  writer.write8((uint8_t) ((m_offset_size << 4) | (m_length_size)));
2437
0
  writer.write8((uint8_t) ((m_base_offset_size << 4) | (m_index_size)));
2438
2439
0
  if (get_version() < 2) {
2440
0
    writer.write16((uint16_t) m_items.size());
2441
0
  }
2442
0
  else {
2443
0
    writer.write32((uint32_t) m_items.size());
2444
0
  }
2445
2446
0
  for (const auto& item : m_items) {
2447
0
    if (get_version() < 2) {
2448
0
      writer.write16((uint16_t) item.item_ID);
2449
0
    }
2450
0
    else {
2451
0
      writer.write32((uint32_t) item.item_ID);
2452
0
    }
2453
2454
0
    if (get_version() >= 1) {
2455
0
      writer.write16(item.construction_method);
2456
0
    }
2457
2458
0
    writer.write16(item.data_reference_index);
2459
0
    if (m_base_offset_size > 0) {
2460
0
      writer.write(m_base_offset_size, item.base_offset);
2461
0
    }
2462
0
    else {
2463
0
      assert(item.base_offset == 0);
2464
0
    }
2465
0
    writer.write16((uint16_t) item.extents.size());
2466
2467
0
    for (const auto& extent : item.extents) {
2468
0
      if (get_version() >= 1 && m_index_size > 0) {
2469
0
        writer.write(m_index_size, extent.index);
2470
0
      }
2471
2472
0
      writer.write(m_offset_size, extent.offset);
2473
0
      writer.write(m_length_size, extent.length);
2474
0
    }
2475
0
  }
2476
2477
0
  writer.set_position(old_pos);
2478
0
}
2479
2480
2481
/*
2482
 *                     version <= 1    version 2   version > 2    mime     uri
2483
 * -----------------------------------------------------------------------------------------------
2484
 * item id               16               16           32          16/32   16/32
2485
 * protection index      16               16           16          16      16
2486
 * item type             -                yes          yes         yes     yes
2487
 * item name             yes              yes          yes         yes     yes
2488
 * content type          yes              -            -           yes     -
2489
 * content encoding      yes              -            -           yes     -
2490
 * hidden item           -                yes          yes         yes     yes
2491
 * item uri type         -                -            -           -       yes
2492
 *
2493
 * Note: HEIF does not allow version 0 and version 1 boxes ! (see 23008-12, 10.2.1)
2494
 */
2495
2496
Error Box_infe::parse(BitstreamRange& range, const heif_security_limits* limits)
2497
69.0k
{
2498
69.0k
  parse_full_box_header(range);
2499
2500
  // only versions 2,3 are required by HEIF
2501
69.0k
  if (get_version() > 3) {
2502
2
    return unsupported_version_error("infe");
2503
2
  }
2504
2505
69.0k
  if (get_version() <= 1) {
2506
324
    m_item_ID = range.read16();
2507
324
    m_item_protection_index = range.read16();
2508
2509
324
    m_item_name = range.read_string();
2510
324
    m_content_type = range.read_string();
2511
324
    m_content_encoding = range.read_string();
2512
324
  }
2513
2514
69.0k
  m_item_type_4cc = 0;
2515
2516
69.0k
  if (get_version() >= 2) {
2517
68.7k
    m_hidden_item = (get_flags() & 1);
2518
2519
68.7k
    if (get_version() == 2) {
2520
68.6k
      m_item_ID = range.read16();
2521
68.6k
    }
2522
91
    else {
2523
91
      m_item_ID = range.read32();
2524
91
    }
2525
2526
68.7k
    m_item_protection_index = range.read16();
2527
68.7k
    m_item_type_4cc = range.read32();
2528
2529
68.7k
    m_item_name = range.read_string();
2530
68.7k
    if (m_item_type_4cc == fourcc("mime")) {
2531
107
      m_content_type = range.read_string();
2532
107
      m_content_encoding = range.read_string();
2533
107
    }
2534
68.6k
    else if (m_item_type_4cc == fourcc("uri ")) {
2535
73
      m_item_uri_type = range.read_string();
2536
73
    }
2537
68.7k
  }
2538
2539
69.0k
  return range.get_error();
2540
69.0k
}
2541
2542
2543
void Box_infe::derive_box_version()
2544
0
{
2545
0
  int min_version = 0;
2546
2547
0
  if (m_hidden_item) {
2548
0
    min_version = std::max(min_version, 2);
2549
0
  }
2550
2551
0
  if (m_item_ID > 0xFFFF) {
2552
0
    min_version = std::max(min_version, 3);
2553
0
  }
2554
2555
2556
0
  if (m_item_type_4cc != 0) {
2557
0
    min_version = std::max(min_version, 2);
2558
0
  }
2559
2560
0
  set_version((uint8_t) min_version);
2561
0
}
2562
2563
2564
void Box_infe::set_hidden_item(bool hidden)
2565
0
{
2566
0
  m_hidden_item = hidden;
2567
2568
0
  if (m_hidden_item) {
2569
0
    set_flags(get_flags() | 1U);
2570
0
  }
2571
0
  else {
2572
0
    set_flags(get_flags() & ~1U);
2573
0
  }
2574
0
}
2575
2576
Error Box_infe::write(StreamWriter& writer) const
2577
0
{
2578
0
  size_t box_start = reserve_box_header_space(writer);
2579
2580
0
  if (get_version() <= 1) {
2581
0
    writer.write16((uint16_t) m_item_ID);
2582
0
    writer.write16(m_item_protection_index);
2583
2584
0
    writer.write(m_item_name);
2585
0
    writer.write(m_content_type);
2586
0
    writer.write(m_content_encoding);
2587
0
  }
2588
2589
0
  if (get_version() >= 2) {
2590
0
    if (get_version() == 2) {
2591
0
      writer.write16((uint16_t) m_item_ID);
2592
0
    }
2593
0
    else if (get_version() == 3) {
2594
0
      writer.write32(m_item_ID);
2595
0
    }
2596
2597
0
    writer.write16(m_item_protection_index);
2598
2599
0
    writer.write32(m_item_type_4cc);
2600
2601
0
    writer.write(m_item_name);
2602
0
    if (m_item_type_4cc == fourcc("mime")) {
2603
0
      writer.write(m_content_type);
2604
0
      writer.write(m_content_encoding);
2605
0
    }
2606
0
    else if (m_item_type_4cc == fourcc("uri ")) {
2607
0
      writer.write(m_item_uri_type);
2608
0
    }
2609
0
  }
2610
2611
0
  prepend_header(writer, box_start);
2612
2613
0
  return Error::Ok;
2614
0
}
2615
2616
2617
void Box_infe::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2618
0
{
2619
0
  Box::dump(sstr, indent, full_log);
2620
2621
0
  sstr << indent << "item_ID: " << m_item_ID << "\n"
2622
0
       << indent << "item_protection_index: " << m_item_protection_index << "\n"
2623
0
       << indent << "item_type: " << fourcc_to_string(m_item_type_4cc) << "\n"
2624
0
       << indent << "item_name: " << m_item_name << "\n";
2625
2626
0
  if (m_item_type_4cc == fourcc("mime")) {
2627
0
    sstr << indent << "content_type: " << m_content_type << "\n"
2628
0
         << indent << "content_encoding: " << m_content_encoding << "\n";
2629
0
  }
2630
2631
0
  if (m_item_type_4cc == fourcc("uri ")) {
2632
0
    sstr << indent << "item uri type: " << m_item_uri_type << "\n";
2633
0
  }
2634
2635
0
  sstr << indent << "hidden item: " << std::boolalpha << m_hidden_item << "\n";
2636
2637
0
  return;
2638
0
}
2639
2640
2641
Error Box_iinf::parse(BitstreamRange& range, const heif_security_limits* limits)
2642
21.3k
{
2643
21.3k
  parse_full_box_header(range);
2644
2645
  // TODO: there are several images in circulation that have an iinf version=2. We should not enforce this with a hard error.
2646
21.3k
  if (false && get_version() > 1) {
2647
0
    return unsupported_version_error("iinf");
2648
0
  }
2649
2650
21.3k
  int nEntries_size = (get_version() > 0) ? 4 : 2;
2651
2652
21.3k
  uint32_t item_count;
2653
21.3k
  if (nEntries_size == 2) {
2654
21.3k
    item_count = range.read16();
2655
21.3k
  }
2656
50
  else {
2657
50
    item_count = range.read32();
2658
50
  }
2659
2660
21.3k
  if (item_count == 0) {
2661
120
    return Error::Ok;
2662
120
  }
2663
2664
  // Sanity check.
2665
21.2k
  if (limits->max_items && item_count > limits->max_items) {
2666
70
    std::stringstream sstr;
2667
70
    sstr << "iinf box contains " << item_count << " items, which exceeds the security limit of "
2668
70
         << limits->max_items << " items.";
2669
2670
70
    return Error(heif_error_Memory_allocation_error,
2671
70
                 heif_suberror_Security_limit_exceeded,
2672
70
                 sstr.str());
2673
70
  }
2674
2675
21.1k
  return read_children(range, item_count, limits);
2676
21.2k
}
2677
2678
2679
void Box_iinf::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2680
0
{
2681
0
  Box::dump(sstr, indent, full_log);
2682
2683
0
  dump_children(sstr, indent, full_log);
2684
2685
0
  return;
2686
0
}
2687
2688
2689
Error Box_iprp::parse(BitstreamRange& range, const heif_security_limits* limits)
2690
21.2k
{
2691
  //parse_full_box_header(range);
2692
2693
21.2k
  return read_children(range, READ_CHILDREN_ALL, limits);
2694
21.2k
}
2695
2696
2697
void Box_iinf::derive_box_version()
2698
0
{
2699
0
  if (m_children.size() > 0xFFFF) {
2700
0
    set_version(1);
2701
0
  }
2702
0
  else {
2703
0
    set_version(0);
2704
0
  }
2705
0
}
2706
2707
2708
Error Box_iinf::write(StreamWriter& writer) const
2709
0
{
2710
0
  size_t box_start = reserve_box_header_space(writer);
2711
2712
0
  int nEntries_size = (get_version() > 0) ? 4 : 2;
2713
2714
0
  writer.write(nEntries_size, m_children.size());
2715
2716
2717
0
  Error err = write_children(writer);
2718
2719
0
  prepend_header(writer, box_start);
2720
2721
0
  return err;
2722
0
}
2723
2724
2725
void Box_iprp::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2726
0
{
2727
0
  Box::dump(sstr, indent, full_log);
2728
2729
0
  dump_children(sstr, indent, full_log);
2730
2731
0
  return;
2732
0
}
2733
2734
2735
uint32_t Box_ipco::find_or_append_child_box(const std::shared_ptr<Box>& box)
2736
42.0k
{
2737
119k
  for (uint32_t i = 0; i < (uint32_t) m_children.size(); i++) {
2738
119k
    if (Box::equal(m_children[i], box)) {
2739
41.8k
      return i;
2740
41.8k
    }
2741
119k
  }
2742
237
  return append_child_box(box);
2743
42.0k
}
2744
2745
2746
Error Box_ipco::parse(BitstreamRange& range, const heif_security_limits* limits)
2747
21.0k
{
2748
  //parse_full_box_header(range);
2749
2750
21.0k
  return read_children(range, READ_CHILDREN_ALL, limits);
2751
21.0k
}
2752
2753
2754
void Box_ipco::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2755
0
{
2756
0
  Box::dump(sstr, indent, full_log);
2757
2758
0
  dump_children(sstr, indent, full_log, true);
2759
2760
0
  return;
2761
0
}
2762
2763
2764
Error Box_pixi::parse(BitstreamRange& range, const heif_security_limits* limits)
2765
12.7k
{
2766
12.7k
  parse_full_box_header(range);
2767
2768
12.7k
  if (get_version() != 0) {
2769
361
    return unsupported_version_error("pixi");
2770
361
  }
2771
2772
12.3k
  StreamReader::grow_status status;
2773
12.3k
  uint8_t num_channels = range.read8();
2774
12.3k
  status = range.wait_for_available_bytes(num_channels);
2775
12.3k
  if (status != StreamReader::grow_status::size_reached) {
2776
    // TODO: return recoverable error at timeout
2777
160
    return Error(heif_error_Invalid_input,
2778
160
                 heif_suberror_End_of_data);
2779
160
  }
2780
2781
12.2k
  m_bits_per_channel.resize(num_channels);
2782
60.1k
  for (int i = 0; i < num_channels; i++) {
2783
47.8k
    m_bits_per_channel[i] = range.read8();
2784
47.8k
  }
2785
2786
12.2k
  return range.get_error();
2787
12.3k
}
2788
2789
2790
void Box_pixi::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2791
0
{
2792
0
  Box::dump(sstr, indent, full_log);
2793
2794
0
  sstr << indent << "bits_per_channel: ";
2795
2796
0
  for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
2797
0
    if (i > 0) sstr << ",";
2798
0
    sstr << ((int) m_bits_per_channel[i]);
2799
0
  }
2800
2801
0
  sstr << "\n";
2802
2803
0
  return;
2804
0
}
2805
2806
2807
Error Box_pixi::write(StreamWriter& writer) const
2808
0
{
2809
0
  size_t box_start = reserve_box_header_space(writer);
2810
2811
0
  if (m_bits_per_channel.size() > 255 ||
2812
0
      m_bits_per_channel.empty()) {
2813
    // TODO: error
2814
0
    assert(false);
2815
0
  }
2816
2817
0
  writer.write8((uint8_t) (m_bits_per_channel.size()));
2818
0
  for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
2819
0
    writer.write8(m_bits_per_channel[i]);
2820
0
  }
2821
2822
0
  prepend_header(writer, box_start);
2823
2824
0
  return Error::Ok;
2825
0
}
2826
2827
2828
Error Box_pasp::parse(BitstreamRange& range, const heif_security_limits* limits)
2829
1.01k
{
2830
  //parse_full_box_header(range);
2831
2832
1.01k
  hSpacing = range.read32();
2833
1.01k
  vSpacing = range.read32();
2834
2835
1.01k
  return range.get_error();
2836
1.01k
}
2837
2838
2839
void Box_pasp::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2840
0
{
2841
0
  Box::dump(sstr, indent, full_log);
2842
2843
0
  sstr << indent << "hSpacing: " << hSpacing << "\n";
2844
0
  sstr << indent << "vSpacing: " << vSpacing << "\n";
2845
2846
0
  return;
2847
0
}
2848
2849
2850
Error Box_pasp::write(StreamWriter& writer) const
2851
0
{
2852
0
  size_t box_start = reserve_box_header_space(writer);
2853
2854
0
  writer.write32(hSpacing);
2855
0
  writer.write32(vSpacing);
2856
2857
0
  prepend_header(writer, box_start);
2858
2859
0
  return Error::Ok;
2860
0
}
2861
2862
2863
Error Box_lsel::parse(BitstreamRange& range, const heif_security_limits* limits)
2864
27
{
2865
27
  layer_id = range.read16();
2866
2867
27
  return range.get_error();
2868
27
}
2869
2870
2871
void Box_lsel::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2872
0
{
2873
0
  Box::dump(sstr, indent, full_log);
2874
2875
0
  sstr << indent << "layer_id: " << layer_id << "\n";
2876
2877
0
  return;
2878
0
}
2879
2880
2881
Error Box_lsel::write(StreamWriter& writer) const
2882
0
{
2883
0
  size_t box_start = reserve_box_header_space(writer);
2884
2885
0
  writer.write16(layer_id);
2886
2887
0
  prepend_header(writer, box_start);
2888
2889
0
  return Error::Ok;
2890
0
}
2891
2892
2893
Error Box_clli::parse(BitstreamRange& range, const heif_security_limits* limits)
2894
12
{
2895
  //parse_full_box_header(range);
2896
2897
12
  clli.max_content_light_level = range.read16();
2898
12
  clli.max_pic_average_light_level = range.read16();
2899
2900
12
  return range.get_error();
2901
12
}
2902
2903
2904
void Box_clli::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2905
0
{
2906
0
  Box::dump(sstr, indent, full_log);
2907
2908
0
  sstr << indent << "max_content_light_level: " << clli.max_content_light_level << "\n";
2909
0
  sstr << indent << "max_pic_average_light_level: " << clli.max_pic_average_light_level << "\n";
2910
2911
0
  return;
2912
0
}
2913
2914
2915
Error Box_clli::write(StreamWriter& writer) const
2916
0
{
2917
0
  size_t box_start = reserve_box_header_space(writer);
2918
2919
0
  writer.write16(clli.max_content_light_level);
2920
0
  writer.write16(clli.max_pic_average_light_level);
2921
2922
0
  prepend_header(writer, box_start);
2923
2924
0
  return Error::Ok;
2925
0
}
2926
2927
2928
Box_mdcv::Box_mdcv()
2929
846
{
2930
846
  set_short_type(fourcc("mdcv"));
2931
2932
846
  memset(&mdcv, 0, sizeof(heif_mastering_display_colour_volume));
2933
846
}
2934
2935
2936
Error Box_mdcv::parse(BitstreamRange& range, const heif_security_limits* limits)
2937
11
{
2938
  //parse_full_box_header(range);
2939
2940
44
  for (int c = 0; c < 3; c++) {
2941
33
    mdcv.display_primaries_x[c] = range.read16();
2942
33
    mdcv.display_primaries_y[c] = range.read16();
2943
33
  }
2944
2945
11
  mdcv.white_point_x = range.read16();
2946
11
  mdcv.white_point_y = range.read16();
2947
11
  mdcv.max_display_mastering_luminance = range.read32();
2948
11
  mdcv.min_display_mastering_luminance = range.read32();
2949
2950
11
  return range.get_error();
2951
11
}
2952
2953
2954
void Box_mdcv::dump(std::ostream& sstr, Indent& indent, bool full_log) const
2955
0
{
2956
0
  Box::dump(sstr, indent, full_log);
2957
2958
0
  sstr << indent << "display_primaries (x,y): ";
2959
0
  sstr << "(" << mdcv.display_primaries_x[0] << ";" << mdcv.display_primaries_y[0] << "), ";
2960
0
  sstr << "(" << mdcv.display_primaries_x[1] << ";" << mdcv.display_primaries_y[1] << "), ";
2961
0
  sstr << "(" << mdcv.display_primaries_x[2] << ";" << mdcv.display_primaries_y[2] << ")\n";
2962
2963
0
  sstr << indent << "white point (x,y): (" << mdcv.white_point_x << ";" << mdcv.white_point_y << ")\n";
2964
0
  sstr << indent << "max display mastering luminance: " << mdcv.max_display_mastering_luminance << "\n";
2965
0
  sstr << indent << "min display mastering luminance: " << mdcv.min_display_mastering_luminance << "\n";
2966
2967
0
  return;
2968
0
}
2969
2970
2971
Error Box_mdcv::write(StreamWriter& writer) const
2972
0
{
2973
0
  size_t box_start = reserve_box_header_space(writer);
2974
2975
0
  for (int c = 0; c < 3; c++) {
2976
0
    writer.write16(mdcv.display_primaries_x[c]);
2977
0
    writer.write16(mdcv.display_primaries_y[c]);
2978
0
  }
2979
2980
0
  writer.write16(mdcv.white_point_x);
2981
0
  writer.write16(mdcv.white_point_y);
2982
2983
0
  writer.write32(mdcv.max_display_mastering_luminance);
2984
0
  writer.write32(mdcv.min_display_mastering_luminance);
2985
2986
0
  prepend_header(writer, box_start);
2987
2988
0
  return Error::Ok;
2989
0
}
2990
2991
Box_amve::Box_amve()
2992
703
{
2993
703
  set_short_type(fourcc("amve"));
2994
2995
  // These values are not valid.
2996
703
  amve.ambient_illumination = 0;
2997
703
  amve.ambient_light_x = 0;
2998
703
  amve.ambient_light_y = 0;
2999
703
}
3000
3001
Error Box_amve::parse(BitstreamRange& range, const heif_security_limits* limits)
3002
38
{
3003
38
  amve.ambient_illumination = range.read32();
3004
38
  amve.ambient_light_x = range.read16();
3005
38
  amve.ambient_light_y = range.read16();
3006
3007
38
  return range.get_error();
3008
38
}
3009
3010
3011
void Box_amve::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3012
0
{
3013
0
  Box::dump(sstr, indent, full_log);
3014
3015
0
  sstr << indent << "ambient_illumination: " << amve.ambient_illumination << "\n";
3016
0
  sstr << indent << "ambient_light_x: " << amve.ambient_light_x << "\n";
3017
0
  sstr << indent << "ambient_light_y: " << amve.ambient_light_y << "\n";
3018
3019
0
  return;
3020
0
}
3021
3022
3023
Error Box_amve::write(StreamWriter& writer) const
3024
0
{
3025
0
  size_t box_start = reserve_box_header_space(writer);
3026
3027
0
  writer.write32(amve.ambient_illumination);
3028
0
  writer.write16(amve.ambient_light_x);
3029
0
  writer.write16(amve.ambient_light_y);
3030
3031
0
  prepend_header(writer, box_start);
3032
3033
0
  return Error::Ok;
3034
0
}
3035
3036
3037
Error Box_ndwt::parse(BitstreamRange& range, const heif_security_limits* limits)
3038
18
{
3039
18
  parse_full_box_header(range);
3040
3041
18
  if (get_version() != 0) {
3042
7
    return unsupported_version_error("ndwt");
3043
7
  }
3044
3045
11
  m_diffuse_white_luminance = range.read32();
3046
3047
11
  return range.get_error();
3048
18
}
3049
3050
3051
void Box_ndwt::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3052
0
{
3053
0
  Box::dump(sstr, indent, full_log);
3054
3055
0
  sstr << indent << "diffuse_white_luminance: " << m_diffuse_white_luminance << "\n";
3056
3057
0
  return;
3058
0
}
3059
3060
3061
Error Box_ndwt::write(StreamWriter& writer) const
3062
0
{
3063
0
  size_t box_start = reserve_box_header_space(writer);
3064
3065
0
  writer.write32(m_diffuse_white_luminance);
3066
3067
0
  prepend_header(writer, box_start);
3068
3069
0
  return Error::Ok;
3070
0
}
3071
3072
3073
Box_cclv::Box_cclv()
3074
964
{
3075
964
  set_short_type(fourcc("cclv"));
3076
3077
964
  m_ccv_primaries_valid = false;
3078
964
}
3079
3080
3081
void Box_cclv::set_primaries(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
3082
175
{
3083
175
  m_ccv_primaries_valid = true;
3084
175
  m_ccv_primaries_x[0] = x0;
3085
175
  m_ccv_primaries_y[0] = y0;
3086
175
  m_ccv_primaries_x[1] = x1;
3087
175
  m_ccv_primaries_y[1] = y1;
3088
175
  m_ccv_primaries_x[2] = x2;
3089
175
  m_ccv_primaries_y[2] = y2;
3090
175
}
3091
3092
3093
Error Box_cclv::parse(BitstreamRange& range, const heif_security_limits* limits)
3094
0
{
3095
0
  uint8_t flags = range.read8();
3096
3097
0
  m_ccv_primaries_valid = (flags & 0b00100000);
3098
0
  bool ccv_min_luminance_valid = (flags & 0b00010000);
3099
0
  bool ccv_max_luminance_valid = (flags & 0b00001000);
3100
0
  bool ccv_avg_luminance_valid = (flags & 0b00000100);
3101
3102
0
  if (m_ccv_primaries_valid) {
3103
0
    for (int c = 0; c < 3; c++) {
3104
0
      m_ccv_primaries_x[c] = range.read32s();
3105
0
      m_ccv_primaries_y[c] = range.read32s();
3106
0
    }
3107
0
  }
3108
3109
0
  if (ccv_min_luminance_valid) {
3110
0
    m_ccv_min_luminance_value = range.read32();
3111
0
  }
3112
3113
0
  if (ccv_max_luminance_valid) {
3114
0
    m_ccv_max_luminance_value = range.read32();
3115
0
  }
3116
3117
0
  if (ccv_avg_luminance_valid) {
3118
0
    m_ccv_avg_luminance_value = range.read32();
3119
0
  }
3120
3121
0
  return range.get_error();
3122
0
}
3123
3124
3125
template <typename T> std::ostream& operator<<(std::ostream& ostr, const std::optional<T>& value)
3126
0
{
3127
0
  if (value) {
3128
0
    ostr << *value;
3129
0
  }
3130
0
  else {
3131
0
    ostr << "-";
3132
0
  }
3133
3134
0
  return ostr;
3135
0
}
3136
3137
3138
void Box_cclv::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3139
0
{
3140
0
  Box::dump(sstr, indent, full_log);
3141
3142
0
  sstr << indent << "ccv_primaries_present_flag: " << m_ccv_primaries_valid << "\n";
3143
0
  if (m_ccv_primaries_valid) {
3144
0
    sstr << indent << "ccv_primaries (x,y): ";
3145
0
    sstr << "(" << m_ccv_primaries_x[0] << ";" << m_ccv_primaries_y[0] << "), ";
3146
0
    sstr << "(" << m_ccv_primaries_x[1] << ";" << m_ccv_primaries_y[1] << "), ";
3147
0
    sstr << "(" << m_ccv_primaries_x[2] << ";" << m_ccv_primaries_y[2] << ")\n";
3148
0
  }
3149
3150
0
  sstr << indent << "ccv_min_luminance_value: " << m_ccv_min_luminance_value << "\n";
3151
0
  sstr << indent << "ccv_max_luminance_value: " << m_ccv_max_luminance_value << "\n";
3152
0
  sstr << indent << "ccv_avg_luminance_value: " << m_ccv_avg_luminance_value << "\n";
3153
3154
0
  return;
3155
0
}
3156
3157
3158
Error Box_cclv::write(StreamWriter& writer) const
3159
0
{
3160
0
  size_t box_start = reserve_box_header_space(writer);
3161
3162
0
  uint8_t flags = 0;
3163
0
  flags |= m_ccv_primaries_valid     ? uint8_t{0b00100000} : uint8_t{0};
3164
0
  flags |= m_ccv_min_luminance_value ? uint8_t{0b00010000} : uint8_t{0};
3165
0
  flags |= m_ccv_max_luminance_value ? uint8_t{0b00001000} : uint8_t{0};
3166
0
  flags |= m_ccv_avg_luminance_value ? uint8_t{0b00000100} : uint8_t{0};
3167
0
  writer.write8(flags);
3168
3169
0
  if (m_ccv_primaries_valid) {
3170
0
    for (int c = 0; c < 3; c++) {
3171
0
      writer.write32s(m_ccv_primaries_x[c]);
3172
0
      writer.write32s(m_ccv_primaries_y[c]);
3173
0
    }
3174
0
  }
3175
3176
0
  if (m_ccv_min_luminance_value) {
3177
0
    writer.write32(*m_ccv_min_luminance_value);
3178
0
  }
3179
3180
0
  if (m_ccv_max_luminance_value) {
3181
0
    writer.write32(*m_ccv_max_luminance_value);
3182
0
  }
3183
3184
0
  if (m_ccv_avg_luminance_value) {
3185
0
    writer.write32(*m_ccv_avg_luminance_value);
3186
0
  }
3187
3188
0
  prepend_header(writer, box_start);
3189
3190
0
  return Error::Ok;
3191
0
}
3192
3193
3194
Error Box_ipco::get_properties_for_item_ID(uint32_t itemID,
3195
                                           const std::shared_ptr<class Box_ipma>& ipma,
3196
                                           std::vector<std::shared_ptr<Box>>& out_properties) const
3197
208k
{
3198
208k
  const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
3199
208k
  if (property_assoc == nullptr) {
3200
2.35k
    std::stringstream sstr;
3201
2.35k
    sstr << "Item (ID=" << itemID << ") has no properties assigned to it in ipma box";
3202
3203
2.35k
    return Error(heif_error_Invalid_input,
3204
2.35k
                 heif_suberror_No_properties_assigned_to_item,
3205
2.35k
                 sstr.str());
3206
2.35k
  }
3207
3208
205k
  const auto& allProperties = get_all_child_boxes();
3209
698k
  for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
3210
698k
    if (assoc.property_index > allProperties.size()) {
3211
319
      std::stringstream sstr;
3212
319
      sstr << "Nonexisting property (index=" << assoc.property_index << ") for item "
3213
319
           << " ID=" << itemID << " referenced in ipma box";
3214
3215
319
      return Error(heif_error_Invalid_input,
3216
319
                   heif_suberror_Ipma_box_references_nonexisting_property,
3217
319
                   sstr.str());
3218
319
    }
3219
3220
698k
    if (assoc.property_index > 0) {
3221
696k
      out_properties.push_back(allProperties[assoc.property_index - 1]);
3222
696k
    }
3223
698k
  }
3224
3225
205k
  return Error::Ok;
3226
205k
}
3227
3228
3229
std::shared_ptr<Box> Box_ipco::get_property_for_item_ID(heif_item_id itemID,
3230
                                                        const std::shared_ptr<class Box_ipma>& ipma,
3231
                                                        uint32_t box_type) const
3232
39.0k
{
3233
39.0k
  const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
3234
39.0k
  if (property_assoc == nullptr) {
3235
0
    return nullptr;
3236
0
  }
3237
3238
39.0k
  const auto& allProperties = get_all_child_boxes();
3239
42.8k
  for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
3240
42.8k
    if (assoc.property_index > allProperties.size() ||
3241
42.8k
        assoc.property_index == 0) {
3242
2
      return nullptr;
3243
2
    }
3244
3245
42.8k
    const auto& property = allProperties[assoc.property_index - 1];
3246
42.8k
    if (property->get_short_type() == box_type) {
3247
39.0k
      return property;
3248
39.0k
    }
3249
42.8k
  }
3250
3251
0
  return nullptr;
3252
39.0k
}
3253
3254
3255
bool Box_ipco::is_property_essential_for_item(heif_item_id itemId,
3256
                                              const std::shared_ptr<const class Box>& property,
3257
                                              const std::shared_ptr<class Box_ipma>& ipma) const
3258
16.0k
{
3259
  // find property index
3260
3261
48.3k
  for (int i = 0; i < (int) m_children.size(); i++) {
3262
48.3k
    if (m_children[i] == property) {
3263
16.0k
      return ipma->is_property_essential_for_item(itemId, i + 1);
3264
16.0k
    }
3265
48.3k
  }
3266
3267
16.0k
  assert(false); // non-existing property
3268
0
  return false;
3269
0
}
3270
3271
3272
Error Box_ispe::parse(BitstreamRange& range, const heif_security_limits* limits)
3273
23.8k
{
3274
23.8k
  parse_full_box_header(range);
3275
3276
23.8k
  if (get_version() != 0) {
3277
4
    return unsupported_version_error("ispe");
3278
4
  }
3279
3280
23.8k
  m_image_width = range.read32();
3281
23.8k
  m_image_height = range.read32();
3282
3283
23.8k
  return range.get_error();
3284
23.8k
}
3285
3286
3287
void Box_ispe::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3288
0
{
3289
0
  Box::dump(sstr, indent, full_log);
3290
3291
0
  sstr << indent << "image width: " << m_image_width << "\n"
3292
0
       << indent << "image height: " << m_image_height << "\n";
3293
3294
0
  return;
3295
0
}
3296
3297
3298
Error Box_ispe::write(StreamWriter& writer) const
3299
0
{
3300
0
  size_t box_start = reserve_box_header_space(writer);
3301
3302
0
  writer.write32(m_image_width);
3303
0
  writer.write32(m_image_height);
3304
3305
0
  prepend_header(writer, box_start);
3306
3307
0
  return Error::Ok;
3308
0
}
3309
3310
3311
bool Box_ispe::operator==(const Box& other) const
3312
34.9k
{
3313
34.9k
  const auto* other_ispe = dynamic_cast<const Box_ispe*>(&other);
3314
34.9k
  if (other_ispe == nullptr) {
3315
34.9k
    return false;
3316
34.9k
  }
3317
3318
0
  return (m_image_width == other_ispe->m_image_width &&
3319
0
          m_image_height == other_ispe->m_image_height);
3320
34.9k
}
3321
3322
3323
Error Box_ipma::parse(BitstreamRange& range, const heif_security_limits* limits)
3324
20.7k
{
3325
20.7k
  parse_full_box_header(range);
3326
3327
  // TODO: is there any specification of allowed values for the ipma version in the HEIF standards?
3328
3329
20.7k
  if (get_version() > 1) {
3330
3
    return unsupported_version_error("ipma");
3331
3
  }
3332
3333
20.7k
  uint32_t entry_cnt = range.read32();
3334
3335
20.7k
  if (limits->max_items && entry_cnt > limits->max_items) {
3336
24
    std::stringstream sstr;
3337
24
    sstr << "ipma box wants to define properties for " << entry_cnt
3338
24
         << " items, but the security limit has been set to " << limits->max_items << " items";
3339
24
    return {heif_error_Invalid_input,
3340
24
            heif_suberror_Security_limit_exceeded,
3341
24
            sstr.str()};
3342
24
  }
3343
3344
84.8k
  for (uint32_t i = 0; i < entry_cnt && !range.error() && !range.eof(); i++) {
3345
64.1k
    Entry entry;
3346
64.1k
    if (get_version() < 1) {
3347
63.6k
      entry.item_ID = range.read16();
3348
63.6k
    }
3349
521
    else {
3350
521
      entry.item_ID = range.read32();
3351
521
    }
3352
3353
64.1k
    int assoc_cnt = range.read8();
3354
287k
    for (int k = 0; k < assoc_cnt; k++) {
3355
223k
      PropertyAssociation association{};
3356
3357
223k
      uint16_t index;
3358
223k
      if (get_flags() & 1) {
3359
894
        index = range.read16();
3360
894
        association.essential = !!(index & 0x8000);
3361
894
        association.property_index = (index & 0x7fff);
3362
894
      }
3363
222k
      else {
3364
222k
        index = range.read8();
3365
222k
        association.essential = !!(index & 0x80);
3366
222k
        association.property_index = (index & 0x7f);
3367
222k
      }
3368
3369
223k
      entry.associations.push_back(association);
3370
223k
    }
3371
3372
64.1k
    m_entries.push_back(entry);
3373
64.1k
  }
3374
3375
20.7k
  return range.get_error();
3376
20.7k
}
3377
3378
3379
const std::vector<Box_ipma::PropertyAssociation>* Box_ipma::get_properties_for_item_ID(uint32_t itemID) const
3380
247k
{
3381
3.67M
  for (const auto& entry : m_entries) {
3382
3.67M
    if (entry.item_ID == itemID) {
3383
244k
      return &entry.associations;
3384
244k
    }
3385
3.67M
  }
3386
3387
2.35k
  return nullptr;
3388
247k
}
3389
3390
3391
bool Box_ipma::is_property_essential_for_item(heif_item_id itemId, int propertyIndex) const
3392
16.0k
{
3393
25.8k
  for (const auto& entry : m_entries) {
3394
25.8k
    if (entry.item_ID == itemId) {
3395
46.2k
      for (const auto& assoc : entry.associations) {
3396
46.2k
        if (assoc.property_index == propertyIndex) {
3397
16.0k
          return assoc.essential;
3398
16.0k
        }
3399
46.2k
      }
3400
16.0k
    }
3401
25.8k
  }
3402
3403
16.0k
  assert(false);
3404
0
  return false;
3405
0
}
3406
3407
3408
heif_property_id Box_ipma::add_property_for_item_ID(heif_item_id itemID,
3409
                                                    PropertyAssociation assoc)
3410
43.4k
{
3411
43.4k
  size_t idx;
3412
869k
  for (idx = 0; idx < m_entries.size(); idx++) {
3413
869k
    if (m_entries[idx].item_ID == itemID) {
3414
43.2k
      break;
3415
43.2k
    }
3416
869k
  }
3417
3418
  // if itemID does not exist, add a new entry
3419
43.4k
  if (idx == m_entries.size()) {
3420
253
    Entry entry;
3421
253
    entry.item_ID = itemID;
3422
253
    m_entries.push_back(entry);
3423
253
  }
3424
3425
  // If the property is already associated with the item, skip.
3426
122k
  for (auto const& a : m_entries[idx].associations) {
3427
122k
    if (a.property_index == assoc.property_index) {
3428
41.0k
      return get_property_id_for_item_ID(itemID, assoc.property_index);
3429
41.0k
    }
3430
3431
    // TODO: should we check that the essential flag matches and return an internal error if not?
3432
122k
  }
3433
3434
  // add the property association
3435
2.44k
  m_entries[idx].associations.push_back(assoc);
3436
3437
2.44k
  return get_property_id_for_item_ID(itemID, assoc.property_index);
3438
43.4k
}
3439
3440
3441
heif_property_id Box_ipma::get_property_id_for_item_ID(heif_item_id itemID, uint16_t property_index) const
3442
43.4k
{
3443
  // Index 0 means "no property". It is skipped by Box_ipco::get_properties_for_item_ID() and
3444
  // hence has no position in the item's property list.
3445
43.4k
  if (property_index == 0) {
3446
0
    return 0;
3447
0
  }
3448
3449
869k
  for (const auto& entry : m_entries) {
3450
869k
    if (entry.item_ID != itemID) {
3451
825k
      continue;
3452
825k
    }
3453
3454
    // Count the associations the way Box_ipco::get_properties_for_item_ID() does, i.e. skipping
3455
    // the associations with index 0, so that the returned id indexes the vector it produces.
3456
43.4k
    heif_property_id id = 0;
3457
125k
    for (const auto& assoc : entry.associations) {
3458
125k
      if (assoc.property_index == 0) {
3459
63
        continue;
3460
63
      }
3461
3462
125k
      id++;
3463
3464
125k
      if (assoc.property_index == property_index) {
3465
43.4k
        return id;
3466
43.4k
      }
3467
125k
    }
3468
3469
0
    break;
3470
43.4k
  }
3471
3472
0
  return 0;
3473
43.4k
}
3474
3475
3476
void Box_ipma::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3477
0
{
3478
0
  Box::dump(sstr, indent, full_log);
3479
3480
0
  for (const Entry& entry : m_entries) {
3481
0
    sstr << indent << "associations for item ID: " << entry.item_ID << "\n";
3482
0
    indent++;
3483
0
    for (const auto& assoc : entry.associations) {
3484
0
      sstr << indent << "property index: " << assoc.property_index
3485
0
           << " (essential: " << std::boolalpha << assoc.essential << ")\n";
3486
0
    }
3487
0
    indent--;
3488
0
  }
3489
3490
0
  return;
3491
0
}
3492
3493
3494
void Box_ipma::derive_box_version()
3495
0
{
3496
0
  int version = 0;
3497
0
  bool large_property_indices = false;
3498
3499
0
  for (const Entry& entry : m_entries) {
3500
0
    if (entry.item_ID > 0xFFFF) {
3501
0
      version = 1;
3502
0
    }
3503
3504
0
    for (const auto& assoc : entry.associations) {
3505
0
      if (assoc.property_index > 0x7F) {
3506
0
        large_property_indices = true;
3507
0
      }
3508
0
    }
3509
0
  }
3510
3511
0
  set_version((uint8_t) version);
3512
0
  set_flags(large_property_indices ? 1 : 0);
3513
0
}
3514
3515
3516
Error Box_ipma::write(StreamWriter& writer) const
3517
0
{
3518
0
  size_t box_start = reserve_box_header_space(writer);
3519
3520
0
  size_t entry_cnt = m_entries.size();
3521
0
  writer.write32((uint32_t) entry_cnt);
3522
3523
0
  for (const Entry& entry : m_entries) {
3524
3525
0
    if (get_version() < 1) {
3526
0
      writer.write16((uint16_t) entry.item_ID);
3527
0
    }
3528
0
    else {
3529
0
      writer.write32(entry.item_ID);
3530
0
    }
3531
3532
0
    size_t assoc_cnt = entry.associations.size();
3533
0
    if (assoc_cnt > 0xFF) {
3534
      // TODO: error, too many associations
3535
0
    }
3536
3537
0
    writer.write8((uint8_t) assoc_cnt);
3538
3539
0
    for (const PropertyAssociation& association : entry.associations) {
3540
3541
0
      if (get_flags() & 1) {
3542
0
        writer.write16((uint16_t) ((association.essential ? 0x8000 : 0) |
3543
0
                                   (association.property_index & 0x7FFF)));
3544
0
      }
3545
0
      else {
3546
0
        writer.write8((uint8_t) ((association.essential ? 0x80 : 0) |
3547
0
                                 (association.property_index & 0x7F)));
3548
0
      }
3549
0
    }
3550
0
  }
3551
3552
0
  prepend_header(writer, box_start);
3553
3554
0
  return Error::Ok;
3555
0
}
3556
3557
3558
void Box_ipma::insert_entries_from_other_ipma_box(const Box_ipma& b)
3559
0
{
3560
0
  m_entries.insert(m_entries.end(),
3561
0
                   b.m_entries.begin(),
3562
0
                   b.m_entries.end());
3563
0
}
3564
3565
3566
void Box_ipma::sort_properties(const std::shared_ptr<Box_ipco>& ipco)
3567
0
{
3568
0
  auto properties = ipco->get_all_child_boxes();
3569
3570
  // Stable-partition: all descriptive properties before all transformative properties.
3571
  // The order within each group is preserved (spec requires it for transformative properties).
3572
3573
0
  for (auto& item : m_entries) {
3574
0
    std::stable_partition(item.associations.begin(), item.associations.end(),
3575
0
                          [&properties](const PropertyAssociation& assoc) {
3576
0
                            if (assoc.property_index == 0 || assoc.property_index > properties.size()) {
3577
0
                              return true; // keep invalid/unset entries in the descriptive group
3578
0
                            }
3579
0
                            return !properties[assoc.property_index - 1]->is_transformative_property();
3580
0
                          });
3581
0
  }
3582
0
}
3583
3584
3585
Error Box_auxC::parse(BitstreamRange& range, const heif_security_limits* limits)
3586
2.62k
{
3587
2.62k
  parse_full_box_header(range);
3588
3589
2.62k
  if (get_version() != 0) {
3590
1
    return unsupported_version_error("auxC");
3591
1
  }
3592
3593
2.62k
  m_aux_type = range.read_string();
3594
3595
6.55k
  while (!range.eof()) {
3596
3.92k
    m_aux_subtypes.push_back(range.read8());
3597
3.92k
  }
3598
3599
2.62k
  return range.get_error();
3600
2.62k
}
3601
3602
3603
Error Box_auxC::write(StreamWriter& writer) const
3604
0
{
3605
0
  size_t box_start = reserve_box_header_space(writer);
3606
3607
0
  writer.write(m_aux_type);
3608
3609
0
  for (uint8_t subtype : m_aux_subtypes) {
3610
0
    writer.write8(subtype);
3611
0
  }
3612
3613
0
  prepend_header(writer, box_start);
3614
3615
0
  return Error::Ok;
3616
0
}
3617
3618
3619
void Box_auxC::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3620
0
{
3621
0
  Box::dump(sstr, indent, full_log);
3622
3623
0
  sstr << indent << "aux type: " << m_aux_type << "\n"
3624
0
       << indent << "aux subtypes: ";
3625
0
  for (uint8_t subtype : m_aux_subtypes) {
3626
0
    sstr << std::hex << std::setw(2) << std::setfill('0') << ((int) subtype) << " ";
3627
0
  }
3628
3629
0
  sstr << "\n";
3630
3631
0
  return;
3632
0
}
3633
3634
3635
Error Box_irot::parse(BitstreamRange& range, const heif_security_limits* limits)
3636
139
{
3637
  //parse_full_box_header(range);
3638
3639
139
  uint16_t rotation = range.read8();
3640
139
  rotation &= 0x03;
3641
3642
139
  m_rotation = rotation * 90;
3643
3644
139
  return range.get_error();
3645
139
}
3646
3647
3648
Error Box_irot::write(StreamWriter& writer) const
3649
22
{
3650
22
  size_t box_start = reserve_box_header_space(writer);
3651
3652
22
  writer.write8((uint8_t) (m_rotation / 90));
3653
3654
22
  prepend_header(writer, box_start);
3655
3656
22
  return Error::Ok;
3657
22
}
3658
3659
3660
void Box_irot::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3661
0
{
3662
0
  Box::dump(sstr, indent, full_log);
3663
3664
0
  sstr << indent << "rotation: " << m_rotation << " degrees (CCW)\n";
3665
3666
0
  return;
3667
0
}
3668
3669
3670
Error Box_imir::parse(BitstreamRange& range, const heif_security_limits* limits)
3671
194
{
3672
  //parse_full_box_header(range);
3673
3674
194
  uint8_t axis = range.read8();
3675
194
  if (axis & 1) {
3676
164
    m_axis = heif_transform_mirror_direction_horizontal;
3677
164
  }
3678
30
  else {
3679
30
    m_axis = heif_transform_mirror_direction_vertical;
3680
30
  }
3681
3682
194
  return range.get_error();
3683
194
}
3684
3685
3686
Error Box_imir::write(StreamWriter& writer) const
3687
24
{
3688
24
  size_t box_start = reserve_box_header_space(writer);
3689
3690
24
  writer.write8(m_axis);
3691
3692
24
  prepend_header(writer, box_start);
3693
3694
24
  return Error::Ok;
3695
24
}
3696
3697
3698
void Box_imir::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3699
0
{
3700
0
  Box::dump(sstr, indent, full_log);
3701
3702
0
  sstr << indent << "mirror direction: ";
3703
0
  switch (m_axis) {
3704
0
    case heif_transform_mirror_direction_vertical:
3705
0
      sstr << "vertical\n";
3706
0
      break;
3707
0
    case heif_transform_mirror_direction_horizontal:
3708
0
      sstr << "horizontal\n";
3709
0
      break;
3710
0
    case heif_transform_mirror_direction_invalid:
3711
0
      sstr << "invalid\n";
3712
0
      break;
3713
0
  }
3714
3715
0
  return;
3716
0
}
3717
3718
3719
Error Box_iscl::parse(BitstreamRange& range, const heif_security_limits* limits)
3720
13
{
3721
13
  parse_full_box_header(range);
3722
3723
13
  if (get_version() != 0) {
3724
1
    return unsupported_version_error("iscl");
3725
1
  }
3726
3727
12
  m_target_width_numerator = range.read16();
3728
12
  m_target_width_denominator = range.read16();
3729
12
  m_target_height_numerator = range.read16();
3730
12
  m_target_height_denominator = range.read16();
3731
3732
12
  if (m_target_width_numerator == 0 || m_target_width_denominator == 0 ||
3733
8
      m_target_height_numerator == 0 || m_target_height_denominator == 0) {
3734
8
    return {heif_error_Invalid_input,
3735
8
            heif_suberror_Invalid_fractional_number,
3736
8
            "iscl property has zero numerator or denominator"};
3737
8
  }
3738
3739
4
  return range.get_error();
3740
12
}
3741
3742
3743
Error Box_iscl::write(StreamWriter& writer) const
3744
0
{
3745
0
  size_t box_start = reserve_box_header_space(writer);
3746
3747
0
  writer.write16(m_target_width_numerator);
3748
0
  writer.write16(m_target_width_denominator);
3749
0
  writer.write16(m_target_height_numerator);
3750
0
  writer.write16(m_target_height_denominator);
3751
3752
0
  prepend_header(writer, box_start);
3753
3754
0
  return Error::Ok;
3755
0
}
3756
3757
3758
void Box_iscl::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3759
0
{
3760
0
  FullBox::dump(sstr, indent, full_log);
3761
3762
0
  sstr << indent << "horizontal scaling factor: " << m_target_width_numerator << " / " << m_target_width_denominator << "\n";
3763
0
  sstr << indent << "vertical scaling factor:   " << m_target_height_numerator << " / " << m_target_height_denominator << "\n";
3764
3765
0
  return;
3766
0
}
3767
3768
3769
Error Box_clap::parse(BitstreamRange& range, const heif_security_limits* limits)
3770
566
{
3771
  //parse_full_box_header(range);
3772
3773
566
  uint32_t clean_aperture_width_num = range.read32();
3774
566
  uint32_t clean_aperture_width_den = range.read32();
3775
566
  uint32_t clean_aperture_height_num = range.read32();
3776
566
  uint32_t clean_aperture_height_den = range.read32();
3777
3778
  // Note: in the standard document 14496-12(2015), it says that the offset values should also be unsigned integers,
3779
  // but this is obviously an error. Even the accompanying standard text says that offsets may be negative.
3780
566
  int32_t horizontal_offset_num = (int32_t) range.read32();
3781
566
  uint32_t horizontal_offset_den = (uint32_t) range.read32();
3782
566
  int32_t vertical_offset_num = (int32_t) range.read32();
3783
566
  uint32_t vertical_offset_den = (uint32_t) range.read32();
3784
3785
  // The checked Fraction construction rejects values outside the int32_t range and
3786
  // zero denominators.
3787
566
  auto clean_aperture_width = Fraction::from_unsigned(clean_aperture_width_num, clean_aperture_width_den);
3788
566
  auto clean_aperture_height = Fraction::from_unsigned(clean_aperture_height_num, clean_aperture_height_den);
3789
566
  auto horizontal_offset = Fraction::from_signed(horizontal_offset_num, horizontal_offset_den);
3790
566
  auto vertical_offset = Fraction::from_signed(vertical_offset_num, vertical_offset_den);
3791
3792
566
  for (const Result<Fraction>* f : {&clean_aperture_width, &clean_aperture_height,
3793
1.96k
                                    &horizontal_offset, &vertical_offset}) {
3794
1.96k
    if (!*f) {
3795
194
      return f->error();
3796
194
    }
3797
1.96k
  }
3798
3799
372
  m_clean_aperture_width = *clean_aperture_width;
3800
372
  m_clean_aperture_height = *clean_aperture_height;
3801
372
  m_horizontal_offset = *horizontal_offset;
3802
372
  m_vertical_offset = *vertical_offset;
3803
3804
372
  return range.get_error();
3805
566
}
3806
3807
3808
Error Box_clap::write(StreamWriter& writer) const
3809
0
{
3810
0
  size_t box_start = reserve_box_header_space(writer);
3811
3812
0
  writer.write32(m_clean_aperture_width.numerator);
3813
0
  writer.write32(m_clean_aperture_width.denominator);
3814
0
  writer.write32(m_clean_aperture_height.numerator);
3815
0
  writer.write32(m_clean_aperture_height.denominator);
3816
0
  writer.write32s(m_horizontal_offset.numerator);
3817
0
  writer.write32(m_horizontal_offset.denominator);
3818
0
  writer.write32s(m_vertical_offset.numerator);
3819
0
  writer.write32(m_vertical_offset.denominator);
3820
3821
0
  prepend_header(writer, box_start);
3822
3823
0
  return Error::Ok;
3824
0
}
3825
3826
3827
void Box_clap::dump(std::ostream& sstr, Indent& indent, bool full_log) const
3828
0
{
3829
0
  Box::dump(sstr, indent, full_log);
3830
3831
0
  sstr << indent << "clean_aperture: " << m_clean_aperture_width.numerator
3832
0
       << "/" << m_clean_aperture_width.denominator << " x "
3833
0
       << m_clean_aperture_height.numerator << "/"
3834
0
       << m_clean_aperture_height.denominator << "\n";
3835
0
  sstr << indent << "offset: " << m_horizontal_offset.numerator << "/"
3836
0
       << m_horizontal_offset.denominator << " ; "
3837
0
       << m_vertical_offset.numerator << "/"
3838
0
       << m_vertical_offset.denominator << "\n";
3839
3840
0
  return;
3841
0
}
3842
3843
3844
double Box_clap::left(int image_width) const
3845
0
{
3846
0
  Fraction pcX = m_horizontal_offset + Fraction(image_width - 1, 2);
3847
0
  Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
3848
0
  return left.to_double();
3849
0
}
3850
3851
double Box_clap::top(int image_height) const
3852
0
{
3853
0
  Fraction pcY = m_vertical_offset + Fraction(image_height - 1, 2);
3854
0
  Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
3855
0
  return top.to_double();
3856
0
}
3857
3858
3859
Result<Box_clap::Crop> Box_clap::get_crop(uint32_t image_width, uint32_t image_height) const
3860
113
{
3861
  // The clean aperture is specified relative to the image center:
3862
  //   pcX  = horizOff + (width - 1)/2
3863
  //   left = pcX - (clapWidth - 1)/2
3864
  //   right = left + clapWidth - 1
3865
  // (and likewise vertically).
3866
  //
3867
  // Computing this needs the image size inside the int32_t-based Fraction. A zero size
3868
  // would underflow `size - 1` (GHSA-jc8f-p23p-5hjg) and a size above INT32_MAX + 1
3869
  // does not fit (GHSA-gh5q-69gg-c964). Such an image cannot be cropped with a 'clap',
3870
  // which is reported as an error instead of computing a bogus crop.
3871
113
  if (image_width == 0 || image_height == 0) {
3872
0
    return Error(heif_error_Invalid_input,
3873
0
                 heif_suberror_Invalid_clean_aperture,
3874
0
                 "Clean aperture cannot be applied to an image with zero size");
3875
0
  }
3876
3877
113
  auto halfWidth = Fraction::from_unsigned(image_width - 1, 2);
3878
113
  auto halfHeight = Fraction::from_unsigned(image_height - 1, 2);
3879
113
  if (!halfWidth || !halfHeight) {
3880
0
    return Error(heif_error_Invalid_input,
3881
0
                 heif_suberror_Invalid_clean_aperture,
3882
0
                 "Clean aperture cannot be applied to an image larger than 2^31 pixels in any direction");
3883
0
  }
3884
3885
113
  Fraction pcX = m_horizontal_offset + *halfWidth;
3886
113
  Fraction pcY = m_vertical_offset + *halfHeight;
3887
3888
113
  Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
3889
113
  Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
3890
3891
113
  Crop crop;
3892
113
  crop.left = left.round_down();
3893
113
  crop.top = top.round();
3894
113
  crop.right = (m_clean_aperture_width - 1 + crop.left).round();
3895
113
  crop.bottom = (m_clean_aperture_height - 1 + crop.top).round();
3896
3897
113
  return crop;
3898
113
}
3899
3900
int Box_clap::get_width_rounded() const
3901
403
{
3902
403
  return m_clean_aperture_width.round();
3903
403
}
3904
3905
int Box_clap::get_height_rounded() const
3906
403
{
3907
403
  return m_clean_aperture_height.round();
3908
403
}
3909
3910
Error Box_clap::set(uint32_t clap_width, uint32_t clap_height,
3911
                    uint32_t image_width, uint32_t image_height)
3912
0
{
3913
0
  if (clap_width > image_width || clap_height > image_height) {
3914
0
    return Error(heif_error_Usage_error,
3915
0
                 heif_suberror_Invalid_parameter_value,
3916
0
                 "Clean aperture is larger than the image");
3917
0
  }
3918
3919
0
  auto width = Fraction::from_unsigned(clap_width, 1);
3920
0
  auto height = Fraction::from_unsigned(clap_height, 1);
3921
0
  auto horizontal_offset = Fraction::from_signed(-int64_t{image_width - clap_width}, 2);
3922
0
  auto vertical_offset = Fraction::from_signed(-int64_t{image_height - clap_height}, 2);
3923
3924
0
  if (!width || !height || !horizontal_offset || !vertical_offset) {
3925
0
    return Error(heif_error_Usage_error,
3926
0
                 heif_suberror_Invalid_parameter_value,
3927
0
                 "Clean aperture values exceed the supported range");
3928
0
  }
3929
3930
0
  m_clean_aperture_width = *width;
3931
0
  m_clean_aperture_height = *height;
3932
0
  m_horizontal_offset = *horizontal_offset;
3933
0
  m_vertical_offset = *vertical_offset;
3934
3935
0
  return Error::Ok;
3936
0
}
3937
3938
3939
Error Box_iref::parse(BitstreamRange& range, const heif_security_limits* limits)
3940
6.69k
{
3941
6.69k
  parse_full_box_header(range);
3942
3943
6.69k
  if (get_version() > 1) {
3944
2
    return unsupported_version_error("iref");
3945
2
  }
3946
3947
15.5k
  while (!range.eof()) {
3948
    // Cap the total number of reference entries, matching the item-count caps in
3949
    // Box_iloc/Box_iinf/Box_ipma (and the per-entry nRefs cap below). Without it
3950
    // the entry count is bounded only by the file size. That is a linear,
3951
    // file-bounded allocation rather than an amplification, so this is a
3952
    // consistency limit and not a security fix, but it rejects a pathological
3953
    // 'iref' early with a clear error instead of parsing it in full.
3954
    //
3955
    // max_items is a heuristic ceiling here, not a semantically exact bound: the
3956
    // number of entries is not strictly limited by the number of items, because
3957
    // one item may be the source (from_item_ID) of several entries, one per
3958
    // reference type (e.g. 'dimg', 'thmb', 'cdsc'). A well-formed file stays far
3959
    // below max_items (default 1000) regardless, so the generous ceiling is fine
3960
    // as a sanity limit; raise max_items if a legitimate file ever exceeds it.
3961
8.88k
    if (limits->max_items && m_references.size() >= limits->max_items) {
3962
0
      std::stringstream sstr;
3963
0
      sstr << "'iref' box contains more than " << limits->max_items
3964
0
           << " reference entries, which exceeds the security limit.";
3965
3966
0
      return {heif_error_Invalid_input,
3967
0
              heif_suberror_Security_limit_exceeded,
3968
0
              sstr.str()};
3969
0
    }
3970
3971
8.88k
    Reference ref;
3972
3973
8.88k
    Error err = ref.header.parse_header(range);
3974
8.88k
    if (err != Error::Ok) {
3975
6
      return err;
3976
6
    }
3977
3978
8.87k
    int read_len = (get_version() == 0) ? 16 : 32;
3979
3980
8.87k
    ref.from_item_ID = static_cast<uint32_t>(range.read_uint(read_len));
3981
8.87k
    uint16_t nRefs = range.read16();
3982
3983
8.87k
    if (nRefs==0) {
3984
3
      return {heif_error_Invalid_input,
3985
3
              heif_suberror_Unspecified,
3986
3
              "Input file has an 'iref' box with no references."};
3987
3
    }
3988
3989
8.87k
    if (limits->max_items && nRefs > limits->max_items) {
3990
12
      std::stringstream sstr;
3991
12
      sstr << "Number of references in iref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references.";
3992
3993
12
      return {heif_error_Invalid_input,
3994
12
              heif_suberror_Security_limit_exceeded,
3995
12
              sstr.str()};
3996
12
    }
3997
3998
62.3k
    for (int i = 0; i < nRefs; i++) {
3999
53.5k
      if (range.eof()) {
4000
30
        std::stringstream sstr;
4001
30
        sstr << "iref box should contain " << nRefs << " references, but we can only read " << i << " references.";
4002
4003
30
        return {heif_error_Invalid_input,
4004
30
                heif_suberror_End_of_data,
4005
30
                sstr.str()};
4006
30
      }
4007
4008
53.4k
      ref.to_item_ID.push_back(static_cast<uint32_t>(range.read_uint(read_len)));
4009
53.4k
    }
4010
4011
8.82k
    m_references.push_back(ref);
4012
8.82k
  }
4013
4014
4015
  // Note: the same item may be listed several times within one reference entry.
4016
  // ISO/IEC 14496-12 does not forbid it, and derived images rely on it: an 'iovl'
4017
  // that places the same input image at two positions references it twice in its
4018
  // 'dimg' entry (one offset per reference). Conformance file C021 does this.
4019
4020
4021
#if 0
4022
  // Note: This input sanity check first did not work as expected.
4023
  // Its idea was to prevent infinite recursions while decoding when the input file
4024
  // contains cyclic references. However, apparently there are cases where cyclic
4025
  // references are actually allowed, like with images that have premultiplied alpha channels:
4026
  // | Box: iref -----
4027
  // | size: 40   (header size: 12)
4028
  // | reference with type 'auxl' from ID: 2 to IDs: 1
4029
  // | reference with type 'prem' from ID: 1 to IDs: 2
4030
  //
4031
  // We now test for cyclic references during the image decoding.
4032
  // We pass down the item IDs that have already been seen during the decoding process.
4033
  // If we try to decode an image IDs that has already been seen previously, we throw an error.
4034
  // The advantage is that the error only occurs when we are trying to decode the faulty image.
4035
4036
  // --- check for cyclic references
4037
4038
  for (const auto& ref : m_references) {
4039
    if (ref.header.get_short_type() != fourcc("dimg") &&
4040
        ref.header.get_short_type() != fourcc("auxl")) {
4041
      continue;
4042
    }
4043
4044
    std::set<heif_item_id> reached_ids; // IDs that we have already reached in the DAG
4045
    std::set<heif_item_id> todo;    // IDs that still need to be followed
4046
4047
    bool reverse = (ref.header.get_short_type() != fourcc("auxl"));
4048
4049
    if (!reverse) {
4050
      todo.insert(ref.from_item_ID);  // start at base item
4051
    }
4052
    else {
4053
      if (ref.to_item_ID.empty()) {
4054
        continue;
4055
      }
4056
4057
      // TODO: what if aux image is assigned to multiple images?
4058
      todo.insert(ref.to_item_ID[0]);  // start at base item
4059
    }
4060
4061
    while (!todo.empty()) {
4062
      // transfer ID into set of reached IDs
4063
      auto id = *todo.begin();
4064
      todo.erase(id);
4065
      reached_ids.insert(id);
4066
4067
      // if this ID refers to another 'iref', follow it
4068
4069
      for (const auto& succ_ref : m_references) {
4070
        if (succ_ref.header.get_short_type() != fourcc("dimg") &&
4071
            succ_ref.header.get_short_type() != fourcc("auxl")) {
4072
          continue;
4073
        }
4074
4075
        heif_item_id from;
4076
        std::vector<heif_item_id> to;
4077
4078
        if (succ_ref.header.get_short_type() == fourcc("auxl")) {
4079
          if (succ_ref.to_item_ID.empty()) {
4080
            continue;
4081
          }
4082
4083
          from = succ_ref.to_item_ID[0];
4084
          to = {succ_ref.from_item_ID};
4085
        }
4086
        else {
4087
          from = succ_ref.from_item_ID;
4088
          to = succ_ref.to_item_ID;
4089
        }
4090
4091
        if (from == id) {
4092
4093
          // Check whether any successor IDs has been visited yet, which would be an error.
4094
          // Otherwise, put that ID into the 'todo' set.
4095
4096
          for (const auto& succ_ref_id : to) {
4097
            if (reached_ids.find(succ_ref_id) != reached_ids.end()) {
4098
              return Error(heif_error_Invalid_input,
4099
                           heif_suberror_Unspecified,
4100
                           "'iref' has cyclic references");
4101
            }
4102
4103
            todo.insert(succ_ref_id);
4104
          }
4105
        }
4106
      }
4107
    }
4108
  }
4109
#endif
4110
4111
6.64k
  build_index();
4112
4113
6.64k
  return range.get_error();
4114
6.69k
}
4115
4116
4117
void Box_iref::derive_box_version()
4118
0
{
4119
0
  uint8_t version = 0;
4120
4121
0
  for (const auto& ref : m_references) {
4122
0
    if (ref.from_item_ID > 0xFFFF) {
4123
0
      version = 1;
4124
0
      break;
4125
0
    }
4126
4127
0
    for (uint32_t r : ref.to_item_ID) {
4128
0
      if (r > 0xFFFF) {
4129
0
        version = 1;
4130
0
        break;
4131
0
      }
4132
0
    }
4133
0
  }
4134
4135
0
  set_version(version);
4136
0
}
4137
4138
4139
Error Box_iref::write(StreamWriter& writer) const
4140
0
{
4141
0
  size_t box_start = reserve_box_header_space(writer);
4142
4143
0
  int id_size = ((get_version() == 0) ? 2 : 4);
4144
4145
0
  for (const auto& ref : m_references) {
4146
0
    uint32_t box_size = uint32_t(4 + 4 + 2 + id_size * (1 + ref.to_item_ID.size()));
4147
4148
    // we write the BoxHeader ourselves since it is very simple
4149
0
    writer.write32(box_size);
4150
0
    writer.write32(ref.header.get_short_type());
4151
4152
0
    writer.write(id_size, ref.from_item_ID);
4153
0
    writer.write16((uint16_t) ref.to_item_ID.size());
4154
4155
0
    for (uint32_t r : ref.to_item_ID) {
4156
0
      writer.write(id_size, r);
4157
0
    }
4158
0
  }
4159
4160
0
  prepend_header(writer, box_start);
4161
4162
0
  return Error::Ok;
4163
0
}
4164
4165
4166
void Box_iref::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4167
0
{
4168
0
  Box::dump(sstr, indent, full_log);
4169
4170
0
  for (const auto& ref : m_references) {
4171
0
    sstr << indent << "reference with type '" << ref.header.get_type_string() << "'"
4172
0
         << " from ID: " << ref.from_item_ID
4173
0
         << " to IDs: ";
4174
0
    for (uint32_t id : ref.to_item_ID) {
4175
0
      sstr << id << " ";
4176
0
    }
4177
0
    sstr << "\n";
4178
0
  }
4179
4180
0
  return;
4181
0
}
4182
4183
4184
void Box_iref::build_index()
4185
6.64k
{
4186
6.64k
  m_from_id_index.clear();
4187
6.64k
  m_from_id_index.reserve(m_references.size());
4188
15.4k
  for (size_t i = 0; i < m_references.size(); i++) {
4189
8.78k
    add_to_index(i);
4190
8.78k
  }
4191
6.64k
}
4192
4193
4194
void Box_iref::add_to_index(size_t reference_index)
4195
9.15k
{
4196
9.15k
  m_from_id_index[m_references[reference_index].from_item_ID].push_back(reference_index);
4197
9.15k
}
4198
4199
4200
bool Box_iref::has_references(uint32_t itemID) const
4201
0
{
4202
0
  return m_from_id_index.find(itemID) != m_from_id_index.end();
4203
0
}
4204
4205
4206
std::vector<Box_iref::Reference> Box_iref::get_references_from(heif_item_id itemID) const
4207
43.8k
{
4208
43.8k
  std::vector<Reference> references;
4209
4210
43.8k
  auto iter = m_from_id_index.find(itemID);
4211
43.8k
  if (iter != m_from_id_index.end()) {
4212
2.19k
    references.reserve(iter->second.size());
4213
2.23k
    for (size_t ref_idx : iter->second) {
4214
2.23k
      references.push_back(m_references[ref_idx]);
4215
2.23k
    }
4216
2.19k
  }
4217
4218
43.8k
  return references;
4219
43.8k
}
4220
4221
4222
std::vector<uint32_t> Box_iref::get_references(uint32_t itemID, uint32_t ref_type) const
4223
122k
{
4224
122k
  auto iter = m_from_id_index.find(itemID);
4225
122k
  if (iter != m_from_id_index.end()) {
4226
20.3k
    for (size_t ref_idx : iter->second) {
4227
20.3k
      const Reference& ref = m_references[ref_idx];
4228
20.3k
      if (ref.header.get_short_type() == ref_type) {
4229
12.8k
        return ref.to_item_ID;
4230
12.8k
      }
4231
20.3k
    }
4232
20.3k
  }
4233
4234
109k
  return std::vector<uint32_t>();
4235
122k
}
4236
4237
4238
void Box_iref::add_references(heif_item_id from_id, uint32_t type, const std::vector<heif_item_id>& to_ids)
4239
365
{
4240
365
  Reference ref;
4241
365
  ref.header.set_short_type(type);
4242
365
  ref.from_item_ID = from_id;
4243
365
  ref.to_item_ID = to_ids;
4244
4245
365
  assert(to_ids.size() <= 0xFFFF);
4246
4247
365
  m_references.push_back(ref);
4248
365
  add_to_index(m_references.size() - 1);
4249
365
}
4250
4251
4252
void Box_iref::overwrite_reference(heif_item_id from_id, uint32_t type, uint32_t reference_idx, heif_item_id to_item)
4253
0
{
4254
0
  for (auto& ref : m_references) {
4255
0
    if (ref.from_item_ID == from_id && ref.header.get_short_type() == type) {
4256
0
      assert(reference_idx < ref.to_item_ID.size());
4257
4258
0
      ref.to_item_ID[reference_idx] = to_item;
4259
0
      return;
4260
0
    }
4261
0
  }
4262
4263
0
  assert(false); // reference was not found
4264
0
}
4265
4266
4267
Error Box_rref::parse(BitstreamRange& range, const heif_security_limits* limits)
4268
28
{
4269
28
  parse_full_box_header(range);
4270
4271
28
  if (get_version() > 1) {
4272
3
    return unsupported_version_error("rref");
4273
3
  }
4274
4275
25
  size_t remainingBytes = range.get_remaining_bytes();
4276
4277
  // The number of reference types should be stored in uint(8), but the
4278
  // common test images C043, C044 store them as uint(32).
4279
  // Let us detect this case by the number of data bytes in this box.
4280
25
  bool cnt_as_uint32 = (remainingBytes > 0 && remainingBytes % 4 == 0);
4281
4282
25
  uint8_t nRefTypes;
4283
25
  if (cnt_as_uint32) {
4284
    // fix broken C043, C044 files
4285
    // TODO: remove me when the files have been corrected as the above check can misfire when there is extra padding
4286
18
    nRefTypes = (uint8_t)range.read32();
4287
18
  }
4288
7
  else {
4289
7
    nRefTypes = range.read8();
4290
7
  }
4291
4292
386
  for (uint8_t i = 0; i < nRefTypes; i++) {
4293
371
    uint32_t refType = range.read32();
4294
4295
371
    if (range.error()) {
4296
10
      std::stringstream sstr;
4297
10
      sstr << "rref box should contain " << ((int)nRefTypes) << " reference types, but we can only read " << m_reference_types.size() << " reference types.";
4298
4299
10
      return {
4300
10
        heif_error_Invalid_input,
4301
10
        heif_suberror_End_of_data,
4302
10
        sstr.str()
4303
10
      };
4304
10
    }
4305
4306
361
    m_reference_types.push_back(refType);
4307
361
  }
4308
4309
15
  return range.get_error();
4310
25
}
4311
4312
4313
static constexpr std::array supported_reference_types{
4314
  fourcc("thmb"),
4315
  fourcc("base"),
4316
  fourcc("cdsc"),
4317
  fourcc("dimg"),
4318
  fourcc("auxl"),
4319
  fourcc("prem")
4320
};
4321
4322
4323
bool Box_rref::all_reference_types_supported() const
4324
0
{
4325
  // TODO: replace with std::ranges variant once it is widely supported (e.g. on older clang/libc++):
4326
  //   return std::ranges::all_of(m_reference_types, [](uint32_t t) {
4327
  //     return std::ranges::find(supported_reference_types, t) != supported_reference_types.end();
4328
  //   });
4329
0
  return std::all_of(m_reference_types.begin(), m_reference_types.end(), [](uint32_t t) {
4330
0
    return std::find(supported_reference_types.begin(), supported_reference_types.end(), t)
4331
0
           != supported_reference_types.end();
4332
0
  });
4333
0
}
4334
4335
4336
Error Box_rref::reference_types_supported_error() const
4337
0
{
4338
0
  std::vector<uint32_t> unsupported_types;
4339
4340
0
  for (uint32_t refType : m_reference_types) {
4341
    // TODO: replace with std::ranges variant once it is widely supported (e.g. on older clang/libc++):
4342
    //   if (std::ranges::find(supported_reference_types, refType) == supported_reference_types.end()) {
4343
0
    if (std::find(supported_reference_types.begin(), supported_reference_types.end(), refType) == supported_reference_types.end()) {
4344
0
      unsupported_types.push_back(refType);
4345
0
    }
4346
0
  }
4347
4348
0
  if (unsupported_types.empty()) {
4349
0
    return Error::Ok;
4350
0
  }
4351
4352
0
  std::stringstream sstr;
4353
4354
0
  sstr << "Unsupported reference types: ";
4355
0
  for (size_t i = 0; i < unsupported_types.size(); i++) {
4356
0
    if (i > 0) {
4357
0
      sstr << ", ";
4358
0
    }
4359
0
    sstr << fourcc_to_string(unsupported_types[i]);
4360
0
  }
4361
4362
0
  return {
4363
0
    heif_error_Unsupported_feature,
4364
0
    heif_suberror_Unspecified,
4365
0
    sstr.str()
4366
0
  };
4367
0
}
4368
4369
4370
Error Box_rref::write(StreamWriter& writer) const
4371
0
{
4372
0
  size_t box_start = reserve_box_header_space(writer);
4373
4374
0
  if (m_reference_types.size() > std::numeric_limits<uint8_t>::max()) {
4375
0
    return {
4376
0
      heif_error_Usage_error,
4377
0
      heif_suberror_Unspecified,
4378
0
      "Too many rref reference types."
4379
0
    };
4380
0
  }
4381
4382
0
  writer.write8(static_cast<uint8_t>(m_reference_types.size()));
4383
4384
0
  for (uint32_t refType : m_reference_types) {
4385
0
    writer.write32(refType);
4386
0
  }
4387
4388
0
  prepend_header(writer, box_start);
4389
4390
0
  return Error::Ok;
4391
0
}
4392
4393
4394
void Box_rref::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4395
0
{
4396
0
  Box::dump(sstr, indent, full_log);
4397
4398
0
  sstr << indent << "reference types: ";
4399
0
  for (size_t i = 0; i < m_reference_types.size(); i++) {
4400
0
    if (i > 0) sstr << ", ";
4401
0
    sstr << fourcc_to_string(m_reference_types[i]);
4402
0
  }
4403
0
  sstr << "\n";
4404
4405
0
  return;
4406
0
}
4407
4408
4409
Error Box_idat::parse(BitstreamRange& range, const heif_security_limits* limits)
4410
915
{
4411
  //parse_full_box_header(range);
4412
4413
915
  m_data_start_pos = range.get_istream()->get_position();
4414
4415
915
  return range.get_error();
4416
915
}
4417
4418
4419
Error Box_idat::write(StreamWriter& writer) const
4420
0
{
4421
0
  size_t box_start = reserve_box_header_space(writer);
4422
4423
0
  writer.write(m_data_for_writing);
4424
4425
0
  prepend_header(writer, box_start);
4426
4427
0
  return Error::Ok;
4428
0
}
4429
4430
4431
void Box_idat::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4432
0
{
4433
0
  Box::dump(sstr, indent, full_log);
4434
4435
0
  if (get_box_size() >= get_header_size()) {
4436
0
    sstr << indent << "number of data bytes: " << get_box_size() - get_header_size() << "\n";
4437
0
  } else {
4438
0
     sstr << indent << "number of data bytes is invalid\n";
4439
0
  }
4440
4441
0
  return;
4442
0
}
4443
4444
4445
Error Box_idat::read_data(const std::shared_ptr<StreamReader>& istr,
4446
                          uint64_t start, uint64_t length,
4447
                          std::vector<uint8_t>& out_data,
4448
                          const heif_security_limits* limits) const
4449
1.59k
{
4450
  // --- security check that we do not allocate too much data
4451
4452
1.59k
  auto curr_size = out_data.size();
4453
4454
1.59k
  auto max_memory_block_size = limits->max_memory_block_size;
4455
1.59k
  if (max_memory_block_size && max_memory_block_size - curr_size < length) {
4456
15
    std::stringstream sstr;
4457
15
    sstr << "idat box contained " << length << " bytes, total memory size would be "
4458
15
         << (curr_size + length) << " bytes, exceeding the security limit of "
4459
15
         << max_memory_block_size << " bytes";
4460
4461
15
    return Error(heif_error_Memory_allocation_error,
4462
15
                 heif_suberror_Security_limit_exceeded,
4463
15
                 sstr.str());
4464
15
  }
4465
4466
4467
  // move to start of data
4468
1.58k
  if (start > (uint64_t) m_data_start_pos + get_box_size()) {
4469
70
    return Error(heif_error_Invalid_input,
4470
70
                 heif_suberror_End_of_data);
4471
70
  }
4472
1.51k
  else if (length > get_box_size() || start + length > get_box_size()) {
4473
14
    return Error(heif_error_Invalid_input,
4474
14
                 heif_suberror_End_of_data);
4475
14
  }
4476
4477
1.50k
  StreamReader::grow_status status = istr->wait_for_file_size((int64_t) m_data_start_pos + start + length);
4478
1.50k
  if (status == StreamReader::grow_status::size_beyond_eof ||
4479
1.50k
      status == StreamReader::grow_status::timeout) {
4480
    // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
4481
0
    return Error(heif_error_Invalid_input,
4482
0
                 heif_suberror_End_of_data);
4483
0
  }
4484
4485
1.50k
  bool success;
4486
1.50k
  success = istr->seek(m_data_start_pos + (std::streampos) start);
4487
1.50k
  assert(success);
4488
1.50k
  (void) success;
4489
4490
1.50k
  if (length > 0) {
4491
    // reserve space for the data in the output array
4492
1.50k
    out_data.resize(static_cast<size_t>(curr_size + length));
4493
1.50k
    uint8_t* data = &out_data[curr_size];
4494
4495
1.50k
    success = istr->read((char*) data, static_cast<size_t>(length));
4496
1.50k
    assert(success);
4497
1.50k
    (void) success;
4498
1.50k
  }
4499
4500
1.50k
  return Error::Ok;
4501
1.50k
}
4502
4503
4504
Error Box_grpl::parse(BitstreamRange& range, const heif_security_limits* limits)
4505
116
{
4506
  //parse_full_box_header(range);
4507
4508
116
  return read_children(range, READ_CHILDREN_ALL, limits); // should we pass the parsing context 'grpl' or are the box types unique?
4509
116
}
4510
4511
4512
void Box_grpl::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4513
0
{
4514
0
  Box::dump(sstr, indent, full_log);
4515
0
  dump_children(sstr, indent, full_log);
4516
0
  return;
4517
0
}
4518
4519
4520
Error Box_EntityToGroup::parse(BitstreamRange& range, const heif_security_limits* limits)
4521
272
{
4522
272
  Error err = parse_full_box_header(range);
4523
272
  if (err != Error::Ok) {
4524
1
    return err;
4525
1
  }
4526
4527
271
  group_id = range.read32();
4528
271
  uint32_t nEntities = range.read32();
4529
4530
271
  if (nEntities > range.get_remaining_bytes() / 4) {
4531
45
    std::stringstream sstr;
4532
45
    size_t maxEntries = range.get_remaining_bytes() / 4;
4533
45
    sstr << "entity group box should contain " << nEntities << " entities, but we can only read " << maxEntries << " entities.";
4534
4535
45
    return {heif_error_Invalid_input,
4536
45
            heif_suberror_End_of_data,
4537
45
            sstr.str()};
4538
45
  }
4539
4540
226
  if (limits->max_size_entity_group && nEntities > limits->max_size_entity_group) {
4541
1
    std::stringstream sstr;
4542
1
    sstr << "entity group box contains " << nEntities << " entities, but the security limit is set to " << limits->max_size_entity_group << " entities.";
4543
4544
1
    return {heif_error_Invalid_input,
4545
1
            heif_suberror_Security_limit_exceeded,
4546
1
            sstr.str()};
4547
1
  }
4548
4549
225
  entity_ids.resize(nEntities);
4550
909
  for (uint32_t i = 0; i < nEntities; i++) {
4551
684
    entity_ids[i] = range.read32();
4552
684
  }
4553
4554
225
  return Error::Ok;
4555
226
}
4556
4557
4558
Error Box_EntityToGroup::write(StreamWriter& writer) const
4559
0
{
4560
0
  size_t box_start = reserve_box_header_space(writer);
4561
4562
0
  write_entity_group_ids(writer);
4563
4564
0
  prepend_header(writer, box_start);
4565
4566
0
  return Error::Ok;
4567
0
}
4568
4569
4570
void Box_EntityToGroup::write_entity_group_ids(StreamWriter& writer) const
4571
0
{
4572
0
  assert(entity_ids.size() <= 0xFFFFFFFF);
4573
4574
0
  writer.write32(group_id);
4575
0
  writer.write32(static_cast<uint32_t>(entity_ids.size()));
4576
4577
0
  for (uint32_t id : entity_ids) {
4578
0
    writer.write32(id);
4579
0
  }
4580
0
}
4581
4582
4583
void Box_EntityToGroup::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4584
0
{
4585
0
  Box::dump(sstr, indent, full_log);
4586
4587
0
  sstr << indent << "group id: " << group_id << "\n"
4588
0
       << indent << "entity IDs: ";
4589
4590
0
  bool first = true;
4591
0
  for (uint32_t id : entity_ids) {
4592
0
    if (first) {
4593
0
      first = false;
4594
0
    }
4595
0
    else {
4596
0
      sstr << ' ';
4597
0
    }
4598
4599
0
    sstr << id;
4600
0
  }
4601
4602
0
  sstr << "\n";
4603
4604
0
  return;
4605
0
}
4606
4607
4608
Error Box_ster::parse(BitstreamRange& range, const heif_security_limits* limits)
4609
5
{
4610
5
  Error err = Box_EntityToGroup::parse(range, limits);
4611
5
  if (err) {
4612
1
    return err;
4613
1
  }
4614
4615
4
  if (entity_ids.size() != 2) {
4616
1
    return {heif_error_Invalid_input,
4617
1
            heif_suberror_Invalid_box_size,
4618
1
            "'ster' entity group does not exists of exactly two images"};
4619
1
  }
4620
4621
3
  return Error::Ok;
4622
4
}
4623
4624
4625
void Box_ster::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4626
0
{
4627
0
  Box::dump(sstr, indent, full_log);
4628
4629
0
  sstr << indent << "group id: " << group_id << "\n"
4630
0
       << indent << "left image ID: " << entity_ids[0] << "\n"
4631
0
       << indent << "right image ID: " << entity_ids[1] << "\n";
4632
4633
0
  return;
4634
0
}
4635
4636
4637
4638
Error Box_pymd::parse(BitstreamRange& range, const heif_security_limits* limits)
4639
230
{
4640
230
  Error err = Box_EntityToGroup::parse(range, limits);
4641
230
  if (err) {
4642
44
    return err;
4643
44
  }
4644
4645
186
  tile_size_x = range.read16();
4646
186
  tile_size_y = range.read16();
4647
4648
800
  for (size_t i = 0; i < entity_ids.size(); i++) {
4649
614
    LayerInfo layer{};
4650
614
    layer.layer_binning = range.read16();
4651
614
    layer.tiles_in_layer_row_minus1 = range.read16();
4652
614
    layer.tiles_in_layer_column_minus1 = range.read16();
4653
4654
614
    m_layer_infos.push_back(layer);
4655
614
  }
4656
4657
186
  return Error::Ok;
4658
230
}
4659
4660
4661
Error Box_pymd::write(StreamWriter& writer) const
4662
0
{
4663
0
  size_t box_start = reserve_box_header_space(writer);
4664
4665
0
  Box_EntityToGroup::write_entity_group_ids(writer);
4666
4667
0
  writer.write16(tile_size_x);
4668
0
  writer.write16(tile_size_y);
4669
4670
0
  for (size_t i = 0; i < entity_ids.size(); i++) {
4671
0
    const LayerInfo& layer = m_layer_infos[i];
4672
4673
0
    writer.write16(layer.layer_binning);
4674
0
    writer.write16(layer.tiles_in_layer_row_minus1);
4675
0
    writer.write16(layer.tiles_in_layer_column_minus1);
4676
0
  }
4677
4678
0
  prepend_header(writer, box_start);
4679
4680
0
  return Error::Ok;
4681
0
}
4682
4683
4684
void Box_pymd::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4685
0
{
4686
0
  Box_EntityToGroup::dump(sstr, indent, full_log);
4687
4688
0
  sstr << indent << "tile size: " << tile_size_x << "x" << tile_size_y << "\n";
4689
4690
0
  int layerNr = 0;
4691
0
  for (const auto& layer : m_layer_infos) {
4692
0
    sstr << indent << "layer " << layerNr << ":\n"
4693
0
         << indent << "| binning: " << layer.layer_binning << "\n"
4694
0
         << indent << "| tiles: " << (layer.tiles_in_layer_row_minus1 + 1) << "x" << (layer.tiles_in_layer_column_minus1 + 1) << "\n";
4695
4696
0
    layerNr++;
4697
0
  }
4698
4699
0
  return;
4700
0
}
4701
4702
4703
Error Box_dinf::parse(BitstreamRange& range, const heif_security_limits* limits)
4704
45
{
4705
  //parse_full_box_header(range);
4706
4707
45
  return read_children(range, READ_CHILDREN_ALL, limits);
4708
45
}
4709
4710
4711
void Box_dinf::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4712
0
{
4713
0
  Box::dump(sstr, indent, full_log);
4714
0
  dump_children(sstr, indent, full_log);
4715
4716
0
  return;
4717
0
}
4718
4719
4720
Error Box_dref::parse(BitstreamRange& range, const heif_security_limits* limits)
4721
31
{
4722
31
  parse_full_box_header(range);
4723
4724
31
  if (get_version() != 0) {
4725
1
    return unsupported_version_error("dref");
4726
1
  }
4727
4728
30
  uint32_t nEntities = range.read32();
4729
4730
  /*
4731
  for (int i=0;i<nEntities;i++) {
4732
    if (range.eof()) {
4733
      break;
4734
    }
4735
  }
4736
  */
4737
4738
30
  if (nEntities > (uint32_t)std::numeric_limits<int>::max()) {
4739
1
    return Error(heif_error_Memory_allocation_error,
4740
1
                 heif_suberror_Security_limit_exceeded,
4741
1
                 "Too many entities in dref box.");
4742
1
  }
4743
4744
  // read_children verifies that exactly nEntities children are present.
4745
29
  return read_children(range, (int)nEntities, limits);
4746
30
}
4747
4748
4749
Error Box_dref::write(StreamWriter& writer) const
4750
0
{
4751
0
  size_t box_start = reserve_box_header_space(writer);
4752
4753
0
  if (m_children.size() > 0xFFFF) {
4754
0
    return {
4755
0
      heif_error_Usage_error,
4756
0
      heif_suberror_Unspecified,
4757
0
      "Too many dref children boxes."
4758
0
    };
4759
0
  }
4760
4761
0
  writer.write32(static_cast<uint32_t>(m_children.size()));
4762
0
  write_children(writer);
4763
4764
0
  prepend_header(writer, box_start);
4765
0
  return Error::Ok;
4766
0
}
4767
4768
4769
void Box_dref::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4770
0
{
4771
0
  Box::dump(sstr, indent, full_log);
4772
0
  dump_children(sstr, indent, full_log);
4773
4774
0
  return;
4775
0
}
4776
4777
4778
Error Box_url::parse(BitstreamRange& range, const heif_security_limits* limits)
4779
28
{
4780
28
  parse_full_box_header(range);
4781
4782
28
  if (get_version() > 0) {
4783
1
    return unsupported_version_error("url");
4784
1
  }
4785
4786
27
  if (get_flags() & 1) {
4787
    // data in same file
4788
7
    m_location.clear();
4789
7
  }
4790
20
  else {
4791
20
    m_location = range.read_string();
4792
20
  }
4793
4794
27
  return range.get_error();
4795
28
}
4796
4797
4798
Error Box_url::write(StreamWriter& writer) const
4799
0
{
4800
0
  size_t box_start = reserve_box_header_space(writer);
4801
4802
0
  if (m_location.empty()) {
4803
0
    assert(get_flags() == 1);
4804
0
  }
4805
0
  else {
4806
0
    assert(get_flags() == 0);
4807
0
    writer.write(m_location);
4808
0
  }
4809
4810
0
  prepend_header(writer, box_start);
4811
0
  return Error::Ok;
4812
0
}
4813
4814
4815
void Box_url::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4816
0
{
4817
0
  Box::dump(sstr, indent, full_log);
4818
  //dump_children(sstr, indent, full_log);
4819
4820
0
  sstr << indent << "location: " << m_location << "\n";
4821
4822
0
  return;
4823
0
}
4824
4825
4826
Error Box_udes::parse(BitstreamRange& range, const heif_security_limits* limits)
4827
85
{
4828
85
  parse_full_box_header(range);
4829
4830
85
  if (get_version() > 0) {
4831
23
    return unsupported_version_error("udes");
4832
23
  }
4833
4834
62
  m_lang = range.read_string();
4835
62
  m_name = range.read_string();
4836
62
  m_description = range.read_string();
4837
62
  m_tags = range.read_string();
4838
62
  return range.get_error();
4839
85
}
4840
4841
void Box_udes::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4842
0
{
4843
0
  Box::dump(sstr, indent, full_log);
4844
0
  sstr << indent << "lang: " << m_lang << "\n";
4845
0
  sstr << indent << "name: " << m_name << "\n";
4846
0
  sstr << indent << "description: " << m_description << "\n";
4847
0
  sstr << indent << "tags: " << m_tags << "\n";
4848
0
  return;
4849
0
}
4850
4851
Error Box_udes::write(StreamWriter& writer) const
4852
0
{
4853
0
  size_t box_start = reserve_box_header_space(writer);
4854
0
  writer.write(m_lang);
4855
0
  writer.write(m_name);
4856
0
  writer.write(m_description);
4857
0
  writer.write(m_tags);
4858
0
  prepend_header(writer, box_start);
4859
0
  return Error::Ok;
4860
0
}
4861
4862
4863
void Box_cmin::RelativeIntrinsicMatrix::compute_focal_length(int image_width, int image_height,
4864
                                                             double& out_focal_length_x, double& out_focal_length_y) const
4865
6
{
4866
6
  out_focal_length_x = focal_length_x * image_width;
4867
4868
6
  if (is_anisotropic) {
4869
3
    out_focal_length_y = focal_length_y * image_height;
4870
3
  }
4871
3
  else {
4872
3
    out_focal_length_y = out_focal_length_x;
4873
3
  }
4874
6
}
4875
4876
4877
void Box_cmin::RelativeIntrinsicMatrix::compute_principal_point(int image_width, int image_height,
4878
                                                                double& out_principal_point_x, double& out_principal_point_y) const
4879
6
{
4880
6
  out_principal_point_x = principal_point_x * image_width;
4881
6
  out_principal_point_y = principal_point_y * image_height;
4882
6
}
4883
4884
4885
Box_cmin::AbsoluteIntrinsicMatrix Box_cmin::RelativeIntrinsicMatrix::to_absolute(int image_width, int image_height) const
4886
6
{
4887
6
  AbsoluteIntrinsicMatrix m{};
4888
6
  compute_focal_length(image_width, image_height, m.focal_length_x, m.focal_length_y);
4889
6
  compute_principal_point(image_width, image_height, m.principal_point_x, m.principal_point_y);
4890
6
  m.skew = skew;
4891
4892
6
  return m;
4893
6
}
4894
4895
4896
void Box_cmin::dump(std::ostream& sstr, Indent& indent, bool full_log) const
4897
0
{
4898
0
  Box::dump(sstr, indent, full_log);
4899
0
  sstr << indent << "principal-point: " << m_matrix.principal_point_x << ", " << m_matrix.principal_point_y << "\n";
4900
0
  if (m_matrix.is_anisotropic) {
4901
0
    sstr << indent << "focal-length: " << m_matrix.focal_length_x << ", " << m_matrix.focal_length_y << "\n";
4902
0
    sstr << indent << "skew: " << m_matrix.skew << "\n";
4903
0
  }
4904
0
  else {
4905
0
    sstr << indent << "focal-length: " << m_matrix.focal_length_x << "\n";
4906
0
    sstr << indent << "no skew\n";
4907
0
  }
4908
4909
0
  return;
4910
0
}
4911
4912
4913
Error Box_cmin::parse(BitstreamRange& range, const heif_security_limits* limits)
4914
105
{
4915
105
  parse_full_box_header(range);
4916
4917
105
  if (get_version() > 0) {
4918
67
    return unsupported_version_error("cmin");
4919
67
  }
4920
4921
38
  m_denominatorShift = (get_flags() & 0x1F00) >> 8;
4922
38
  uint32_t denominator = (1U << m_denominatorShift);
4923
4924
38
  m_matrix.focal_length_x = range.read32s() / (double)denominator;
4925
38
  m_matrix.principal_point_x = range.read32s() / (double)denominator;
4926
38
  m_matrix.principal_point_y = range.read32s() / (double)denominator;
4927
4928
38
  if (get_flags() & 1) {
4929
28
    m_skewDenominatorShift = ((get_flags()) & 0x1F0000) >> 16;
4930
28
    uint32_t skewDenominator = (1U << m_skewDenominatorShift);
4931
4932
28
    m_matrix.focal_length_y = range.read32s() / (double)denominator;
4933
28
    m_matrix.skew = range.read32s() / (double)skewDenominator;
4934
4935
28
    m_matrix.is_anisotropic = true;
4936
28
  }
4937
10
  else {
4938
10
    m_matrix.is_anisotropic = false;
4939
10
    m_matrix.focal_length_y = 0;
4940
10
    m_matrix.skew = 0;
4941
10
  }
4942
38
  return range.get_error();
4943
105
}
4944
4945
4946
static uint32_t get_signed_fixed_point_shift(double v)
4947
0
{
4948
0
  if (v==0) {
4949
0
    return 31;
4950
0
  }
4951
4952
0
  v = std::abs(v);
4953
4954
0
  uint32_t shift = 0;
4955
0
  while (v < (1<<30)) {
4956
0
    v *= 2;
4957
0
    shift++;
4958
4959
0
    if (shift==31) {
4960
0
      return shift;
4961
0
    }
4962
0
  }
4963
4964
0
  return shift;
4965
0
}
4966
4967
4968
void Box_cmin::set_intrinsic_matrix(RelativeIntrinsicMatrix matrix)
4969
0
{
4970
0
  m_matrix = matrix;
4971
4972
0
  uint32_t flags = 0;
4973
0
  flags |= matrix.is_anisotropic ? 1 : 0;
4974
4975
0
  uint32_t shift_fx = get_signed_fixed_point_shift(matrix.focal_length_x);
4976
0
  uint32_t shift_px = get_signed_fixed_point_shift(matrix.principal_point_x);
4977
0
  uint32_t shift_py = get_signed_fixed_point_shift(matrix.principal_point_y);
4978
0
  m_denominatorShift = std::min(std::min(shift_fx, shift_px), shift_py);
4979
4980
0
  if (matrix.is_anisotropic) {
4981
0
    uint32_t shift_fy = get_signed_fixed_point_shift(matrix.focal_length_y);
4982
0
    m_denominatorShift = std::min(m_denominatorShift, shift_fy);
4983
4984
0
    m_skewDenominatorShift = get_signed_fixed_point_shift(matrix.skew);
4985
0
  }
4986
0
  else {
4987
0
    m_skewDenominatorShift = 0;
4988
0
  }
4989
4990
0
  flags |= (m_denominatorShift << 8);
4991
0
  flags |= (m_skewDenominatorShift << 16);
4992
4993
0
  set_flags(flags);
4994
0
}
4995
4996
4997
Error Box_cmin::write(StreamWriter& writer) const
4998
0
{
4999
0
  size_t box_start = reserve_box_header_space(writer);
5000
5001
0
  uint32_t denominator = (1U << m_denominatorShift);
5002
5003
0
  writer.write32s(static_cast<int32_t>(m_matrix.focal_length_x * denominator));
5004
0
  writer.write32s(static_cast<int32_t>(m_matrix.principal_point_x * denominator));
5005
0
  writer.write32s(static_cast<int32_t>(m_matrix.principal_point_y * denominator));
5006
5007
0
  if (get_flags() & 1) {
5008
0
    writer.write32s(static_cast<int32_t>(m_matrix.focal_length_y * denominator));
5009
5010
0
    uint32_t skewDenominator = (1U << m_skewDenominatorShift);
5011
0
    writer.write32s(static_cast<int32_t>(m_matrix.skew * skewDenominator));
5012
0
  }
5013
5014
0
  prepend_header(writer, box_start);
5015
5016
0
  return Error::Ok;
5017
0
}
5018
5019
5020
static std::array<double,9> mul(const std::array<double,9>& a, const std::array<double,9>& b)
5021
0
{
5022
0
  std::array<double, 9> m{};
5023
5024
0
  m[0] = a[0]*b[0] + a[1]*b[3] + a[2]*b[6];
5025
0
  m[1] = a[0]*b[1] + a[1]*b[4] + a[2]*b[7];
5026
0
  m[2] = a[0]*b[2] + a[1]*b[5] + a[2]*b[8];
5027
5028
0
  m[3] = a[3]*b[0] + a[4]*b[3] + a[5]*b[6];
5029
0
  m[4] = a[3]*b[1] + a[4]*b[4] + a[5]*b[7];
5030
0
  m[5] = a[3]*b[2] + a[4]*b[5] + a[5]*b[8];
5031
5032
0
  m[6] = a[6]*b[0] + a[7]*b[3] + a[8]*b[6];
5033
0
  m[7] = a[6]*b[1] + a[7]*b[4] + a[8]*b[7];
5034
0
  m[8] = a[6]*b[2] + a[7]*b[5] + a[8]*b[8];
5035
5036
0
  return m;
5037
0
}
5038
5039
5040
std::array<double,9> Box_cmex::ExtrinsicMatrix::calculate_rotation_matrix() const
5041
0
{
5042
0
  std::array<double,9> m{};
5043
5044
0
  if (rotation_as_quaternions) {
5045
0
    double qx = quaternion_x;
5046
0
    double qy = quaternion_y;
5047
0
    double qz = quaternion_z;
5048
0
    double qw = quaternion_w;
5049
5050
0
    m[0] = 1-2*(qy*qy+qz*qz);
5051
0
    m[1] = 2*(qx*qy-qz*qw);
5052
0
    m[2] = 2*(qx*qz+qy*qw);
5053
0
    m[3] = 2*(qx*qy+qz*qw);
5054
0
    m[4] = 1-2*(qx*qx+qz*qz);
5055
0
    m[5] = 2*(qy*qz-qx*qw);
5056
0
    m[6] = 2*(qx*qz-qy*qw);
5057
0
    m[7] = 2*(qy*qz+qx*qw);
5058
0
    m[8] = 1-2*(qx*qx+qy*qy);
5059
0
  }
5060
0
  else {
5061
    // This rotation order fits the conformance data
5062
    // https://github.com/MPEGGroup/FileFormatConformance
5063
    // branch m62054_extrinsics : FileFormatConformance/data/file_features/under_consideration/ex_in_trinsics/extrinsic_rotation
5064
5065
0
    std::array<double,9> m_yaw{};    // Z
5066
0
    std::array<double,9> m_pitch{};  // Y
5067
0
    std::array<double,9> m_roll{};   // X
5068
5069
0
    const double d2r = M_PI/180;
5070
5071
0
    double x = d2r * rotation_roll;
5072
0
    double y = d2r * rotation_pitch;
5073
0
    double z = d2r * rotation_yaw;
5074
5075
    // X
5076
0
    m_roll[0] = 1;
5077
0
    m_roll[4] = m_roll[8] = cos(x);
5078
0
    m_roll[5] = -sin(x);
5079
0
    m_roll[7] = sin(x);
5080
5081
    // Y
5082
0
    m_pitch[4] = 1;
5083
0
    m_pitch[0] = m_pitch[8] = cos(y);
5084
0
    m_pitch[6] = -sin(y);
5085
0
    m_pitch[2] = sin(y);
5086
5087
    // Z
5088
0
    m_yaw[8] = 1;
5089
0
    m_yaw[0] = m_yaw[4] = cos(z);
5090
0
    m_yaw[1] = -sin(z);
5091
0
    m_yaw[3] = sin(z);
5092
5093
0
    m = mul(m_yaw, mul(m_pitch, m_roll));
5094
0
  }
5095
5096
0
  return m;
5097
0
}
5098
5099
5100
Error Box_cmex::parse(BitstreamRange& range, const heif_security_limits* limits)
5101
76
{
5102
76
  parse_full_box_header(range);
5103
5104
76
  if (get_version() > 0) {
5105
15
    return unsupported_version_error("cmex");
5106
15
  }
5107
5108
61
  m_matrix = ExtrinsicMatrix{};
5109
5110
61
  if (get_flags() & pos_x_present) {
5111
30
    m_has_pos_x = true;
5112
30
    m_matrix.pos_x = range.read32s();
5113
30
  }
5114
5115
61
  if (get_flags() & pos_y_present) {
5116
29
    m_has_pos_y = true;
5117
29
    m_matrix.pos_y = range.read32s();
5118
29
  }
5119
5120
61
  if (get_flags() & pos_z_present) {
5121
35
    m_has_pos_z = true;
5122
35
    m_matrix.pos_z = range.read32s();
5123
35
  }
5124
5125
61
  if (get_flags() & orientation_present) {
5126
30
    m_has_orientation = true;
5127
5128
30
    if (get_version() == 0) {
5129
30
      bool use32bit = (get_flags() & rot_large_field_size);
5130
30
      int32_t quat_x = use32bit ? range.read32s() : range.read16s();
5131
30
      int32_t quat_y = use32bit ? range.read32s() : range.read16s();
5132
30
      int32_t quat_z = use32bit ? range.read32s() : range.read16s();
5133
5134
30
      uint32_t div = 1U << (14 + (use32bit ? 16 : 0));
5135
5136
30
      m_matrix.rotation_as_quaternions = true;
5137
30
      m_matrix.quaternion_x = quat_x / (double)div;
5138
30
      m_matrix.quaternion_y = quat_y / (double)div;
5139
30
      m_matrix.quaternion_z = quat_z / (double)div;
5140
5141
30
      double q_sum = (m_matrix.quaternion_x * m_matrix.quaternion_x +
5142
30
                      m_matrix.quaternion_y * m_matrix.quaternion_y +
5143
30
                      m_matrix.quaternion_z * m_matrix.quaternion_z);
5144
5145
30
      if (q_sum > 1.0) {
5146
16
        return Error(heif_error_Invalid_input,
5147
16
                     heif_suberror_Unspecified,
5148
16
                     "Invalid quaternion in extrinsic rotation matrix");
5149
16
      }
5150
5151
14
      m_matrix.quaternion_w = sqrt(1 - q_sum);
5152
5153
14
    } else if (get_version() == 1) {
5154
0
      uint32_t div = 1<<16;
5155
0
      m_matrix.rotation_yaw = range.read32s() / (double)div;
5156
0
      m_matrix.rotation_pitch = range.read32s() / (double)div;
5157
0
      m_matrix.rotation_roll = range.read32s() / (double)div;
5158
0
    }
5159
30
  }
5160
5161
45
  if (get_flags() & id_present) {
5162
31
    m_has_world_coordinate_system_id = true;
5163
31
    m_matrix.world_coordinate_system_id = range.read32();
5164
31
  }
5165
5166
45
  return range.get_error();
5167
61
}
5168
5169
5170
void Box_cmex::dump(std::ostream& sstr, Indent& indent, bool full_log) const
5171
0
{
5172
0
  Box::dump(sstr, indent, full_log);
5173
0
  sstr << indent << "camera position (um): ";
5174
0
  sstr << m_matrix.pos_x << " ; ";
5175
0
  sstr << m_matrix.pos_y << " ; ";
5176
0
  sstr << m_matrix.pos_z << "\n";
5177
5178
0
  sstr << indent << "orientation ";
5179
0
  if (m_matrix.rotation_as_quaternions) {
5180
0
    sstr << "(quaterion)\n";
5181
0
    sstr << indent << "  q = ["
5182
0
         << m_matrix.quaternion_x << ";"
5183
0
         << m_matrix.quaternion_y << ";"
5184
0
         << m_matrix.quaternion_z << ";"
5185
0
         << m_matrix.quaternion_w << "]\n";
5186
0
  }
5187
0
  else {
5188
0
    sstr << "(angles)\n";
5189
0
    sstr << indent << "  yaw:   " << m_matrix.rotation_yaw << "\n";
5190
0
    sstr << indent << "  pitch: " << m_matrix.rotation_pitch << "\n";
5191
0
    sstr << indent << "  roll:  " << m_matrix.rotation_roll << "\n";
5192
0
  }
5193
5194
0
  sstr << indent << "world coordinate system id: " << m_matrix.world_coordinate_system_id << "\n";
5195
5196
0
  return;
5197
0
}
5198
5199
5200
5201
5202
Error Box_cmex::set_extrinsic_matrix(ExtrinsicMatrix matrix)
5203
0
{
5204
0
  m_matrix = matrix;
5205
5206
0
  uint32_t flags = 0;
5207
5208
0
  m_has_pos_x = (matrix.pos_x != 0);
5209
0
  m_has_pos_y = (matrix.pos_y != 0);
5210
0
  m_has_pos_z = (matrix.pos_z != 0);
5211
5212
0
  if (m_has_pos_x) {
5213
0
    flags |= pos_x_present;
5214
0
  }
5215
5216
0
  if (m_has_pos_y) {
5217
0
    flags |= pos_y_present;
5218
0
  }
5219
5220
0
  if (m_has_pos_z) {
5221
0
    flags |= pos_z_present;
5222
0
  }
5223
5224
0
  if (matrix.rotation_as_quaternions) {
5225
0
    if (matrix.quaternion_x != 0 ||
5226
0
        matrix.quaternion_y != 0 ||
5227
0
        matrix.quaternion_z != 0) {
5228
0
      flags |= orientation_present;
5229
5230
0
      double q_sum = (m_matrix.quaternion_x * m_matrix.quaternion_x +
5231
0
                      m_matrix.quaternion_y * m_matrix.quaternion_y +
5232
0
                      m_matrix.quaternion_z * m_matrix.quaternion_z);
5233
5234
0
      if (q_sum > 1.0) {
5235
0
        return Error(heif_error_Invalid_input,
5236
0
                     heif_suberror_Unspecified,
5237
0
                     "Invalid quaternion in extrinsic rotation matrix");
5238
0
      }
5239
5240
0
      if (matrix.quaternion_w < 0) {
5241
0
        matrix.quaternion_x = -matrix.quaternion_x;
5242
0
        matrix.quaternion_y = -matrix.quaternion_y;
5243
0
        matrix.quaternion_z = -matrix.quaternion_z;
5244
0
        matrix.quaternion_w = -matrix.quaternion_w;
5245
0
      }
5246
0
    }
5247
0
  }
5248
0
  else {
5249
0
    if (matrix.rotation_yaw != 0 ||
5250
0
        matrix.rotation_pitch != 0 ||
5251
0
        matrix.rotation_roll != 0) {
5252
0
      flags |= orientation_present;
5253
5254
0
      if (matrix.rotation_yaw < -180.0 || matrix.rotation_yaw >= 180.0) {
5255
0
        return Error(heif_error_Invalid_input,
5256
0
                     heif_suberror_Unspecified,
5257
0
                     "Invalid yaw angle");
5258
0
      }
5259
5260
0
      if (matrix.rotation_pitch < -90.0 || matrix.rotation_pitch > 90.0) {
5261
0
        return Error(heif_error_Invalid_input,
5262
0
                     heif_suberror_Unspecified,
5263
0
                     "Invalid pitch angle");
5264
0
      }
5265
5266
0
      if (matrix.rotation_roll < -180.0 || matrix.rotation_roll >= 180.0) {
5267
0
        return Error(heif_error_Invalid_input,
5268
0
                     heif_suberror_Unspecified,
5269
0
                     "Invalid roll angle");
5270
0
      }
5271
0
    }
5272
0
  }
5273
5274
0
  if (matrix.orientation_is_32bit) {
5275
0
    flags |= rot_large_field_size;
5276
0
  }
5277
5278
0
  if (matrix.world_coordinate_system_id != 0) {
5279
0
    flags |= id_present;
5280
0
  }
5281
5282
0
  set_flags(flags);
5283
0
  set_version(m_matrix.rotation_as_quaternions ? 0 : 1);
5284
5285
0
  return Error::Ok;
5286
0
}
5287
5288
5289
Error Box_cmex::write(StreamWriter& writer) const
5290
0
{
5291
0
  size_t box_start = reserve_box_header_space(writer);
5292
5293
0
  if (m_has_pos_x) {
5294
0
    writer.write32s(m_matrix.pos_x);
5295
0
  }
5296
5297
0
  if (m_has_pos_y) {
5298
0
    writer.write32s(m_matrix.pos_y);
5299
0
  }
5300
5301
0
  if (m_has_pos_z) {
5302
0
    writer.write32s(m_matrix.pos_z);
5303
0
  }
5304
5305
0
  if (m_has_orientation) {
5306
0
    if (m_matrix.rotation_as_quaternions) {
5307
0
      if (m_matrix.orientation_is_32bit) {
5308
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_x * (1<<30)));
5309
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_y * (1<<30)));
5310
0
        writer.write32s(static_cast<int32_t>(m_matrix.quaternion_z * (1<<30)));
5311
0
      }
5312
0
      else {
5313
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_x * (1<<14)));
5314
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_y * (1<<14)));
5315
0
        writer.write16s(static_cast<int16_t>(m_matrix.quaternion_z * (1<<14)));
5316
0
      }
5317
0
    }
5318
0
    else {
5319
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_yaw * (1<<16)));
5320
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_pitch * (1<<16)));
5321
0
      writer.write32s(static_cast<int32_t>(m_matrix.rotation_roll * (1<<16)));
5322
0
    }
5323
0
  }
5324
5325
0
  if (m_has_world_coordinate_system_id) {
5326
0
    writer.write32(m_matrix.world_coordinate_system_id);
5327
0
  }
5328
5329
5330
0
  prepend_header(writer, box_start);
5331
5332
0
  return Error::Ok;
5333
0
}
5334
5335
5336
std::string Box_taic::dump(const heif_tai_clock_info& info, Indent& indent)
5337
0
{
5338
0
  std::ostringstream sstr;
5339
0
  sstr << indent << "time_uncertainty: ";
5340
0
  if (info.time_uncertainty == heif_tai_clock_info_time_uncertainty_unknown) {
5341
0
    sstr << "unknown\n";
5342
0
  }
5343
0
  else {
5344
0
    sstr << info.time_uncertainty << "\n";
5345
0
  }
5346
0
  sstr << indent << "clock_resolution: " << info.clock_resolution << "\n";
5347
0
  sstr << indent << "clock_drift_rate: ";
5348
0
  if (info.clock_drift_rate == heif_tai_clock_info_clock_drift_rate_unknown) {
5349
0
    sstr << "undefined\n";
5350
0
  }
5351
0
  else {
5352
0
    sstr << info.clock_drift_rate << "\n";
5353
0
  }
5354
5355
0
  sstr << indent << "clock_type: " << int(info.clock_type) << " ";
5356
0
  switch (info.clock_type) {
5357
0
    case heif_tai_clock_info_clock_type_unknown: sstr << "(unknown)\n"; break;
5358
0
    case heif_tai_clock_info_clock_type_synchronized_to_atomic_source: sstr << "(synchronized to atomic source)\n"; break;
5359
0
    case heif_tai_clock_info_clock_type_not_synchronized_to_atomic_source: sstr << "(not synchronized to atomic source)\n"; break;
5360
0
    default: sstr << "(illegal value)\n"; break;;
5361
0
  }
5362
0
  return sstr.str();
5363
0
}
5364
5365
5366
0
void Box_taic::dump(std::ostream& sstr, Indent& indent, bool full_log) const {
5367
0
  Box::dump(sstr, indent, full_log);
5368
0
  sstr << dump(m_info, indent);
5369
5370
0
  return;
5371
0
}
5372
5373
0
Error Box_taic::write(StreamWriter& writer) const {
5374
0
  size_t box_start = reserve_box_header_space(writer);
5375
0
  writer.write64(m_info.time_uncertainty);
5376
0
  writer.write32(m_info.clock_resolution);
5377
0
  writer.write32(m_info.clock_drift_rate);
5378
0
  writer.write8(static_cast<uint8_t>(m_info.clock_type << 6));
5379
5380
0
  prepend_header(writer, box_start);
5381
5382
0
  return Error::Ok;
5383
0
}
5384
5385
10
Error Box_taic::parse(BitstreamRange& range, const heif_security_limits*) {
5386
10
  parse_full_box_header(range);
5387
5388
10
  m_info.time_uncertainty = range.read64();
5389
10
  m_info.clock_resolution = range.read32();
5390
5391
10
  m_info.clock_drift_rate = range.read32s();
5392
10
  m_info.clock_type = range.read8() >> 6;
5393
10
  return range.get_error();
5394
10
}
5395
5396
5397
bool operator==(const heif_tai_clock_info& a,
5398
                const heif_tai_clock_info& b)
5399
0
{
5400
0
  return a.version == b.version &&
5401
0
         a.time_uncertainty == b.time_uncertainty &&
5402
0
         a.clock_resolution == b.clock_resolution &&
5403
0
         a.clock_drift_rate == b.clock_drift_rate &&
5404
0
         a.clock_type == b.clock_type;
5405
0
}
5406
5407
bool Box_taic::operator==(const Box& other) const
5408
0
{
5409
0
  const auto* other_taic = dynamic_cast<const Box_taic*>(&other);
5410
0
  if (other_taic == nullptr) {
5411
0
    return false;
5412
0
  }
5413
5414
0
  return m_info == other_taic->m_info;
5415
0
}
5416
5417
5418
0
void Box_itai::dump(std::ostream& sstr, Indent& indent, bool full_log) const {
5419
0
  Box::dump(sstr, indent, full_log);
5420
0
  sstr << indent << "tai_timestamp: " << m_timestamp.tai_timestamp << "\n";
5421
0
  sstr << indent << "synchronization_state: " << int(m_timestamp.synchronization_state) << "\n";
5422
0
  sstr << indent << "timestamp_generation_failure: " << int(m_timestamp.timestamp_generation_failure) << "\n";
5423
0
  sstr << indent << "timestamp_is_modified: " << int(m_timestamp.timestamp_is_modified) << "\n";
5424
0
  return;
5425
0
}
5426
5427
5428
std::vector<uint8_t> Box_itai::encode_tai_to_bitstream(const heif_tai_timestamp_packet* tai)
5429
0
{
5430
0
  StreamWriter writer;
5431
0
  writer.write64(tai->tai_timestamp);
5432
5433
0
  uint8_t status_bits = 0;
5434
0
  status_bits |= tai->synchronization_state ? (1 << 7) : 0;
5435
0
  status_bits |= tai->timestamp_generation_failure ? (1 << 6) : 0;
5436
0
  status_bits |= tai->timestamp_is_modified ? (1 << 5) : 0;
5437
5438
0
  writer.write8(status_bits);
5439
5440
0
  return writer.get_data();
5441
0
}
5442
5443
bool operator==(const heif_tai_timestamp_packet& a,
5444
                const heif_tai_timestamp_packet& b)
5445
0
{
5446
0
  return a.version == b.version &&
5447
0
         a.tai_timestamp == b.tai_timestamp &&
5448
0
         a.synchronization_state == b.synchronization_state &&
5449
0
         a.timestamp_generation_failure == b.timestamp_generation_failure &&
5450
0
         a.timestamp_is_modified == b.timestamp_is_modified;
5451
0
}
5452
5453
bool Box_itai::operator==(const Box& other) const
5454
0
{
5455
0
  const auto* other_itai = dynamic_cast<const Box_itai*>(&other);
5456
0
  if (other_itai == nullptr) {
5457
0
    return false;
5458
0
  }
5459
5460
0
  return m_timestamp == other_itai->m_timestamp;
5461
0
}
5462
5463
5464
5465
uint64_t uint8_vector_to_uint64_BE(const uint8_t* data)
5466
0
{
5467
0
  uint64_t value = ((static_cast<uint64_t>(data[0]) << 56) |
5468
0
                    (static_cast<uint64_t>(data[1]) << 48) |
5469
0
                    (static_cast<uint64_t>(data[2]) << 40) |
5470
0
                    (static_cast<uint64_t>(data[3]) << 32) |
5471
0
                    (static_cast<uint64_t>(data[4]) << 24) |
5472
0
                    (static_cast<uint64_t>(data[5]) << 16) |
5473
0
                    (static_cast<uint64_t>(data[6]) << 8) |
5474
0
                    (static_cast<uint64_t>(data[7]) << 0));
5475
5476
0
  return value;
5477
0
}
5478
5479
5480
Result<heif_tai_timestamp_packet> Box_itai::decode_tai_from_vector(const std::vector<uint8_t>& data)
5481
0
{
5482
0
  if (data.size() != 9) {
5483
0
    return Error{heif_error_Invalid_input,
5484
0
                 heif_suberror_Unspecified,
5485
0
                 "Wrong size of TAI timestamp data"};
5486
0
  }
5487
5488
0
  uint8_t status_bits = data[8];
5489
5490
0
  heif_tai_timestamp_packet tai;
5491
0
  tai.version = 1;
5492
0
  tai.tai_timestamp = uint8_vector_to_uint64_BE(data.data());
5493
0
  tai.synchronization_state = !!(status_bits & 0x80);
5494
0
  tai.timestamp_generation_failure = !!(status_bits & 0x40);
5495
0
  tai.timestamp_is_modified = !!(status_bits & 0x20);
5496
5497
0
  return tai;
5498
0
}
5499
5500
5501
0
Error Box_itai::write(StreamWriter& writer) const {
5502
0
  size_t box_start = reserve_box_header_space(writer);
5503
5504
0
  std::vector<uint8_t> tai_data = encode_tai_to_bitstream(&m_timestamp);
5505
5506
0
  writer.write(tai_data);
5507
5508
0
  prepend_header(writer, box_start);
5509
0
  return Error::Ok;
5510
0
}
5511
5512
5
Error Box_itai::parse(BitstreamRange& range, const heif_security_limits*) {
5513
5
  parse_full_box_header(range);
5514
5515
5
  m_timestamp.version = 1;
5516
5
  m_timestamp.tai_timestamp = range.read64();
5517
5518
5
  uint8_t status_bits = range.read8();
5519
5520
5
  m_timestamp.synchronization_state = !!(status_bits & 0x80);
5521
5
  m_timestamp.timestamp_generation_failure = !!(status_bits & 0x40);
5522
5
  m_timestamp.timestamp_is_modified = !!(status_bits & 0x20);
5523
5524
5
  return range.get_error();
5525
5
}
5526
5527
Error Box_elng::parse(BitstreamRange& range, const heif_security_limits* limits)
5528
16
{
5529
16
  parse_full_box_header(range);
5530
5531
16
  if (get_version() > 0) {
5532
3
    return unsupported_version_error("elng");
5533
3
  }
5534
5535
13
  m_lang = range.read_string();
5536
13
  return range.get_error();
5537
16
}
5538
5539
void Box_elng::dump(std::ostream& sstr, Indent& indent, bool full_log) const
5540
0
{
5541
0
  Box::dump(sstr, indent, full_log);
5542
0
  sstr << indent << "extended_language: " << m_lang << "\n";
5543
0
  return;
5544
0
}
5545
5546
Error Box_elng::write(StreamWriter& writer) const
5547
0
{
5548
0
  size_t box_start = reserve_box_header_space(writer);
5549
0
  writer.write(m_lang);
5550
0
  prepend_header(writer, box_start);
5551
0
  return Error::Ok;
5552
0
}
5553
5554
5555
Error Box_gimi_content_id::parse(BitstreamRange& range, const heif_security_limits* limits)
5556
16
{
5557
16
  m_content_id = range.read_string_until_eof();
5558
5559
16
  return range.get_error();
5560
16
}
5561
5562
5563
Error Box_gimi_content_id::write(StreamWriter& writer) const
5564
0
{
5565
0
  size_t box_start = reserve_box_header_space(writer);
5566
5567
0
  writer.write(m_content_id, false);
5568
5569
0
  prepend_header(writer, box_start);
5570
5571
0
  return Error::Ok;
5572
0
}
5573
5574
5575
void Box_gimi_content_id::dump(std::ostream& sstr, Indent& indent, bool full_log) const
5576
0
{
5577
0
  Box::dump(sstr, indent, full_log);
5578
5579
0
  sstr << indent << "content ID: " << m_content_id << "\n";
5580
5581
0
  return;
5582
0
}