Coverage Report

Created: 2026-06-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/file.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
21
#include "file.h"
22
#include "box.h"
23
#include "libheif/heif.h"
24
#include "libheif/heif_properties.h"
25
#include "compression.h"
26
#include "image-items/grid.h"
27
#include "image-items/jpeg2000.h"
28
#include "image-items/jpeg.h"
29
#include "image-items/overlay.h"
30
#include "image-items/vvc.h"
31
#include "codecs/avif_boxes.h"
32
#include "codecs/hevc_boxes.h"
33
#include "sequences/seq_boxes.h"
34
#include "mini.h"
35
36
#include <cstdint>
37
#include <fstream>
38
#include <limits>
39
#include <sstream>
40
#include <utility>
41
#include <cstring>
42
#include <cassert>
43
#include <algorithm>
44
45
#include "libheif/heif_cxx.h"
46
47
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
48
49
#ifndef NOMINMAX
50
#define NOMINMAX 1
51
#endif
52
53
#include <windows.h>
54
#endif
55
56
57
#if WITH_UNCOMPRESSED_CODEC
58
#include "image-items/unc_image.h"
59
#endif
60
61
// TODO: make this a decoder option
62
23.8k
#define STRICT_PARSING false
63
64
65
HeifFile::HeifFile()
66
24.7k
{
67
24.7k
  m_file_layout = std::make_shared<FileLayout>();
68
24.7k
}
69
70
24.7k
HeifFile::~HeifFile() = default;
71
72
std::vector<heif_item_id> HeifFile::get_item_IDs() const
73
11.8k
{
74
11.8k
  std::vector<heif_item_id> IDs;
75
76
24.1k
  for (const auto& infe : m_infe_boxes) {
77
24.1k
    IDs.push_back(infe.second->get_item_ID());
78
24.1k
  }
79
80
11.8k
  return IDs;
81
11.8k
}
82
83
84
std::shared_ptr<const Box_infe> HeifFile::get_infe_box(heif_item_id ID) const
85
117k
{
86
117k
  auto iter = m_infe_boxes.find(ID);
87
117k
  if (iter == m_infe_boxes.end()) {
88
103
    return nullptr;
89
103
  }
90
91
117k
  return iter->second;
92
117k
}
93
94
95
std::shared_ptr<Box_infe> HeifFile::get_infe_box(heif_item_id ID)
96
56.6k
{
97
56.6k
  auto iter = m_infe_boxes.find(ID);
98
56.6k
  if (iter == m_infe_boxes.end()) {
99
0
    return nullptr;
100
0
  }
101
102
56.6k
  return iter->second;
103
56.6k
}
104
105
106
Error HeifFile::read_from_file(const char* input_filename)
107
0
{
108
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
109
  auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(convert_utf8_path_to_utf16(input_filename).c_str(), std::ios_base::binary));
110
#else
111
0
  auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(input_filename, std::ios_base::binary));
112
0
#endif
113
0
  if (!input_stream_istr->good()) {
114
0
    std::stringstream sstr;
115
0
    sstr << "Error opening file: " << strerror(errno) << " (" << errno << ")\n";
116
0
    return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str());
117
0
  }
118
119
0
  auto input_stream = std::make_shared<StreamReader_istream>(std::move(input_stream_istr));
120
0
  return read(input_stream);
121
0
}
122
123
124
Error HeifFile::read_from_memory(const void* data, size_t size, bool copy)
125
12.3k
{
126
12.3k
  auto input_stream = std::make_shared<StreamReader_memory>((const uint8_t*) data, size, copy);
127
128
12.3k
  return read(input_stream);
129
12.3k
}
130
131
132
Error HeifFile::read(const std::shared_ptr<StreamReader>& reader)
133
12.3k
{
134
12.3k
  assert(m_limits);
135
136
12.3k
  m_input_stream = reader;
137
138
12.3k
  Error err;
139
12.3k
  err = m_file_layout->read(reader, m_limits);
140
12.3k
  if (err) {
141
346
    return err;
142
346
  }
143
144
12.0k
  Error error = parse_heif_file();
145
12.0k
  return error;
146
12.3k
}
147
148
149
bool HeifFile::has_images() const
150
23.7k
{
151
23.7k
  if (!m_meta_box) {
152
11
    return false;
153
11
  }
154
155
23.7k
  return m_hdlr_box && m_hdlr_box->get_handler_type() == fourcc("pict");
156
23.7k
}
157
158
159
void HeifFile::new_empty_file()
160
12.3k
{
161
  //m_input_stream.reset();
162
12.3k
  m_top_level_boxes.clear();
163
164
12.3k
  m_ftyp_box = std::make_shared<Box_ftyp>();
165
12.3k
  m_top_level_boxes.push_back(m_ftyp_box);
166
12.3k
}
167
168
169
void HeifFile::init_for_meta_item()
170
0
{
171
0
  if (!m_meta_box) {
172
0
    m_meta_box = std::make_shared<Box_meta>();
173
0
    m_top_level_boxes.push_back(m_meta_box);
174
0
  }
175
176
0
  if (!m_hdlr_box) {
177
0
    m_hdlr_box = std::make_shared<Box_hdlr>();
178
0
    m_hdlr_box->set_handler_type(fourcc("null"));
179
0
    m_meta_box->append_child_box(m_hdlr_box);
180
0
  }
181
182
0
  if (!m_iloc_box) {
183
0
    m_iloc_box = std::make_shared<Box_iloc>();
184
0
    m_meta_box->append_child_box(m_iloc_box);
185
0
  }
186
187
0
  if (!m_iinf_box) {
188
0
    m_iinf_box = std::make_shared<Box_iinf>();
189
0
    m_meta_box->append_child_box(m_iinf_box);
190
0
  }
191
0
}
192
193
194
void HeifFile::init_for_image()
195
0
{
196
0
  init_for_meta_item();
197
198
  // Upgrade handler from "null" to "pict" if needed
199
0
  if (m_hdlr_box->get_handler_type() == fourcc("null")) {
200
0
    m_hdlr_box->set_handler_type(fourcc("pict"));
201
0
  }
202
203
0
  if (!m_pitm_box) {
204
0
    m_pitm_box = std::make_shared<Box_pitm>();
205
0
    m_meta_box->append_child_box(m_pitm_box);
206
0
  }
207
208
0
  if (!m_iprp_box) {
209
0
    m_iprp_box = std::make_shared<Box_iprp>();
210
0
    m_meta_box->append_child_box(m_iprp_box);
211
0
  }
212
213
0
  if (!m_ipco_box) {
214
0
    m_ipco_box = std::make_shared<Box_ipco>();
215
0
    m_iprp_box->append_child_box(m_ipco_box);
216
0
  }
217
218
0
  if (!m_ipma_box) {
219
0
    m_ipma_box = std::make_shared<Box_ipma>();
220
0
    m_iprp_box->append_child_box(m_ipma_box);
221
0
  }
222
0
}
223
224
225
void HeifFile::init_for_sequence()
226
0
{
227
0
  if (m_moov_box) {
228
0
    return;
229
0
  }
230
231
0
  m_moov_box = std::make_shared<Box_moov>();
232
0
  m_top_level_boxes.push_back(m_moov_box);
233
234
0
  m_mvhd_box = std::make_shared<Box_mvhd>();
235
0
  m_moov_box->append_child_box(m_mvhd_box);
236
0
}
237
238
239
size_t HeifFile::append_mdat_data(const std::vector<uint8_t>& data)
240
0
{
241
0
  if (!m_mdat_data) {
242
0
    m_mdat_data = std::make_unique<MdatData_Memory>();
243
0
  }
244
245
0
  return m_mdat_data->append_data(data);
246
0
}
247
248
249
void HeifFile::derive_box_versions()
250
0
{
251
0
  for (auto& box : m_top_level_boxes) {
252
0
    box->derive_box_version_recursive();
253
0
  }
254
0
}
255
256
257
void HeifFile::write(StreamWriter& writer)
258
0
{
259
0
  if (m_write_mini_format) {
260
0
    std::string reason;
261
0
    if (Box_mini::can_convert_to_mini(this, reason)) {
262
0
      auto mini = Box_mini::create_from_heif_file(this);
263
0
      if (mini) {
264
        // Adjust ftyp for mini format
265
0
        auto ftyp = get_ftyp_box();
266
267
        // Determine codec brand from primary item type
268
0
        uint32_t item_type = get_item_type_4cc(get_primary_image_ID());
269
0
        heif_brand2 codec_brand = 0;
270
0
        if (item_type == fourcc("av01")) {
271
0
          codec_brand = heif_brand2_avif;
272
0
        }
273
0
        else if (item_type == fourcc("hvc1")) {
274
0
          codec_brand = heif_brand2_heic;
275
0
        }
276
277
0
        ftyp->set_major_brand(fourcc("mif3"));
278
0
        ftyp->set_minor_version(codec_brand);
279
0
        ftyp->clear_compatible_brands();
280
281
        // Write ftyp + mini (no mdat needed)
282
0
        ftyp->write(writer);
283
0
        mini->write(writer);
284
0
        return;
285
0
      }
286
0
    }
287
    // Fall through to normal write if conversion fails
288
0
  }
289
290
0
  for (auto& box : m_top_level_boxes) {
291
0
    if (box == nullptr) {
292
      // Either mini or meta will be null, just ignore that one
293
0
      continue;
294
0
    }
295
0
    Error err = box->write(writer);
296
0
    (void)err; // TODO: error ?
297
0
  }
298
299
0
  if (m_iloc_box) {
300
    // TODO: rewrite to use MdatData class
301
0
    Error err = m_iloc_box->write_mdat_after_iloc(writer);
302
0
    (void)err; // TODO: error ?
303
0
  }
304
305
0
  if (m_mdat_data) {
306
0
    Result<size_t> mdatResult = write_mdat(writer);
307
0
    if (!mdatResult) {
308
0
      return; // TODO: error ?
309
0
    }
310
311
0
    for (auto& box : m_top_level_boxes) {
312
0
      box->patch_file_pointers_recursively(writer, *mdatResult);
313
0
    }
314
0
  }
315
0
}
316
317
318
std::string HeifFile::debug_dump_boxes() const
319
0
{
320
0
  std::stringstream sstr;
321
322
0
  bool first = true;
323
324
0
  for (const auto& box : m_top_level_boxes) {
325
0
    if (box == nullptr) {
326
      // Either mini or meta will be null, just ignore that one
327
0
      continue;
328
0
    }
329
    // dump box content for debugging
330
331
0
    if (first) {
332
0
      first = false;
333
0
    }
334
0
    else {
335
0
      sstr << "\n";
336
0
    }
337
338
0
    Indent indent;
339
0
    sstr << box->dump(indent);
340
0
  }
341
342
0
  return sstr.str();
343
0
}
344
345
346
std::string HeifFile::debug_dump_item_data() const
347
0
{
348
0
  std::stringstream sstr;
349
0
  bool has_output = false;
350
351
0
  sstr << "=== Item Data ===\n";
352
353
0
  for (const auto& [id, infe] : m_infe_boxes) {
354
0
    uint32_t item_type = infe->get_item_type_4cc();
355
356
0
    if (item_type == fourcc("grid")) {
357
0
      auto dataResult = get_uncompressed_item_data(id);
358
0
      if (!dataResult) {
359
0
        continue;
360
0
      }
361
362
0
      ImageGrid grid;
363
0
      Error err = grid.parse(*dataResult);
364
0
      if (err) {
365
0
        continue;
366
0
      }
367
368
0
      has_output = true;
369
0
      sstr << "\nitem ID " << id << " (grid):\n";
370
371
0
      std::istringstream lines(grid.dump());
372
0
      std::string line;
373
0
      while (std::getline(lines, line)) {
374
0
        sstr << "  " << line << "\n";
375
0
      }
376
0
    }
377
0
    else if (item_type == fourcc("iovl")) {
378
0
      if (!m_iref_box) {
379
0
        continue;
380
0
      }
381
382
0
      auto refs = m_iref_box->get_references(id, fourcc("dimg"));
383
384
0
      auto dataResult = get_uncompressed_item_data(id);
385
0
      if (!dataResult) {
386
0
        continue;
387
0
      }
388
389
0
      ImageOverlay overlay;
390
0
      Error err = overlay.parse(refs.size(), *dataResult);
391
0
      if (err) {
392
0
        continue;
393
0
      }
394
395
0
      has_output = true;
396
0
      sstr << "\nitem ID " << id << " (iovl):\n";
397
398
0
      std::istringstream lines(overlay.dump());
399
0
      std::string line;
400
0
      while (std::getline(lines, line)) {
401
0
        sstr << "  " << line << "\n";
402
0
      }
403
0
    }
404
0
  }
405
406
0
  if (has_output) {
407
0
    return sstr.str();
408
0
  }
409
410
0
  return {};
411
0
}
412
413
414
Error HeifFile::parse_heif_file()
415
12.0k
{
416
  // --- read all top-level boxes
417
418
#if 0
419
  for (;;) {
420
    std::shared_ptr<Box> box;
421
    Error error = Box::read(range, &box);
422
423
    if (range.error() || range.eof()) {
424
      break;
425
    }
426
427
    // When an EOF error is returned, this is not really a fatal exception,
428
    // but simply the indication that we reached the end of the file.
429
    // TODO: this design should be cleaned up
430
    if (error.error_code == heif_error_Invalid_input && error.sub_error_code == heif_suberror_End_of_data) {
431
      break;
432
    }
433
434
    if (error != Error::Ok) {
435
      return error;
436
    }
437
438
    m_top_level_boxes.push_back(box);
439
440
441
    // extract relevant boxes (ftyp, meta)
442
443
    if (box->get_short_type() == fourcc("meta")) {
444
      m_meta_box = std::dynamic_pointer_cast<Box_meta>(box);
445
    }
446
447
    if (box->get_short_type() == fourcc("ftyp")) {
448
      m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(box);
449
    }
450
  }
451
#endif
452
453
12.0k
  m_ftyp_box = m_file_layout->get_ftyp_box();
454
12.0k
  if (!m_ftyp_box) {
455
19
    return Error(heif_error_Invalid_input,
456
19
                 heif_suberror_No_ftyp_box);
457
19
  }
458
459
12.0k
  m_top_level_boxes.push_back(m_ftyp_box);
460
461
12.0k
  bool is_sequence_brand = (m_ftyp_box->has_compatible_brand(heif_brand2_msf1) ||
462
12.0k
                            m_ftyp_box->has_compatible_brand(heif_brand2_isom) ||
463
12.0k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp41) ||
464
11.9k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp42));
465
466
  // --- check whether this is a HEIF file and its structural format
467
468
12.0k
  if (!m_ftyp_box->has_compatible_brand(heif_brand2_heic) &&
469
8.97k
      !m_ftyp_box->has_compatible_brand(heif_brand2_heix) &&
470
8.92k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mif1) &&
471
6.63k
      !m_ftyp_box->has_compatible_brand(heif_brand2_avif) &&
472
55
      !m_ftyp_box->has_compatible_brand(heif_brand2_1pic) &&
473
55
      !(m_ftyp_box->get_major_brand() == heif_brand2_mif3) &&
474
55
      !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg) &&
475
55
      !m_ftyp_box->has_compatible_brand(heif_brand2_isom) &&
476
55
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp42) &&
477
54
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp41) &&
478
43
      !m_ftyp_box->has_compatible_brand(heif_brand2_msf1)) {
479
43
    std::stringstream sstr;
480
43
    sstr << "File does not include any supported brands.\n";
481
482
43
    return Error(heif_error_Unsupported_filetype,
483
43
                 heif_suberror_Unspecified,
484
43
                 sstr.str());
485
43
      }
486
487
11.9k
  m_mini_box = m_file_layout->get_mini_box();
488
11.9k
  m_top_level_boxes.push_back(m_mini_box);
489
490
11.9k
  if (m_mini_box) {
491
39
    Error err = m_mini_box->create_expanded_boxes(this);
492
39
    if (err) {
493
4
      return err;
494
4
    }
495
35
    return Error::Ok;
496
39
  }
497
498
11.9k
  m_meta_box = m_file_layout->get_meta_box();
499
11.9k
  if (m_meta_box) {
500
11.9k
    m_top_level_boxes.push_back(m_meta_box);
501
11.9k
  }
502
503
  // TODO: we are missing 'mdat' top level boxes
504
505
11.9k
  m_moov_box = m_file_layout->get_moov_box();
506
11.9k
  if (m_moov_box) {
507
12
    m_top_level_boxes.push_back(m_moov_box);
508
12
  }
509
510
  // if we didn't find the mini box, meta is required
511
512
11.9k
  if (!m_meta_box && !is_sequence_brand) {
513
0
    return Error(heif_error_Invalid_input,
514
0
                 heif_suberror_No_meta_box);
515
0
  }
516
517
11.9k
  if (!m_moov_box && is_sequence_brand) {
518
1
    return Error(heif_error_Invalid_input,
519
1
                 heif_suberror_No_moov_box);
520
1
  }
521
522
11.9k
  if (m_meta_box) {
523
11.9k
    auto err = parse_heif_images();
524
11.9k
    if (err) {
525
54
      return err;
526
54
    }
527
11.9k
  }
528
529
11.8k
  if (m_moov_box) {
530
12
    auto err = parse_heif_sequences();
531
12
    if (err) {
532
1
      return err;
533
1
    }
534
12
  }
535
536
11.8k
  return Error::Ok;
537
11.8k
}
538
539
540
Error HeifFile::parse_heif_images()
541
11.9k
{
542
11.9k
  m_hdlr_box = m_meta_box->get_child_box<Box_hdlr>();
543
11.9k
  if (STRICT_PARSING && !m_hdlr_box) {
544
0
    return Error(heif_error_Invalid_input,
545
0
                 heif_suberror_No_hdlr_box);
546
0
  }
547
548
  // --- assign item boxes
549
550
11.9k
  m_iinf_box = m_meta_box->get_child_box<Box_iinf>();
551
11.9k
  if (!m_iinf_box) {
552
23
    return Error(heif_error_Invalid_input,
553
23
                 heif_suberror_No_iinf_box);
554
23
  }
555
556
11.8k
  std::vector<std::shared_ptr<Box_infe>> infe_boxes = m_iinf_box->get_child_boxes<Box_infe>();
557
558
24.6k
  for (auto& infe_box : infe_boxes) {
559
24.6k
    if (!infe_box) {
560
0
      return Error(heif_error_Invalid_input,
561
0
                   heif_suberror_No_infe_box);
562
0
    }
563
564
24.6k
    m_infe_boxes.insert(std::make_pair(infe_box->get_item_ID(), infe_box));
565
24.6k
  }
566
567
568
11.8k
  if (has_images()) {
569
    // --- find mandatory boxes needed for image decoding
570
571
11.8k
    m_pitm_box = m_meta_box->get_child_box<Box_pitm>();
572
11.8k
    if (!m_pitm_box) {
573
3
      return Error(heif_error_Invalid_input,
574
3
                   heif_suberror_No_pitm_box);
575
3
    }
576
577
11.8k
    m_iprp_box = m_meta_box->get_child_box<Box_iprp>();
578
11.8k
    if (!m_iprp_box) {
579
8
      return Error(heif_error_Invalid_input,
580
8
                   heif_suberror_No_iprp_box);
581
8
    }
582
583
11.8k
    m_ipco_box = m_iprp_box->get_child_box<Box_ipco>();
584
11.8k
    if (!m_ipco_box) {
585
2
      return Error(heif_error_Invalid_input,
586
2
                   heif_suberror_No_ipco_box);
587
2
    }
588
589
11.8k
    auto ipma_boxes = m_iprp_box->get_child_boxes<Box_ipma>();
590
11.8k
    if (ipma_boxes.empty()) {
591
1
      return Error(heif_error_Invalid_input,
592
1
                   heif_suberror_No_ipma_box);
593
1
    }
594
11.8k
    for (size_t i=1;i<ipma_boxes.size();i++) {
595
0
      ipma_boxes[0]->insert_entries_from_other_ipma_box(*ipma_boxes[i]);
596
0
    }
597
11.8k
    m_ipma_box = ipma_boxes[0];
598
11.8k
  }
599
600
11.8k
  m_iloc_box = m_meta_box->get_child_box<Box_iloc>();
601
11.8k
  if (!m_iloc_box) {
602
3
    return Error(heif_error_Invalid_input,
603
3
                 heif_suberror_No_iloc_box);
604
3
  }
605
606
11.8k
  m_idat_box = m_meta_box->get_child_box<Box_idat>();
607
608
11.8k
  m_iref_box = m_meta_box->get_child_box<Box_iref>();
609
11.8k
  if (m_iref_box && m_pitm_box) {
610
2.67k
    Error error = check_for_ref_cycle(get_primary_image_ID(), m_iref_box);
611
2.67k
    if (error) {
612
14
      return error;
613
14
    }
614
2.67k
  }
615
616
11.8k
  m_grpl_box = m_meta_box->get_child_box<Box_grpl>();
617
618
619
11.8k
  return Error::Ok;
620
11.8k
}
621
622
623
Error HeifFile::parse_heif_sequences()
624
12
{
625
12
  m_mvhd_box = m_moov_box->get_child_box<Box_mvhd>();
626
12
  if (!m_mvhd_box) {
627
1
    return {heif_error_Invalid_input,
628
1
            heif_suberror_Unspecified,
629
1
            "No mvhd box in image sequence."};
630
1
  }
631
632
11
  return Error::Ok;
633
12
}
634
635
636
Error HeifFile::check_for_ref_cycle(heif_item_id ID,
637
                                    const std::shared_ptr<Box_iref>& iref_box) const
638
2.67k
{
639
2.67k
  std::unordered_set<heif_item_id> parent_items;
640
2.67k
  return check_for_ref_cycle_recursion(ID, iref_box, parent_items);
641
2.67k
}
642
643
644
Error HeifFile::check_for_ref_cycle_recursion(heif_item_id ID,
645
                                    const std::shared_ptr<Box_iref>& iref_box,
646
7.54k
                                    std::unordered_set<heif_item_id>& parent_items) const {
647
7.54k
  if (parent_items.find(ID) != parent_items.end()) {
648
14
    return Error(heif_error_Invalid_input,
649
14
                 heif_suberror_Item_reference_cycle,
650
14
                 "Image reference cycle");
651
14
  }
652
7.52k
  parent_items.insert(ID);
653
654
7.52k
  std::vector<heif_item_id> image_references = iref_box->get_references(ID, fourcc("dimg"));
655
7.52k
  for (heif_item_id reference_idx : image_references) {
656
4.87k
    Error error = check_for_ref_cycle_recursion(reference_idx, iref_box, parent_items);
657
4.87k
    if (error) {
658
24
      return error;
659
24
    }
660
4.87k
  }
661
662
7.50k
  parent_items.erase(ID);
663
7.50k
  return Error::Ok;
664
7.52k
}
665
666
667
bool HeifFile::item_exists(heif_item_id ID) const
668
5.85k
{
669
5.85k
  auto image_iter = m_infe_boxes.find(ID);
670
5.85k
  return image_iter != m_infe_boxes.end();
671
5.85k
}
672
673
674
bool HeifFile::has_item_with_id(heif_item_id ID) const
675
48
{
676
48
  auto infe_box = get_infe_box(ID);
677
48
  return infe_box != nullptr;
678
48
}
679
680
681
uint32_t HeifFile::get_item_type_4cc(heif_item_id ID) const
682
84.4k
{
683
84.4k
  auto infe_box = get_infe_box(ID);
684
84.4k
  if (!infe_box) {
685
81
    return 0;
686
81
  }
687
688
84.3k
  return infe_box->get_item_type_4cc();
689
84.4k
}
690
691
692
std::string HeifFile::get_content_type(heif_item_id ID) const
693
23.2k
{
694
23.2k
  auto infe_box = get_infe_box(ID);
695
23.2k
  if (!infe_box) {
696
0
    return "";
697
0
  }
698
699
23.2k
  return infe_box->get_content_type();
700
23.2k
}
701
702
std::string HeifFile::get_item_uri_type(heif_item_id ID) const
703
4.30k
{
704
4.30k
  auto infe_box = get_infe_box(ID);
705
4.30k
  if (!infe_box) {
706
0
    return "";
707
0
  }
708
709
4.30k
  return infe_box->get_item_uri_type();
710
4.30k
}
711
712
713
Error HeifFile::get_properties(heif_item_id imageID,
714
                               std::vector<std::shared_ptr<Box>>& properties) const
715
77.7k
{
716
77.7k
  if (!m_ipco_box) {
717
0
    return Error(heif_error_Invalid_input,
718
0
                 heif_suberror_No_ipco_box);
719
0
  }
720
77.7k
  else if (!m_ipma_box) {
721
0
    return Error(heif_error_Invalid_input,
722
0
                 heif_suberror_No_ipma_box);
723
0
  }
724
725
77.7k
  return m_ipco_box->get_properties_for_item_ID(imageID, m_ipma_box, properties);
726
77.7k
}
727
728
729
Result<std::vector<uint8_t>> HeifFile::get_uncompressed_item_data(heif_item_id ID) const
730
5.85k
{
731
5.85k
  assert(m_limits);
732
733
5.85k
#if ENABLE_PARALLEL_TILE_DECODING
734
  // std::lock_guard<std::mutex> guard(m_read_mutex);   // TODO: I think that this is not needed anymore because this function is not used for image data anymore.
735
5.85k
#endif
736
737
5.85k
  if (!m_iloc_box) {
738
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
739
0
  }
740
741
5.85k
  if (!item_exists(ID)) {
742
0
    return Error(heif_error_Usage_error,
743
0
                 heif_suberror_Nonexisting_item_referenced);
744
0
  }
745
746
5.85k
  auto infe_box = get_infe_box(ID);
747
5.85k
  if (!infe_box) {
748
0
    return Error(heif_error_Usage_error,
749
0
                 heif_suberror_Nonexisting_item_referenced);
750
0
  }
751
752
753
5.85k
  uint32_t item_type = infe_box->get_item_type_4cc();
754
5.85k
  std::string content_type = infe_box->get_content_type();
755
756
  // --- decompress data
757
758
5.85k
  Error error;
759
760
5.85k
  if (item_type == fourcc("mime")) {
761
219
    std::string encoding = infe_box->get_content_encoding();
762
    // Skip the encoding comparisons when no encoding is set; falling through
763
    // to the raw read below is the right behaviour for the uncompressed case.
764
    // Skipping is also necessary to avoid libstdc++'s _S_compare(0, N) which
765
    // computes unsigned `0 - N` and trips UBSan even though the cast result
766
    // is benign.
767
219
    if (!encoding.empty()) {
768
4
      if (encoding == "compress_zlib") {
769
#if HAVE_ZLIB
770
        std::vector<uint8_t> compressed_data;
771
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
772
        if (error) {
773
          return error;
774
        }
775
776
        return decompress_zlib(compressed_data);
777
#else
778
0
        return Error(heif_error_Unsupported_feature,
779
0
                     heif_suberror_Unsupported_header_compression_method,
780
0
                     encoding);
781
0
#endif
782
0
      }
783
4
      else if (encoding == "deflate") {
784
#if HAVE_ZLIB
785
        std::vector<uint8_t> compressed_data;
786
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
787
        if (error) {
788
          return error;
789
        }
790
        return decompress_deflate(compressed_data);
791
#else
792
1
        return Error(heif_error_Unsupported_feature,
793
1
                     heif_suberror_Unsupported_header_compression_method,
794
1
                     encoding);
795
1
#endif
796
1
      }
797
3
      else if (encoding == "br") {
798
#if HAVE_BROTLI
799
        std::vector<uint8_t> compressed_data;
800
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
801
        if (error) {
802
          return error;
803
        }
804
        return decompress_brotli(compressed_data);
805
#else
806
0
        return Error(heif_error_Unsupported_feature,
807
0
                     heif_suberror_Unsupported_header_compression_method,
808
0
                     encoding);
809
0
#endif
810
0
      }
811
3
      else {
812
3
        return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec);
813
3
      }
814
4
    }
815
219
  }
816
817
818
  // --- read uncompressed
819
820
5.84k
  std::vector<uint8_t> data;
821
5.84k
  error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &data, m_limits);
822
5.84k
  if (error) {
823
2.70k
    return error;
824
2.70k
  }
825
3.14k
  else {
826
3.14k
    return data;
827
3.14k
  }
828
5.84k
}
829
830
831
Error HeifFile::append_data_from_file_range(std::vector<uint8_t>& out_data, uint64_t offset, uint32_t size) const
832
0
{
833
0
  auto old_size = out_data.size();
834
835
  // --- check that the requested range does not exceed the file size
836
837
0
  uint64_t end_pos = offset + size;
838
0
  if (m_input_stream->wait_for_file_size(end_pos) != StreamReader::grow_status::size_reached) {
839
0
    std::stringstream sstr;
840
0
    sstr << "File range " << offset << ".." << end_pos << " is beyond end of file.";
841
0
    return {heif_error_Invalid_input,
842
0
            heif_suberror_End_of_data,
843
0
            sstr.str()};
844
0
  }
845
846
  // --- check security limit on resulting buffer size
847
848
0
  if (m_limits) {
849
0
    auto max_memory_block_size = m_limits->max_memory_block_size;
850
0
    if (max_memory_block_size && max_memory_block_size - old_size < size) {
851
0
      std::stringstream sstr;
852
0
      sstr << "Sample data of " << size << " bytes would grow total buffer to "
853
0
           << (old_size + size) << " bytes, exceeding the security limit of "
854
0
           << max_memory_block_size << " bytes.";
855
0
      return {heif_error_Memory_allocation_error,
856
0
              heif_suberror_Security_limit_exceeded,
857
0
              sstr.str()};
858
0
    }
859
0
  }
860
861
0
  if (!m_input_stream->seek(offset)) {
862
0
    return {heif_error_Invalid_input,
863
0
            heif_suberror_End_of_data,
864
0
            "Cannot seek to sample data offset."};
865
0
  }
866
867
0
  out_data.resize(old_size + size);
868
869
0
  if (!m_input_stream->read(out_data.data() + old_size, size)) {
870
0
    out_data.resize(old_size);
871
0
    return {heif_error_Invalid_input,
872
0
            heif_suberror_End_of_data,
873
0
            "Failed to read sample data from file."};
874
0
  }
875
876
0
  return {};
877
0
}
878
879
880
Error HeifFile::append_data_from_iloc(heif_item_id ID, std::vector<uint8_t>& out_data, uint64_t offset, uint64_t size) const
881
12.7k
{
882
12.7k
  if (!m_iloc_box) {
883
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
884
0
  }
885
886
12.7k
  const auto& items = m_iloc_box->get_items();
887
12.7k
  const Box_iloc::Item* item = nullptr;
888
19.4k
  for (const auto& i : items) {
889
19.4k
    if (i.item_ID == ID) {
890
12.4k
      item = &i;
891
12.4k
      break;
892
12.4k
    }
893
19.4k
  }
894
12.7k
  if (!item) {
895
294
    std::stringstream sstr;
896
294
    sstr << "Item with ID " << ID << " has no compressed data";
897
898
294
    return {heif_error_Invalid_input,
899
294
            heif_suberror_No_item_data,
900
294
            sstr.str()};
901
294
  }
902
903
12.4k
  return m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, offset, size, m_limits);
904
12.7k
}
905
906
907
Result<std::vector<uint8_t>> HeifFile::get_item_data(heif_item_id ID, heif_metadata_compression* out_compression) const
908
0
{
909
0
  Error error;
910
911
0
  assert(m_limits);
912
913
0
  if (!m_iloc_box) {
914
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
915
0
  }
916
917
0
  auto infe_box = get_infe_box(ID);
918
0
  if (!infe_box) {
919
0
    return Error{heif_error_Usage_error,
920
0
                 heif_suberror_Nonexisting_item_referenced};
921
0
  }
922
923
0
  uint32_t item_type = infe_box->get_item_type_4cc();
924
0
  std::string content_type = infe_box->get_content_type();
925
926
  // --- non 'mime' data (uncompressed)
927
928
0
  if (item_type != fourcc("mime")) {
929
0
    if (out_compression) {
930
0
      *out_compression = heif_metadata_compression_off;
931
0
    }
932
933
0
    std::vector<uint8_t> out_data;
934
0
    Error err = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, m_limits);
935
0
    if (err) {
936
0
      return err;
937
0
    }
938
0
    else {
939
0
      return out_data;
940
0
    }
941
0
  }
942
943
944
  // --- mime data
945
946
0
  std::string encoding = infe_box->get_content_encoding();
947
948
0
  heif_metadata_compression compression;
949
950
0
  if (encoding.empty()) {
951
    // shortcut for case of uncompressed mime data
952
953
0
    if (out_compression) {
954
0
      *out_compression = heif_metadata_compression_off;
955
0
    }
956
957
0
    std::vector<uint8_t> out_data;
958
0
    Error err = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, m_limits);
959
0
    if (err) {
960
0
      return err;
961
0
    }
962
0
    else {
963
0
      return out_data;
964
0
    }
965
0
  }
966
0
  else if (encoding == "compress_zlib") {
967
0
    compression = heif_metadata_compression_zlib;
968
0
  }
969
0
  else if (encoding == "deflate") {
970
0
    compression = heif_metadata_compression_deflate;
971
0
  }
972
0
  else if (encoding == "br") {
973
0
    compression = heif_metadata_compression_brotli;
974
0
  }
975
0
  else {
976
0
    compression = heif_metadata_compression_unknown;
977
0
  }
978
979
  // read compressed data
980
981
0
  std::vector<uint8_t> compressed_data;
982
0
  error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
983
0
  if (error) {
984
0
    return error;
985
0
  }
986
987
  // return compressed data, if we do not want to have it uncompressed
988
989
0
  const bool return_compressed = (out_compression != nullptr);
990
0
  if (return_compressed) {
991
0
    *out_compression = compression;
992
0
    return compressed_data;
993
0
  }
994
995
  // decompress the data
996
997
0
  switch (compression) {
998
#if HAVE_ZLIB
999
    case heif_metadata_compression_zlib:
1000
      return decompress_zlib(compressed_data);
1001
    case heif_metadata_compression_deflate:
1002
      return decompress_deflate(compressed_data);
1003
#endif
1004
#if HAVE_BROTLI
1005
    case heif_metadata_compression_brotli:
1006
      return decompress_brotli(compressed_data);
1007
#endif
1008
0
    default:
1009
0
      return Error{heif_error_Unsupported_filetype, heif_suberror_Unsupported_header_compression_method};
1010
0
  }
1011
0
}
1012
1013
1014
Result<heif_item_id> HeifFile::get_unused_item_id()
1015
0
{
1016
0
  return m_id_creator.get_new_id(IDCreator::Namespace::item);
1017
0
}
1018
1019
1020
Result<heif_item_id> HeifFile::add_new_image(uint32_t item_type)
1021
0
{
1022
0
  auto result = add_new_infe_box(item_type);
1023
0
  if (!result) {
1024
0
    return result.error();
1025
0
  }
1026
0
  return (*result)->get_item_ID();
1027
0
}
1028
1029
1030
Result<std::shared_ptr<Box_infe>> HeifFile::add_new_infe_box(uint32_t item_type)
1031
0
{
1032
0
  init_for_image();
1033
1034
0
  auto idResult = get_unused_item_id();
1035
0
  if (!idResult) {
1036
0
    return idResult.error();
1037
0
  }
1038
0
  heif_item_id id = *idResult;
1039
1040
0
  auto infe = std::make_shared<Box_infe>();
1041
0
  infe->set_item_ID(id);
1042
0
  infe->set_hidden_item(false);
1043
0
  infe->set_item_type_4cc(item_type);
1044
1045
0
  m_infe_boxes[id] = infe;
1046
0
  m_iinf_box->append_child_box(infe);
1047
1048
0
  return infe;
1049
0
}
1050
1051
1052
Result<std::shared_ptr<Box_infe>> HeifFile::add_new_meta_infe_box(uint32_t item_type)
1053
0
{
1054
0
  init_for_meta_item();
1055
1056
0
  auto idResult = get_unused_item_id();
1057
0
  if (!idResult) {
1058
0
    return idResult.error();
1059
0
  }
1060
0
  heif_item_id id = *idResult;
1061
1062
0
  auto infe = std::make_shared<Box_infe>();
1063
0
  infe->set_item_ID(id);
1064
0
  infe->set_hidden_item(false);
1065
0
  infe->set_item_type_4cc(item_type);
1066
1067
0
  m_infe_boxes[id] = infe;
1068
0
  m_iinf_box->append_child_box(infe);
1069
1070
0
  return infe;
1071
0
}
1072
1073
1074
void HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential)
1075
0
{
1076
0
  auto ispe = std::make_shared<Box_ispe>();
1077
0
  ispe->set_size(width, height);
1078
1079
0
  uint32_t index = m_ipco_box->find_or_append_child_box(ispe);
1080
1081
0
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1082
0
}
1083
1084
1085
1086
heif_property_id HeifFile::add_property(heif_item_id id, const std::shared_ptr<Box>& property, bool essential)
1087
3.22k
{
1088
3.22k
  uint32_t index = m_ipco_box->find_or_append_child_box(property);
1089
1090
3.22k
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1091
1092
3.22k
  return index + 1;
1093
3.22k
}
1094
1095
1096
heif_property_id HeifFile::add_property_without_deduplication(heif_item_id id, const std::shared_ptr<Box>& property, bool essential)
1097
0
{
1098
0
  uint32_t index = m_ipco_box->append_child_box(property);
1099
1100
0
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1101
1102
0
  return index + 1;
1103
0
}
1104
1105
1106
void HeifFile::add_orientation_properties(heif_item_id id, heif_orientation orientation)
1107
36
{
1108
  // Note: ISO/IEC 23000-22:2019(E) (MIAF) 7.3.6.7 requires the following order:
1109
  // clean aperture first, then rotation, then mirror
1110
1111
36
  int rotation_ccw = 0;
1112
36
  heif_transform_mirror_direction mirror;
1113
36
  bool has_mirror = false;
1114
1115
36
  switch (orientation) {
1116
1
    case heif_orientation_normal:
1117
1
      break;
1118
0
    case heif_orientation_flip_horizontally:
1119
0
      mirror = heif_transform_mirror_direction_horizontal;
1120
0
      has_mirror = true;
1121
0
      break;
1122
4
    case heif_orientation_rotate_180:
1123
4
      rotation_ccw = 180;
1124
4
      break;
1125
0
    case heif_orientation_flip_vertically:
1126
0
      mirror = heif_transform_mirror_direction_vertical;
1127
0
      has_mirror = true;
1128
0
      break;
1129
2
    case heif_orientation_rotate_90_cw_then_flip_horizontally:
1130
2
      rotation_ccw = 270;
1131
2
      mirror = heif_transform_mirror_direction_horizontal;
1132
2
      has_mirror = true;
1133
2
      break;
1134
29
    case heif_orientation_rotate_90_cw:
1135
29
      rotation_ccw = 270;
1136
29
      break;
1137
0
    case heif_orientation_rotate_90_cw_then_flip_vertically:
1138
0
      rotation_ccw = 270;
1139
0
      mirror = heif_transform_mirror_direction_vertical;
1140
0
      has_mirror = true;
1141
0
      break;
1142
0
    case heif_orientation_rotate_270_cw:
1143
0
      rotation_ccw = 90;
1144
0
      break;
1145
36
  }
1146
1147
  // omit rotation when angle is 0
1148
36
  if (rotation_ccw!=0) {
1149
35
    auto irot = std::make_shared<Box_irot>();
1150
35
    irot->set_rotation_ccw(rotation_ccw);
1151
1152
35
    uint32_t index = m_ipco_box->find_or_append_child_box(irot);
1153
1154
35
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1155
35
  }
1156
1157
36
  if (has_mirror) {
1158
2
    auto imir = std::make_shared<Box_imir>();
1159
2
    imir->set_mirror_direction(mirror);
1160
1161
2
    uint32_t index = m_ipco_box->find_or_append_child_box(imir);
1162
1163
2
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1164
2
  }
1165
36
}
1166
1167
1168
Result<heif_item_id> HeifFile::add_infe(uint32_t item_type, const uint8_t* data, size_t size)
1169
0
{
1170
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1171
1172
0
  auto infe_result = add_new_infe_box(item_type);
1173
0
  if (!infe_result) {
1174
0
    return infe_result.error();
1175
0
  }
1176
0
  auto infe_box = *infe_result;
1177
0
  infe_box->set_hidden_item(true);
1178
1179
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1180
1181
0
  set_item_data(infe_box, data, size, heif_metadata_compression_off);
1182
1183
0
  return metadata_id;
1184
0
}
1185
1186
1187
void HeifFile::add_infe_box(heif_item_id id, std::shared_ptr<Box_infe> infe)
1188
73
{
1189
73
  m_infe_boxes.insert(std::make_pair(id, std::move(infe)));
1190
73
}
1191
1192
1193
Result<heif_item_id> HeifFile::add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size)
1194
0
{
1195
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1196
1197
0
  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
1198
0
  if (!infe_result) {
1199
0
    return infe_result.error();
1200
0
  }
1201
0
  auto infe_box = *infe_result;
1202
0
  infe_box->set_hidden_item(true);
1203
0
  infe_box->set_content_type(content_type);
1204
1205
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1206
1207
0
  set_item_data(infe_box, data, size, content_encoding);
1208
1209
0
  return metadata_id;
1210
0
}
1211
1212
1213
Result<heif_item_id> HeifFile::add_precompressed_infe_mime(const char* content_type, std::string content_encoding, const uint8_t* data, size_t size)
1214
0
{
1215
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1216
1217
0
  auto infe_result = add_new_meta_infe_box(fourcc("mime"));
1218
0
  if (!infe_result) {
1219
0
    return infe_result.error();
1220
0
  }
1221
0
  auto infe_box = *infe_result;
1222
0
  infe_box->set_hidden_item(true);
1223
0
  infe_box->set_content_type(content_type);
1224
1225
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1226
1227
0
  set_precompressed_item_data(infe_box, data, size, std::move(content_encoding));
1228
1229
0
  return metadata_id;
1230
0
}
1231
1232
1233
Result<heif_item_id> HeifFile::add_infe_uri(const char* item_uri_type, const uint8_t* data, size_t size)
1234
0
{
1235
  // create an infe box describing what kind of data we are storing (this also creates a new ID)
1236
1237
0
  auto infe_result = add_new_meta_infe_box(fourcc("uri "));
1238
0
  if (!infe_result) {
1239
0
    return infe_result.error();
1240
0
  }
1241
0
  auto infe_box = *infe_result;
1242
0
  infe_box->set_hidden_item(true);
1243
0
  infe_box->set_item_uri_type(item_uri_type);
1244
1245
0
  heif_item_id metadata_id = infe_box->get_item_ID();
1246
1247
0
  set_item_data(infe_box, data, size, heif_metadata_compression_off);
1248
1249
0
  return metadata_id;
1250
0
}
1251
1252
1253
Error HeifFile::set_item_data(const std::shared_ptr<Box_infe>& item, const uint8_t* data, size_t size, heif_metadata_compression compression)
1254
0
{
1255
  // --- metadata compression
1256
1257
0
  if (compression == heif_metadata_compression_auto) {
1258
0
    compression = heif_metadata_compression_off; // currently, we don't use header compression by default
1259
0
  }
1260
1261
  // only set metadata compression for MIME type data which has 'content_encoding' field
1262
0
  if (compression != heif_metadata_compression_off &&
1263
0
      item->get_item_type_4cc() != fourcc("mime")) {
1264
    // TODO: error, compression not supported
1265
0
  }
1266
1267
1268
0
  std::vector<uint8_t> data_array;
1269
0
  if (compression == heif_metadata_compression_zlib) {
1270
#if HAVE_ZLIB
1271
    data_array = compress_zlib((const uint8_t*) data, size);
1272
    item->set_content_encoding("compress_zlib");
1273
#else
1274
0
    return Error(heif_error_Unsupported_feature,
1275
0
                 heif_suberror_Unsupported_header_compression_method);
1276
0
#endif
1277
0
  }
1278
0
  else if (compression == heif_metadata_compression_deflate) {
1279
#if HAVE_ZLIB
1280
    data_array = compress_deflate((const uint8_t*) data, size);
1281
    item->set_content_encoding("deflate");
1282
#else
1283
0
    return Error(heif_error_Unsupported_feature,
1284
0
                 heif_suberror_Unsupported_header_compression_method);
1285
0
#endif
1286
0
  }
1287
0
  else if (compression == heif_metadata_compression_brotli) {
1288
#if HAVE_BROTLI
1289
    data_array = compress_brotli((const uint8_t*)data, size);
1290
    item->set_content_encoding("br");
1291
#else
1292
0
    return Error(heif_error_Unsupported_feature,
1293
0
                 heif_suberror_Unsupported_header_compression_method);
1294
0
#endif
1295
0
  }
1296
0
  else {
1297
    // uncompressed data, plain copy
1298
1299
0
    if (size > 0) { // Note: this 'if' is not necessary, but a workaround to a compiler bug (https://github.com/strukturag/libheif/issues/1421)
1300
0
      data_array.resize(size);
1301
0
      memcpy(data_array.data(), data, size);
1302
0
    }
1303
0
  }
1304
1305
  // copy the data into the file, store the pointer to it in an iloc box entry
1306
1307
0
  append_iloc_data(item->get_item_ID(), data_array, 0);
1308
1309
0
  return Error::Ok;
1310
0
}
1311
1312
1313
Error HeifFile::set_precompressed_item_data(const std::shared_ptr<Box_infe>& item, const uint8_t* data, size_t size, std::string content_encoding)
1314
0
{
1315
  // only set metadata compression for MIME type data which has 'content_encoding' field
1316
0
  if (!content_encoding.empty() &&
1317
0
      item->get_item_type_4cc() != fourcc("mime")) {
1318
    // TODO: error, compression not supported
1319
0
  }
1320
1321
1322
0
  std::vector<uint8_t> data_array;
1323
0
  data_array.resize(size);
1324
0
  memcpy(data_array.data(), data, size);
1325
1326
0
  item->set_content_encoding(content_encoding);
1327
1328
  // copy the data into the file, store the pointer to it in an iloc box entry
1329
1330
0
  append_iloc_data(item->get_item_ID(), data_array, 0);
1331
1332
0
  return Error::Ok;
1333
0
}
1334
1335
1336
void HeifFile::append_iloc_data(heif_item_id id, const std::vector<uint8_t>& nal_packets, uint8_t construction_method)
1337
0
{
1338
0
  m_iloc_box->append_data(id, nal_packets, construction_method);
1339
0
}
1340
1341
1342
void HeifFile::replace_iloc_data(heif_item_id id, uint64_t offset, const std::vector<uint8_t>& data, uint8_t construction_method)
1343
0
{
1344
0
  m_iloc_box->replace_data(id, offset, data, construction_method);
1345
0
}
1346
1347
1348
void HeifFile::set_primary_item_id(heif_item_id id)
1349
39
{
1350
39
  if (!m_pitm_box) {
1351
39
    m_pitm_box = std::make_shared<Box_pitm>();
1352
39
    m_meta_box->replace_child_box(m_pitm_box);
1353
39
  }
1354
1355
39
  m_pitm_box->set_item_ID(id);
1356
39
}
1357
1358
1359
void HeifFile::set_ipco_box(std::shared_ptr<Box_ipco> ipco)
1360
36
{
1361
36
  m_ipco_box = ipco;
1362
36
  m_meta_box->replace_child_box(ipco);
1363
36
}
1364
1365
1366
void HeifFile::set_ipma_box(std::shared_ptr<Box_ipma> ipma)
1367
35
{
1368
35
  m_ipma_box = ipma;
1369
35
  m_meta_box->replace_child_box(ipma);
1370
35
}
1371
1372
1373
void HeifFile::set_iloc_box(std::shared_ptr<Box_iloc> iloc)
1374
35
{
1375
35
  m_iloc_box = iloc;
1376
35
  m_meta_box->replace_child_box(iloc);
1377
35
}
1378
1379
1380
void HeifFile::set_iref_box(std::shared_ptr<Box_iref> iref)
1381
35
{
1382
35
  m_iref_box = iref;
1383
35
  m_meta_box->replace_child_box(iref);
1384
35
}
1385
1386
1387
void HeifFile::add_iref_reference(heif_item_id from, uint32_t type,
1388
                                  const std::vector<heif_item_id>& to)
1389
0
{
1390
0
  if (!m_iref_box) {
1391
0
    m_iref_box = std::make_shared<Box_iref>();
1392
0
    m_meta_box->append_child_box(m_iref_box);
1393
0
  }
1394
1395
0
  m_iref_box->add_references(from, type, to);
1396
0
}
1397
1398
1399
void HeifFile::set_iref_reference(heif_item_id from, uint32_t type, int reference_idx, heif_item_id to_item)
1400
0
{
1401
0
  assert(m_iref_box);
1402
0
  m_iref_box->overwrite_reference(from, type, reference_idx, to_item);
1403
0
}
1404
1405
1406
void HeifFile::add_entity_group_box(const std::shared_ptr<Box>& entity_group_box)
1407
0
{
1408
0
  if (!m_grpl_box) {
1409
0
    m_grpl_box = std::make_shared<Box_grpl>();
1410
0
    m_meta_box->append_child_box(m_grpl_box);
1411
0
  }
1412
1413
0
  m_grpl_box->append_child_box(entity_group_box);
1414
0
}
1415
1416
1417
std::shared_ptr<Box_EntityToGroup> HeifFile::get_entity_group(heif_entity_group_id id)
1418
0
{
1419
0
  if (!m_grpl_box) {
1420
0
    return nullptr;
1421
0
  }
1422
1423
0
  const auto& entityGroups = m_grpl_box->get_all_child_boxes();
1424
0
  for (auto& groupBase : entityGroups) {
1425
0
    auto group = std::dynamic_pointer_cast<Box_EntityToGroup>(groupBase);
1426
0
    assert(group);
1427
1428
0
    if (group->get_group_id() == id) {
1429
0
      return group;
1430
0
    }
1431
0
  }
1432
1433
0
  return nullptr;
1434
0
}
1435
1436
1437
void HeifFile::set_auxC_property(heif_item_id id, const std::string& type)
1438
0
{
1439
0
  auto auxC = std::make_shared<Box_auxC>();
1440
0
  auxC->set_aux_type(type);
1441
1442
0
  uint32_t index = m_ipco_box->find_or_append_child_box(auxC);
1443
1444
0
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1445
0
}
1446
1447
#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
1448
std::wstring HeifFile::convert_utf8_path_to_utf16(std::string str)
1449
{
1450
  std::wstring ret;
1451
  int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), NULL, 0);
1452
  if (len > 0)
1453
  {
1454
    ret.resize(len);
1455
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.length(), &ret[0], len);
1456
  }
1457
  return ret;
1458
}
1459
#endif
1460
1461
1462
Result<size_t> HeifFile::write_mdat(StreamWriter& writer)
1463
0
{
1464
  // --- write mdat box
1465
1466
0
  size_t mdatSize = m_mdat_data->get_data_size();
1467
1468
0
  if (mdatSize <= 0xFFFFFFFF - 8) {
1469
0
    writer.write32((uint32_t) (mdatSize + 8));
1470
0
    writer.write32(fourcc("mdat"));
1471
0
  }
1472
0
  else {
1473
    // box size > 4 GB
1474
1475
0
    writer.write32(1);
1476
0
    writer.write32(fourcc("mdat"));
1477
0
    writer.write64(mdatSize+8+8);
1478
0
  }
1479
1480
0
  size_t dataStartPos = writer.get_position();
1481
1482
0
  m_mdat_data->write(writer);
1483
1484
0
  return dataStartPos;
1485
0
}