Coverage Report

Created: 2026-05-24 07:45

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
96.9k
#define STRICT_PARSING false
63
64
65
HeifFile::HeifFile()
66
194k
{
67
194k
  m_file_layout = std::make_shared<FileLayout>();
68
194k
}
69
70
194k
HeifFile::~HeifFile() = default;
71
72
std::vector<heif_item_id> HeifFile::get_item_IDs() const
73
47.5k
{
74
47.5k
  std::vector<heif_item_id> IDs;
75
76
109k
  for (const auto& infe : m_infe_boxes) {
77
109k
    IDs.push_back(infe.second->get_item_ID());
78
109k
  }
79
80
47.5k
  return IDs;
81
47.5k
}
82
83
84
std::shared_ptr<const Box_infe> HeifFile::get_infe_box(heif_item_id ID) const
85
523k
{
86
523k
  auto iter = m_infe_boxes.find(ID);
87
523k
  if (iter == m_infe_boxes.end()) {
88
994
    return nullptr;
89
994
  }
90
91
522k
  return iter->second;
92
523k
}
93
94
95
std::shared_ptr<Box_infe> HeifFile::get_infe_box(heif_item_id ID)
96
242k
{
97
242k
  auto iter = m_infe_boxes.find(ID);
98
242k
  if (iter == m_infe_boxes.end()) {
99
0
    return nullptr;
100
0
  }
101
102
242k
  return iter->second;
103
242k
}
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
97.0k
{
126
97.0k
  auto input_stream = std::make_shared<StreamReader_memory>((const uint8_t*) data, size, copy);
127
128
97.0k
  return read(input_stream);
129
97.0k
}
130
131
132
Error HeifFile::read(const std::shared_ptr<StreamReader>& reader)
133
97.0k
{
134
97.0k
  assert(m_limits);
135
136
97.0k
  m_input_stream = reader;
137
138
97.0k
  Error err;
139
97.0k
  err = m_file_layout->read(reader, m_limits);
140
97.0k
  if (err) {
141
43.2k
    return err;
142
43.2k
  }
143
144
53.7k
  Error error = parse_heif_file();
145
53.7k
  return error;
146
97.0k
}
147
148
149
bool HeifFile::has_images() const
150
96.3k
{
151
96.3k
  if (!m_meta_box) {
152
476
    return false;
153
476
  }
154
155
95.8k
  return m_hdlr_box && m_hdlr_box->get_handler_type() == fourcc("pict");
156
96.3k
}
157
158
159
void HeifFile::new_empty_file()
160
97.2k
{
161
  //m_input_stream.reset();
162
97.2k
  m_top_level_boxes.clear();
163
164
97.2k
  m_ftyp_box = std::make_shared<Box_ftyp>();
165
97.2k
  m_top_level_boxes.push_back(m_ftyp_box);
166
97.2k
}
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
53.7k
{
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
53.7k
  m_ftyp_box = m_file_layout->get_ftyp_box();
454
53.7k
  if (!m_ftyp_box) {
455
2.37k
    return Error(heif_error_Invalid_input,
456
2.37k
                 heif_suberror_No_ftyp_box);
457
2.37k
  }
458
459
51.3k
  m_top_level_boxes.push_back(m_ftyp_box);
460
461
51.3k
  bool is_sequence_brand = (m_ftyp_box->has_compatible_brand(heif_brand2_msf1) ||
462
51.2k
                            m_ftyp_box->has_compatible_brand(heif_brand2_isom) ||
463
50.8k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp41) ||
464
50.7k
                            m_ftyp_box->has_compatible_brand(heif_brand2_mp42));
465
466
  // --- check whether this is a HEIF file and its structural format
467
468
51.3k
  if (!m_ftyp_box->has_compatible_brand(heif_brand2_heic) &&
469
39.3k
      !m_ftyp_box->has_compatible_brand(heif_brand2_heix) &&
470
38.7k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mif1) &&
471
17.0k
      !m_ftyp_box->has_compatible_brand(heif_brand2_avif) &&
472
2.72k
      !m_ftyp_box->has_compatible_brand(heif_brand2_1pic) &&
473
2.71k
      !(m_ftyp_box->get_major_brand() == heif_brand2_mif3) &&
474
2.70k
      !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg) &&
475
2.70k
      !m_ftyp_box->has_compatible_brand(heif_brand2_isom) &&
476
2.26k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp42) &&
477
2.26k
      !m_ftyp_box->has_compatible_brand(heif_brand2_mp41) &&
478
2.18k
      !m_ftyp_box->has_compatible_brand(heif_brand2_msf1)) {
479
2.13k
    std::stringstream sstr;
480
2.13k
    sstr << "File does not include any supported brands.\n";
481
482
2.13k
    return Error(heif_error_Unsupported_filetype,
483
2.13k
                 heif_suberror_Unspecified,
484
2.13k
                 sstr.str());
485
2.13k
      }
486
487
49.2k
  m_mini_box = m_file_layout->get_mini_box();
488
49.2k
  m_top_level_boxes.push_back(m_mini_box);
489
490
49.2k
  if (m_mini_box) {
491
164
    Error err = m_mini_box->create_expanded_boxes(this);
492
164
    if (err) {
493
31
      return err;
494
31
    }
495
133
    return Error::Ok;
496
164
  }
497
498
49.0k
  m_meta_box = m_file_layout->get_meta_box();
499
49.0k
  if (m_meta_box) {
500
48.4k
    m_top_level_boxes.push_back(m_meta_box);
501
48.4k
  }
502
503
  // TODO: we are missing 'mdat' top level boxes
504
505
49.0k
  m_moov_box = m_file_layout->get_moov_box();
506
49.0k
  if (m_moov_box) {
507
602
    m_top_level_boxes.push_back(m_moov_box);
508
602
  }
509
510
  // if we didn't find the mini box, meta is required
511
512
49.0k
  if (!m_meta_box && !is_sequence_brand) {
513
13
    return Error(heif_error_Invalid_input,
514
13
                 heif_suberror_No_meta_box);
515
13
  }
516
517
49.0k
  if (!m_moov_box && is_sequence_brand) {
518
15
    return Error(heif_error_Invalid_input,
519
15
                 heif_suberror_No_moov_box);
520
15
  }
521
522
49.0k
  if (m_meta_box) {
523
48.4k
    auto err = parse_heif_images();
524
48.4k
    if (err) {
525
761
      return err;
526
761
    }
527
48.4k
  }
528
529
48.2k
  if (m_moov_box) {
530
584
    auto err = parse_heif_sequences();
531
584
    if (err) {
532
103
      return err;
533
103
    }
534
584
  }
535
536
48.1k
  return Error::Ok;
537
48.2k
}
538
539
540
Error HeifFile::parse_heif_images()
541
48.4k
{
542
48.4k
  m_hdlr_box = m_meta_box->get_child_box<Box_hdlr>();
543
48.4k
  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
48.4k
  m_iinf_box = m_meta_box->get_child_box<Box_iinf>();
551
48.4k
  if (!m_iinf_box) {
552
422
    return Error(heif_error_Invalid_input,
553
422
                 heif_suberror_No_iinf_box);
554
422
  }
555
556
48.0k
  std::vector<std::shared_ptr<Box_infe>> infe_boxes = m_iinf_box->get_child_boxes<Box_infe>();
557
558
111k
  for (auto& infe_box : infe_boxes) {
559
111k
    if (!infe_box) {
560
0
      return Error(heif_error_Invalid_input,
561
0
                   heif_suberror_No_infe_box);
562
0
    }
563
564
111k
    m_infe_boxes.insert(std::make_pair(infe_box->get_item_ID(), infe_box));
565
111k
  }
566
567
568
48.0k
  if (has_images()) {
569
    // --- find mandatory boxes needed for image decoding
570
571
47.7k
    m_pitm_box = m_meta_box->get_child_box<Box_pitm>();
572
47.7k
    if (!m_pitm_box) {
573
39
      return Error(heif_error_Invalid_input,
574
39
                   heif_suberror_No_pitm_box);
575
39
    }
576
577
47.6k
    m_iprp_box = m_meta_box->get_child_box<Box_iprp>();
578
47.6k
    if (!m_iprp_box) {
579
86
      return Error(heif_error_Invalid_input,
580
86
                   heif_suberror_No_iprp_box);
581
86
    }
582
583
47.5k
    m_ipco_box = m_iprp_box->get_child_box<Box_ipco>();
584
47.5k
    if (!m_ipco_box) {
585
47
      return Error(heif_error_Invalid_input,
586
47
                   heif_suberror_No_ipco_box);
587
47
    }
588
589
47.5k
    auto ipma_boxes = m_iprp_box->get_child_boxes<Box_ipma>();
590
47.5k
    if (ipma_boxes.empty()) {
591
36
      return Error(heif_error_Invalid_input,
592
36
                   heif_suberror_No_ipma_box);
593
36
    }
594
47.7k
    for (size_t i=1;i<ipma_boxes.size();i++) {
595
261
      ipma_boxes[0]->insert_entries_from_other_ipma_box(*ipma_boxes[i]);
596
261
    }
597
47.5k
    m_ipma_box = ipma_boxes[0];
598
47.5k
  }
599
600
47.8k
  m_iloc_box = m_meta_box->get_child_box<Box_iloc>();
601
47.8k
  if (!m_iloc_box) {
602
90
    return Error(heif_error_Invalid_input,
603
90
                 heif_suberror_No_iloc_box);
604
90
  }
605
606
47.7k
  m_idat_box = m_meta_box->get_child_box<Box_idat>();
607
608
47.7k
  m_iref_box = m_meta_box->get_child_box<Box_iref>();
609
47.7k
  if (m_iref_box && m_pitm_box) {
610
13.8k
    Error error = check_for_ref_cycle(get_primary_image_ID(), m_iref_box);
611
13.8k
    if (error) {
612
41
      return error;
613
41
    }
614
13.8k
  }
615
616
47.6k
  m_grpl_box = m_meta_box->get_child_box<Box_grpl>();
617
618
619
47.6k
  return Error::Ok;
620
47.7k
}
621
622
623
Error HeifFile::parse_heif_sequences()
624
584
{
625
584
  m_mvhd_box = m_moov_box->get_child_box<Box_mvhd>();
626
584
  if (!m_mvhd_box) {
627
103
    return {heif_error_Invalid_input,
628
103
            heif_suberror_Unspecified,
629
103
            "No mvhd box in image sequence."};
630
103
  }
631
632
481
  return Error::Ok;
633
584
}
634
635
636
Error HeifFile::check_for_ref_cycle(heif_item_id ID,
637
                                    const std::shared_ptr<Box_iref>& iref_box) const
638
13.8k
{
639
13.8k
  std::unordered_set<heif_item_id> parent_items;
640
13.8k
  return check_for_ref_cycle_recursion(ID, iref_box, parent_items);
641
13.8k
}
642
643
644
Error HeifFile::check_for_ref_cycle_recursion(heif_item_id ID,
645
                                    const std::shared_ptr<Box_iref>& iref_box,
646
30.9k
                                    std::unordered_set<heif_item_id>& parent_items) const {
647
30.9k
  if (parent_items.find(ID) != parent_items.end()) {
648
41
    return Error(heif_error_Invalid_input,
649
41
                 heif_suberror_Item_reference_cycle,
650
41
                 "Image reference cycle");
651
41
  }
652
30.9k
  parent_items.insert(ID);
653
654
30.9k
  std::vector<heif_item_id> image_references = iref_box->get_references(ID, fourcc("dimg"));
655
30.9k
  for (heif_item_id reference_idx : image_references) {
656
17.0k
    Error error = check_for_ref_cycle_recursion(reference_idx, iref_box, parent_items);
657
17.0k
    if (error) {
658
74
      return error;
659
74
    }
660
17.0k
  }
661
662
30.8k
  parent_items.erase(ID);
663
30.8k
  return Error::Ok;
664
30.9k
}
665
666
667
bool HeifFile::item_exists(heif_item_id ID) const
668
29.9k
{
669
29.9k
  auto image_iter = m_infe_boxes.find(ID);
670
29.9k
  return image_iter != m_infe_boxes.end();
671
29.9k
}
672
673
674
bool HeifFile::has_item_with_id(heif_item_id ID) const
675
474
{
676
474
  auto infe_box = get_infe_box(ID);
677
474
  return infe_box != nullptr;
678
474
}
679
680
681
uint32_t HeifFile::get_item_type_4cc(heif_item_id ID) const
682
367k
{
683
367k
  auto infe_box = get_infe_box(ID);
684
367k
  if (!infe_box) {
685
802
    return 0;
686
802
  }
687
688
366k
  return infe_box->get_item_type_4cc();
689
367k
}
690
691
692
std::string HeifFile::get_content_type(heif_item_id ID) const
693
103k
{
694
103k
  auto infe_box = get_infe_box(ID);
695
103k
  if (!infe_box) {
696
0
    return "";
697
0
  }
698
699
103k
  return infe_box->get_content_type();
700
103k
}
701
702
std::string HeifFile::get_item_uri_type(heif_item_id ID) const
703
22.4k
{
704
22.4k
  auto infe_box = get_infe_box(ID);
705
22.4k
  if (!infe_box) {
706
0
    return "";
707
0
  }
708
709
22.4k
  return infe_box->get_item_uri_type();
710
22.4k
}
711
712
713
Error HeifFile::get_properties(heif_item_id imageID,
714
                               std::vector<std::shared_ptr<Box>>& properties) const
715
153k
{
716
153k
  if (!m_ipco_box) {
717
0
    return Error(heif_error_Invalid_input,
718
0
                 heif_suberror_No_ipco_box);
719
0
  }
720
153k
  else if (!m_ipma_box) {
721
0
    return Error(heif_error_Invalid_input,
722
0
                 heif_suberror_No_ipma_box);
723
0
  }
724
725
153k
  return m_ipco_box->get_properties_for_item_ID(imageID, m_ipma_box, properties);
726
153k
}
727
728
729
Result<std::vector<uint8_t>> HeifFile::get_uncompressed_item_data(heif_item_id ID) const
730
29.9k
{
731
29.9k
  assert(m_limits);
732
733
29.9k
#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
29.9k
#endif
736
737
29.9k
  if (!m_iloc_box) {
738
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
739
0
  }
740
741
29.9k
  if (!item_exists(ID)) {
742
0
    return Error(heif_error_Usage_error,
743
0
                 heif_suberror_Nonexisting_item_referenced);
744
0
  }
745
746
29.9k
  auto infe_box = get_infe_box(ID);
747
29.9k
  if (!infe_box) {
748
0
    return Error(heif_error_Usage_error,
749
0
                 heif_suberror_Nonexisting_item_referenced);
750
0
  }
751
752
753
29.9k
  uint32_t item_type = infe_box->get_item_type_4cc();
754
29.9k
  std::string content_type = infe_box->get_content_type();
755
756
  // --- decompress data
757
758
29.9k
  Error error;
759
760
29.9k
  if (item_type == fourcc("mime")) {
761
1.30k
    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
1.30k
    if (!encoding.empty()) {
768
681
      if (encoding == "compress_zlib") {
769
7
#if HAVE_ZLIB
770
7
        std::vector<uint8_t> compressed_data;
771
7
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
772
7
        if (error) {
773
3
          return error;
774
3
        }
775
776
4
        return decompress_zlib(compressed_data);
777
#else
778
        return Error(heif_error_Unsupported_feature,
779
                     heif_suberror_Unsupported_header_compression_method,
780
                     encoding);
781
#endif
782
7
      }
783
674
      else if (encoding == "deflate") {
784
149
#if HAVE_ZLIB
785
149
        std::vector<uint8_t> compressed_data;
786
149
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
787
149
        if (error) {
788
1
          return error;
789
1
        }
790
148
        return decompress_deflate(compressed_data);
791
#else
792
        return Error(heif_error_Unsupported_feature,
793
                     heif_suberror_Unsupported_header_compression_method,
794
                     encoding);
795
#endif
796
149
      }
797
525
      else if (encoding == "br") {
798
507
#if HAVE_BROTLI
799
507
        std::vector<uint8_t> compressed_data;
800
507
        error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data, m_limits);
801
507
        if (error) {
802
1
          return error;
803
1
        }
804
506
        return decompress_brotli(compressed_data);
805
#else
806
        return Error(heif_error_Unsupported_feature,
807
                     heif_suberror_Unsupported_header_compression_method,
808
                     encoding);
809
#endif
810
507
      }
811
18
      else {
812
18
        return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec);
813
18
      }
814
681
    }
815
1.30k
  }
816
817
818
  // --- read uncompressed
819
820
29.2k
  std::vector<uint8_t> data;
821
29.2k
  error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &data, m_limits);
822
29.2k
  if (error) {
823
13.7k
    return error;
824
13.7k
  }
825
15.5k
  else {
826
15.5k
    return data;
827
15.5k
  }
828
29.2k
}
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
51.9k
{
882
51.9k
  if (!m_iloc_box) {
883
0
    return Error(heif_error_Invalid_input, heif_suberror_No_iloc_box);
884
0
  }
885
886
51.9k
  const auto& items = m_iloc_box->get_items();
887
51.9k
  const Box_iloc::Item* item = nullptr;
888
73.1k
  for (const auto& i : items) {
889
73.1k
    if (i.item_ID == ID) {
890
51.3k
      item = &i;
891
51.3k
      break;
892
51.3k
    }
893
73.1k
  }
894
51.9k
  if (!item) {
895
492
    std::stringstream sstr;
896
492
    sstr << "Item with ID " << ID << " has no compressed data";
897
898
492
    return {heif_error_Invalid_input,
899
492
            heif_suberror_No_item_data,
900
492
            sstr.str()};
901
492
  }
902
903
51.4k
  return m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &out_data, offset, size, m_limits);
904
51.9k
}
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
0
#if HAVE_ZLIB
999
0
    case heif_metadata_compression_zlib:
1000
0
      return decompress_zlib(compressed_data);
1001
0
    case heif_metadata_compression_deflate:
1002
0
      return decompress_deflate(compressed_data);
1003
0
#endif
1004
0
#if HAVE_BROTLI
1005
0
    case heif_metadata_compression_brotli:
1006
0
      return decompress_brotli(compressed_data);
1007
0
#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
30.4k
{
1088
30.4k
  uint32_t index = m_ipco_box->find_or_append_child_box(property);
1089
1090
30.4k
  m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)});
1091
1092
30.4k
  return index + 1;
1093
30.4k
}
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
137
{
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
137
  int rotation_ccw = 0;
1112
137
  heif_transform_mirror_direction mirror;
1113
137
  bool has_mirror = false;
1114
1115
137
  switch (orientation) {
1116
7
    case heif_orientation_normal:
1117
7
      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
1
    case heif_orientation_rotate_180:
1123
1
      rotation_ccw = 180;
1124
1
      break;
1125
3
    case heif_orientation_flip_vertically:
1126
3
      mirror = heif_transform_mirror_direction_vertical;
1127
3
      has_mirror = true;
1128
3
      break;
1129
1
    case heif_orientation_rotate_90_cw_then_flip_horizontally:
1130
1
      rotation_ccw = 270;
1131
1
      mirror = heif_transform_mirror_direction_horizontal;
1132
1
      has_mirror = true;
1133
1
      break;
1134
53
    case heif_orientation_rotate_90_cw:
1135
53
      rotation_ccw = 270;
1136
53
      break;
1137
64
    case heif_orientation_rotate_90_cw_then_flip_vertically:
1138
64
      rotation_ccw = 270;
1139
64
      mirror = heif_transform_mirror_direction_vertical;
1140
64
      has_mirror = true;
1141
64
      break;
1142
8
    case heif_orientation_rotate_270_cw:
1143
8
      rotation_ccw = 90;
1144
8
      break;
1145
137
  }
1146
1147
  // omit rotation when angle is 0
1148
137
  if (rotation_ccw!=0) {
1149
127
    auto irot = std::make_shared<Box_irot>();
1150
127
    irot->set_rotation_ccw(rotation_ccw);
1151
1152
127
    uint32_t index = m_ipco_box->find_or_append_child_box(irot);
1153
1154
127
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1155
127
  }
1156
1157
137
  if (has_mirror) {
1158
68
    auto imir = std::make_shared<Box_imir>();
1159
68
    imir->set_mirror_direction(mirror);
1160
1161
68
    uint32_t index = m_ipco_box->find_or_append_child_box(imir);
1162
1163
68
    m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
1164
68
  }
1165
137
}
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
397
{
1189
397
  m_infe_boxes.insert(std::make_pair(id, std::move(infe)));
1190
397
}
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
0
#if HAVE_ZLIB
1271
0
    data_array = compress_zlib((const uint8_t*) data, size);
1272
0
    item->set_content_encoding("compress_zlib");
1273
#else
1274
    return Error(heif_error_Unsupported_feature,
1275
                 heif_suberror_Unsupported_header_compression_method);
1276
#endif
1277
0
  }
1278
0
  else if (compression == heif_metadata_compression_deflate) {
1279
0
#if HAVE_ZLIB
1280
0
    data_array = compress_deflate((const uint8_t*) data, size);
1281
0
    item->set_content_encoding("deflate");
1282
#else
1283
    return Error(heif_error_Unsupported_feature,
1284
                 heif_suberror_Unsupported_header_compression_method);
1285
#endif
1286
0
  }
1287
0
  else if (compression == heif_metadata_compression_brotli) {
1288
0
#if HAVE_BROTLI
1289
0
    data_array = compress_brotli((const uint8_t*)data, size);
1290
0
    item->set_content_encoding("br");
1291
#else
1292
    return Error(heif_error_Unsupported_feature,
1293
                 heif_suberror_Unsupported_header_compression_method);
1294
#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
164
{
1350
164
  if (!m_pitm_box) {
1351
164
    m_pitm_box = std::make_shared<Box_pitm>();
1352
164
    m_meta_box->replace_child_box(m_pitm_box);
1353
164
  }
1354
1355
164
  m_pitm_box->set_item_ID(id);
1356
164
}
1357
1358
1359
void HeifFile::set_ipco_box(std::shared_ptr<Box_ipco> ipco)
1360
140
{
1361
140
  m_ipco_box = ipco;
1362
140
  m_meta_box->replace_child_box(ipco);
1363
140
}
1364
1365
1366
void HeifFile::set_ipma_box(std::shared_ptr<Box_ipma> ipma)
1367
133
{
1368
133
  m_ipma_box = ipma;
1369
133
  m_meta_box->replace_child_box(ipma);
1370
133
}
1371
1372
1373
void HeifFile::set_iloc_box(std::shared_ptr<Box_iloc> iloc)
1374
133
{
1375
133
  m_iloc_box = iloc;
1376
133
  m_meta_box->replace_child_box(iloc);
1377
133
}
1378
1379
1380
void HeifFile::set_iref_box(std::shared_ptr<Box_iref> iref)
1381
133
{
1382
133
  m_iref_box = iref;
1383
133
  m_meta_box->replace_child_box(iref);
1384
133
}
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
}