1 /*
2  * HEIF codec.
3  * Copyright (c) 2017 struktur AG, Dirk Farin <farin@struktur.de>
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 "heif_file.h"
22 
23 #include <fstream>
24 #include <limits>
25 #include <sstream>
26 #include <utility>
27 #include <cstring>
28 #include <cassert>
29 
30 #ifdef _MSC_VER
31 
32 #ifndef NOMINMAX
33 #define NOMINMAX 1
34 #endif
35 
36 #include <Windows.h>
37 #endif
38 
39 using namespace heif;
40 
41 // TODO: make this a decoder option
42 #define STRICT_PARSING false
43 
44 
HeifFile()45 HeifFile::HeifFile()
46 {
47 }
48 
49 
~HeifFile()50 HeifFile::~HeifFile()
51 {
52 }
53 
54 
get_item_IDs() const55 std::vector<heif_item_id> HeifFile::get_item_IDs() const
56 {
57   std::vector<heif_item_id> IDs;
58 
59   for (const auto& infe : m_infe_boxes) {
60     IDs.push_back(infe.second->get_item_ID());
61   }
62 
63   return IDs;
64 }
65 
66 
read_from_file(const char * input_filename)67 Error HeifFile::read_from_file(const char* input_filename)
68 {
69 #ifdef _MSC_VER
70   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));
71 #else
72   auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(input_filename, std::ios_base::binary));
73 #endif
74   if (!input_stream_istr->good()) {
75     std::stringstream sstr;
76     sstr << "Error opening file: " << strerror(errno) << " (" << errno << ")\n";
77     return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str());
78   }
79 
80   auto input_stream = std::make_shared<StreamReader_istream>(std::move(input_stream_istr));
81   return read(input_stream);
82 }
83 
84 
read_from_memory(const void * data,size_t size,bool copy)85 Error HeifFile::read_from_memory(const void* data, size_t size, bool copy)
86 {
87   auto input_stream = std::make_shared<StreamReader_memory>((const uint8_t*) data, size, copy);
88 
89   return read(input_stream);
90 }
91 
92 
read(std::shared_ptr<StreamReader> reader)93 Error HeifFile::read(std::shared_ptr<StreamReader> reader)
94 {
95   m_input_stream = reader;
96 
97   uint64_t maxSize = std::numeric_limits<int64_t>::max();
98   heif::BitstreamRange range(m_input_stream, maxSize);
99 
100   Error error = parse_heif_file(range);
101   return error;
102 }
103 
104 
new_empty_file()105 void HeifFile::new_empty_file()
106 {
107   m_input_stream.reset();
108   m_top_level_boxes.clear();
109 
110   m_ftyp_box = std::make_shared<Box_ftyp>();
111   m_hdlr_box = std::make_shared<Box_hdlr>();
112   m_meta_box = std::make_shared<Box_meta>();
113   m_ipco_box = std::make_shared<Box_ipco>();
114   m_ipma_box = std::make_shared<Box_ipma>();
115   m_iloc_box = std::make_shared<Box_iloc>();
116   m_iinf_box = std::make_shared<Box_iinf>();
117   m_iprp_box = std::make_shared<Box_iprp>();
118   m_pitm_box = std::make_shared<Box_pitm>();
119 
120   m_meta_box->append_child_box(m_hdlr_box);
121   m_meta_box->append_child_box(m_pitm_box);
122   m_meta_box->append_child_box(m_iloc_box);
123   m_meta_box->append_child_box(m_iinf_box);
124   m_meta_box->append_child_box(m_iprp_box);
125 
126   m_iprp_box->append_child_box(m_ipco_box);
127   m_iprp_box->append_child_box(m_ipma_box);
128 
129   m_infe_boxes.clear();
130 
131   m_top_level_boxes.push_back(m_ftyp_box);
132   m_top_level_boxes.push_back(m_meta_box);
133 }
134 
135 
set_brand(heif_compression_format format)136 void HeifFile::set_brand(heif_compression_format format)
137 {
138   switch (format) {
139     case heif_compression_HEVC:
140       m_ftyp_box->set_major_brand(fourcc("heic"));
141       m_ftyp_box->set_minor_version(0);
142       m_ftyp_box->add_compatible_brand(fourcc("mif1"));
143       m_ftyp_box->add_compatible_brand(fourcc("heic"));
144       break;
145 
146     case heif_compression_AV1:
147       m_ftyp_box->set_major_brand(fourcc("avif"));
148       m_ftyp_box->set_minor_version(0);
149       m_ftyp_box->add_compatible_brand(fourcc("avif"));
150       m_ftyp_box->add_compatible_brand(fourcc("mif1"));
151       break;
152 
153     default:
154       break;
155   }
156 }
157 
158 
write(StreamWriter & writer)159 void HeifFile::write(StreamWriter& writer)
160 {
161   for (auto& box : m_top_level_boxes) {
162     box->derive_box_version_recursive();
163     box->write(writer);
164   }
165 
166   m_iloc_box->write_mdat_after_iloc(writer);
167 }
168 
169 
debug_dump_boxes() const170 std::string HeifFile::debug_dump_boxes() const
171 {
172   std::stringstream sstr;
173 
174   bool first = true;
175 
176   for (const auto& box : m_top_level_boxes) {
177     // dump box content for debugging
178 
179     if (first) {
180       first = false;
181     }
182     else {
183       sstr << "\n";
184     }
185 
186     heif::Indent indent;
187     sstr << box->dump(indent);
188   }
189 
190   return sstr.str();
191 }
192 
193 
parse_heif_file(BitstreamRange & range)194 Error HeifFile::parse_heif_file(BitstreamRange& range)
195 {
196   // --- read all top-level boxes
197 
198   for (;;) {
199     std::shared_ptr<Box> box;
200     Error error = Box::read(range, &box);
201 
202     // When an EOF error is returned, this is not really a fatal exception,
203     // but simply the indication that we reached the end of the file.
204     if (error != Error::Ok || range.error() || range.eof()) {
205       break;
206     }
207 
208     m_top_level_boxes.push_back(box);
209 
210 
211     // extract relevant boxes (ftyp, meta)
212 
213     if (box->get_short_type() == fourcc("meta")) {
214       m_meta_box = std::dynamic_pointer_cast<Box_meta>(box);
215     }
216 
217     if (box->get_short_type() == fourcc("ftyp")) {
218       m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(box);
219     }
220   }
221 
222 
223 
224   // --- check whether this is a HEIF file and its structural format
225 
226   if (!m_ftyp_box) {
227     return Error(heif_error_Invalid_input,
228                  heif_suberror_No_ftyp_box);
229   }
230 
231   if (!m_ftyp_box->has_compatible_brand(fourcc("heic")) &&
232       !m_ftyp_box->has_compatible_brand(fourcc("heix")) &&
233       !m_ftyp_box->has_compatible_brand(fourcc("mif1")) &&
234       !m_ftyp_box->has_compatible_brand(fourcc("avif"))) {
235     std::stringstream sstr;
236     sstr << "File does not include any supported brands.\n";
237 
238     return Error(heif_error_Unsupported_filetype,
239                  heif_suberror_Unspecified,
240                  sstr.str());
241   }
242 
243   if (!m_meta_box) {
244     return Error(heif_error_Invalid_input,
245                  heif_suberror_No_meta_box);
246   }
247 
248 
249   m_hdlr_box = std::dynamic_pointer_cast<Box_hdlr>(m_meta_box->get_child_box(fourcc("hdlr")));
250   if (STRICT_PARSING && !m_hdlr_box) {
251     return Error(heif_error_Invalid_input,
252                  heif_suberror_No_hdlr_box);
253   }
254 
255   if (m_hdlr_box &&
256       m_hdlr_box->get_handler_type() != fourcc("pict")) {
257     return Error(heif_error_Invalid_input,
258                  heif_suberror_No_pict_handler);
259   }
260 
261 
262   // --- find mandatory boxes needed for image decoding
263 
264   m_pitm_box = std::dynamic_pointer_cast<Box_pitm>(m_meta_box->get_child_box(fourcc("pitm")));
265   if (!m_pitm_box) {
266     return Error(heif_error_Invalid_input,
267                  heif_suberror_No_pitm_box);
268   }
269 
270   m_iprp_box = std::dynamic_pointer_cast<Box_iprp>(m_meta_box->get_child_box(fourcc("iprp")));
271   if (!m_iprp_box) {
272     return Error(heif_error_Invalid_input,
273                  heif_suberror_No_iprp_box);
274   }
275 
276   m_ipco_box = std::dynamic_pointer_cast<Box_ipco>(m_iprp_box->get_child_box(fourcc("ipco")));
277   if (!m_ipco_box) {
278     return Error(heif_error_Invalid_input,
279                  heif_suberror_No_ipco_box);
280   }
281 
282   m_ipma_box = std::dynamic_pointer_cast<Box_ipma>(m_iprp_box->get_child_box(fourcc("ipma")));
283   if (!m_ipma_box) {
284     return Error(heif_error_Invalid_input,
285                  heif_suberror_No_ipma_box);
286   }
287 
288   m_iloc_box = std::dynamic_pointer_cast<Box_iloc>(m_meta_box->get_child_box(fourcc("iloc")));
289   if (!m_iloc_box) {
290     return Error(heif_error_Invalid_input,
291                  heif_suberror_No_iloc_box);
292   }
293 
294   m_idat_box = std::dynamic_pointer_cast<Box_idat>(m_meta_box->get_child_box(fourcc("idat")));
295 
296   m_iref_box = std::dynamic_pointer_cast<Box_iref>(m_meta_box->get_child_box(fourcc("iref")));
297 
298   m_iinf_box = std::dynamic_pointer_cast<Box_iinf>(m_meta_box->get_child_box(fourcc("iinf")));
299   if (!m_iinf_box) {
300     return Error(heif_error_Invalid_input,
301                  heif_suberror_No_iinf_box);
302   }
303 
304 
305 
306   // --- build list of images
307 
308   std::vector<std::shared_ptr<Box>> infe_boxes = m_iinf_box->get_child_boxes(fourcc("infe"));
309 
310   for (auto& box : infe_boxes) {
311     std::shared_ptr<Box_infe> infe_box = std::dynamic_pointer_cast<Box_infe>(box);
312     if (!infe_box) {
313       return Error(heif_error_Invalid_input,
314                    heif_suberror_No_infe_box);
315     }
316 
317     m_infe_boxes.insert(std::make_pair(infe_box->get_item_ID(), infe_box));
318   }
319 
320   return Error::Ok;
321 }
322 
323 
image_exists(heif_item_id ID) const324 bool HeifFile::image_exists(heif_item_id ID) const
325 {
326   auto image_iter = m_infe_boxes.find(ID);
327   return image_iter != m_infe_boxes.end();
328 }
329 
330 
get_infe(heif_item_id ID) const331 std::shared_ptr<Box_infe> HeifFile::get_infe(heif_item_id ID) const
332 {
333   // --- get the image from the list of all images
334 
335   auto image_iter = m_infe_boxes.find(ID);
336   if (image_iter == m_infe_boxes.end()) {
337     return nullptr;
338   }
339 
340   return image_iter->second;
341 }
342 
343 
get_item_type(heif_item_id ID) const344 std::string HeifFile::get_item_type(heif_item_id ID) const
345 {
346   auto infe_box = get_infe(ID);
347   if (!infe_box) {
348     return "";
349   }
350 
351   return infe_box->get_item_type();
352 }
353 
354 
get_content_type(heif_item_id ID) const355 std::string HeifFile::get_content_type(heif_item_id ID) const
356 {
357   auto infe_box = get_infe(ID);
358   if (!infe_box) {
359     return "";
360   }
361 
362   return infe_box->get_content_type();
363 }
364 
365 
get_properties(heif_item_id imageID,std::vector<Box_ipco::Property> & properties) const366 Error HeifFile::get_properties(heif_item_id imageID,
367                                std::vector<Box_ipco::Property>& properties) const
368 {
369   if (!m_ipco_box) {
370     return Error(heif_error_Invalid_input,
371                  heif_suberror_No_ipco_box);
372   }
373   else if (!m_ipma_box) {
374     return Error(heif_error_Invalid_input,
375                  heif_suberror_No_ipma_box);
376   }
377 
378   return m_ipco_box->get_properties_for_item_ID(imageID, m_ipma_box, properties);
379 }
380 
381 
get_image_chroma_from_configuration(heif_item_id imageID) const382 heif_chroma HeifFile::get_image_chroma_from_configuration(heif_item_id imageID) const
383 {
384   // HEVC
385 
386   auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
387   std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
388   if (hvcC_box) {
389     return (heif_chroma) (hvcC_box->get_configuration().chroma_format);
390   }
391 
392 
393   // AV1
394 
395   box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
396   std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
397   if (av1C_box) {
398     Box_av1C::configuration config = av1C_box->get_configuration();
399     if (config.chroma_subsampling_x == 1 &&
400         config.chroma_subsampling_y == 1) {
401       return heif_chroma_420;
402     }
403     else if (config.chroma_subsampling_x == 1 &&
404              config.chroma_subsampling_y == 0) {
405       return heif_chroma_422;
406     }
407     else if (config.chroma_subsampling_x == 0 &&
408              config.chroma_subsampling_y == 0) {
409       return heif_chroma_444;
410     }
411     else {
412       return heif_chroma_undefined;
413     }
414   }
415 
416 
417   assert(false);
418   return heif_chroma_undefined;
419 }
420 
421 
get_luma_bits_per_pixel_from_configuration(heif_item_id imageID) const422 int HeifFile::get_luma_bits_per_pixel_from_configuration(heif_item_id imageID) const
423 {
424   std::string image_type = get_item_type(imageID);
425 
426   // HEVC
427 
428   if (image_type == "hvc1") {
429     auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
430     std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
431     if (hvcC_box) {
432       return hvcC_box->get_configuration().bit_depth_luma;
433     }
434   }
435 
436 
437   // AV1
438 
439   if (image_type == "av01") {
440     auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
441     std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
442     if (av1C_box) {
443       Box_av1C::configuration config = av1C_box->get_configuration();
444       if (!config.high_bitdepth) {
445         return 8;
446       }
447       else if (config.twelve_bit) {
448         return 12;
449       }
450       else {
451         return 10;
452       }
453     }
454   }
455 
456   return -1;
457 }
458 
459 
get_chroma_bits_per_pixel_from_configuration(heif_item_id imageID) const460 int HeifFile::get_chroma_bits_per_pixel_from_configuration(heif_item_id imageID) const
461 {
462   std::string image_type = get_item_type(imageID);
463 
464   // HEVC
465 
466   if (image_type == "hvc1") {
467     auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
468     std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
469     if (hvcC_box) {
470       return hvcC_box->get_configuration().bit_depth_chroma;
471     }
472   }
473 
474   // AV1
475 
476   if (image_type == "av01") {
477     auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
478     std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
479     if (av1C_box) {
480       Box_av1C::configuration config = av1C_box->get_configuration();
481       if (!config.high_bitdepth) {
482         return 8;
483       }
484       else if (config.twelve_bit) {
485         return 12;
486       }
487       else {
488         return 10;
489       }
490     }
491   }
492 
493   return -1;
494 }
495 
496 
get_compressed_image_data(heif_item_id ID,std::vector<uint8_t> * data) const497 Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector<uint8_t>* data) const
498 {
499 #if ENABLE_PARALLEL_TILE_DECODING
500   std::lock_guard<std::mutex> guard(m_read_mutex);
501 #endif
502 
503   if (!image_exists(ID)) {
504     return Error(heif_error_Usage_error,
505                  heif_suberror_Nonexisting_item_referenced);
506   }
507 
508   auto infe_box = get_infe(ID);
509   if (!infe_box) {
510     return Error(heif_error_Usage_error,
511                  heif_suberror_Nonexisting_item_referenced);
512   }
513 
514 
515   std::string item_type = infe_box->get_item_type();
516   std::string content_type = infe_box->get_content_type();
517 
518   // --- get coded image data pointers
519 
520   auto items = m_iloc_box->get_items();
521   const Box_iloc::Item* item = nullptr;
522   for (const auto& i : items) {
523     if (i.item_ID == ID) {
524       item = &i;
525       break;
526     }
527   }
528   if (!item) {
529     std::stringstream sstr;
530     sstr << "Item with ID " << ID << " has no compressed data";
531 
532     return Error(heif_error_Invalid_input,
533                  heif_suberror_No_item_data,
534                  sstr.str());
535   }
536 
537   Error error = Error(heif_error_Unsupported_feature,
538                       heif_suberror_Unsupported_codec);
539   if (item_type == "hvc1") {
540     // --- --- --- HEVC
541 
542     // --- get properties for this image
543 
544     std::vector<Box_ipco::Property> properties;
545     Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties);
546     if (err) {
547       return err;
548     }
549 
550     // --- get codec configuration
551 
552     std::shared_ptr<Box_hvcC> hvcC_box;
553     for (auto& prop : properties) {
554       if (prop.property->get_short_type() == fourcc("hvcC")) {
555         hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(prop.property);
556         if (hvcC_box) {
557           break;
558         }
559       }
560     }
561 
562     if (!hvcC_box) {
563       // Should always have an hvcC box, because we are checking this in
564       // heif_context::interpret_heif_file()
565       assert(false);
566       return Error(heif_error_Invalid_input,
567                    heif_suberror_No_hvcC_box);
568     }
569     else if (!hvcC_box->get_headers(data)) {
570       return Error(heif_error_Invalid_input,
571                    heif_suberror_No_item_data);
572     }
573 
574     error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
575   }
576   else if (item_type == "av01") {
577     // --- --- --- AV1
578 
579     // --- get properties for this image
580 
581     std::vector<Box_ipco::Property> properties;
582     Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties);
583     if (err) {
584       return err;
585     }
586 
587     // --- get codec configuration
588 
589     std::shared_ptr<Box_av1C> av1C_box;
590     for (auto& prop : properties) {
591       if (prop.property->get_short_type() == fourcc("av1C")) {
592         av1C_box = std::dynamic_pointer_cast<Box_av1C>(prop.property);
593         if (av1C_box) {
594           break;
595         }
596       }
597     }
598 
599     if (!av1C_box) {
600       // Should always have an hvcC box, because we are checking this in
601       // heif_context::interpret_heif_file()
602       return Error(heif_error_Invalid_input,
603                    heif_suberror_No_av1C_box);
604     }
605     else if (!av1C_box->get_headers(data)) {
606       return Error(heif_error_Invalid_input,
607                    heif_suberror_No_item_data);
608     }
609 
610     error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
611   }
612   else if (true ||  // fallback case for all kinds of generic metadata (e.g. 'iptc')
613            item_type == "grid" ||
614            item_type == "iovl" ||
615            item_type == "Exif" ||
616            (item_type == "mime" && content_type == "application/rdf+xml")) {
617     error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
618   }
619 
620   if (error != Error::Ok) {
621     return error;
622   }
623 
624   return Error::Ok;
625 }
626 
627 
get_unused_item_id() const628 heif_item_id HeifFile::get_unused_item_id() const
629 {
630   for (heif_item_id id = 1;;
631        id++) {
632 
633     bool id_exists = false;
634 
635     for (const auto& infe : m_infe_boxes) {
636       if (infe.second->get_item_ID() == id) {
637         id_exists = true;
638         break;
639       }
640     }
641 
642     if (!id_exists) {
643       return id;
644     }
645   }
646 
647   assert(false); // should never be reached
648   return 0;
649 }
650 
651 
add_new_image(const char * item_type)652 heif_item_id HeifFile::add_new_image(const char* item_type)
653 {
654   auto box = add_new_infe_box(item_type);
655   return box->get_item_ID();
656 }
657 
658 
add_new_infe_box(const char * item_type)659 std::shared_ptr<Box_infe> HeifFile::add_new_infe_box(const char* item_type)
660 {
661   heif_item_id id = get_unused_item_id();
662 
663   auto infe = std::make_shared<Box_infe>();
664   infe->set_item_ID(id);
665   infe->set_hidden_item(false);
666   infe->set_item_type(item_type);
667 
668   m_infe_boxes[id] = infe;
669   m_iinf_box->append_child_box(infe);
670 
671   return infe;
672 }
673 
674 
add_ispe_property(heif_item_id id,uint32_t width,uint32_t height)675 void HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height)
676 {
677   auto ispe = std::make_shared<Box_ispe>();
678   ispe->set_size(width, height);
679 
680   int index = m_ipco_box->append_child_box(ispe);
681 
682   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{false, uint16_t(index + 1)});
683 }
684 
add_clap_property(heif_item_id id,uint32_t clap_width,uint32_t clap_height,uint32_t image_width,uint32_t image_height)685 void HeifFile::add_clap_property(heif_item_id id, uint32_t clap_width, uint32_t clap_height,
686                                  uint32_t image_width, uint32_t image_height)
687 {
688   auto clap = std::make_shared<Box_clap>();
689   clap->set(clap_width, clap_height, image_width, image_height);
690 
691   int index = m_ipco_box->append_child_box(clap);
692 
693   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
694 }
695 
696 
add_pixi_property(heif_item_id id,uint8_t c1,uint8_t c2,uint8_t c3)697 void HeifFile::add_pixi_property(heif_item_id id, uint8_t c1, uint8_t c2, uint8_t c3)
698 {
699   auto pixi = std::make_shared<Box_pixi>();
700   pixi->add_channel_bits(c1);
701   if (c2 || c3) {
702     pixi->add_channel_bits(c2);
703     pixi->add_channel_bits(c3);
704   }
705 
706   int index = m_ipco_box->append_child_box(pixi);
707 
708   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
709 }
710 
711 
add_hvcC_property(heif_item_id id)712 void HeifFile::add_hvcC_property(heif_item_id id)
713 {
714   auto hvcC = std::make_shared<Box_hvcC>();
715   int index = m_ipco_box->append_child_box(hvcC);
716 
717   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
718 }
719 
720 
append_hvcC_nal_data(heif_item_id id,const std::vector<uint8_t> & nal_data)721 Error HeifFile::append_hvcC_nal_data(heif_item_id id, const std::vector<uint8_t>& nal_data)
722 {
723   auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
724                                                                                        m_ipma_box,
725                                                                                        fourcc("hvcC")));
726 
727   if (hvcC) {
728     hvcC->append_nal_data(nal_data);
729     return Error::Ok;
730   }
731   else {
732     // Should always have an hvcC box, because we are checking this in
733     // heif_context::interpret_heif_file()
734     assert(false);
735     return Error(heif_error_Usage_error,
736                  heif_suberror_No_hvcC_box);
737   }
738 }
739 
740 
set_hvcC_configuration(heif_item_id id,const Box_hvcC::configuration & config)741 Error HeifFile::set_hvcC_configuration(heif_item_id id, const Box_hvcC::configuration& config)
742 {
743   auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
744                                                                                        m_ipma_box,
745                                                                                        fourcc("hvcC")));
746 
747   if (hvcC) {
748     hvcC->set_configuration(config);
749     return Error::Ok;
750   }
751   else {
752     return Error(heif_error_Usage_error,
753                  heif_suberror_No_hvcC_box);
754   }
755 }
756 
757 
append_hvcC_nal_data(heif_item_id id,const uint8_t * data,size_t size)758 Error HeifFile::append_hvcC_nal_data(heif_item_id id, const uint8_t* data, size_t size)
759 {
760   std::vector<Box_ipco::Property> properties;
761 
762   auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
763                                                                                        m_ipma_box,
764                                                                                        fourcc("hvcC")));
765 
766   if (hvcC) {
767     hvcC->append_nal_data(data, size);
768     return Error::Ok;
769   }
770   else {
771     return Error(heif_error_Usage_error,
772                  heif_suberror_No_hvcC_box);
773   }
774 }
775 
776 
add_av1C_property(heif_item_id id)777 void HeifFile::add_av1C_property(heif_item_id id)
778 {
779   auto av1C = std::make_shared<Box_av1C>();
780   int index = m_ipco_box->append_child_box(av1C);
781 
782   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
783 }
784 
785 
set_av1C_configuration(heif_item_id id,const Box_av1C::configuration & config)786 Error HeifFile::set_av1C_configuration(heif_item_id id, const Box_av1C::configuration& config)
787 {
788   auto av1C = std::dynamic_pointer_cast<Box_av1C>(m_ipco_box->get_property_for_item_ID(id,
789                                                                                        m_ipma_box,
790                                                                                        fourcc("av1C")));
791 
792   if (av1C) {
793     av1C->set_configuration(config);
794     return Error::Ok;
795   }
796   else {
797     return Error(heif_error_Usage_error,
798                  heif_suberror_No_av1C_box);
799   }
800 }
801 
802 
append_iloc_data(heif_item_id id,const std::vector<uint8_t> & nal_packets,uint8_t construction_method)803 void HeifFile::append_iloc_data(heif_item_id id, const std::vector<uint8_t>& nal_packets, uint8_t construction_method)
804 {
805   m_iloc_box->append_data(id, nal_packets, construction_method);
806 }
807 
808 
append_iloc_data_with_4byte_size(heif_item_id id,const uint8_t * data,size_t size)809 void HeifFile::append_iloc_data_with_4byte_size(heif_item_id id, const uint8_t* data, size_t size)
810 {
811   std::vector<uint8_t> nal;
812   nal.resize(size + 4);
813 
814   nal[0] = (uint8_t) ((size >> 24) & 0xFF);
815   nal[1] = (uint8_t) ((size >> 16) & 0xFF);
816   nal[2] = (uint8_t) ((size >> 8) & 0xFF);
817   nal[3] = (uint8_t) ((size >> 0) & 0xFF);
818 
819   memcpy(nal.data() + 4, data, size);
820 
821   append_iloc_data(id, nal);
822 }
823 
set_primary_item_id(heif_item_id id)824 void HeifFile::set_primary_item_id(heif_item_id id)
825 {
826   m_pitm_box->set_item_ID(id);
827 }
828 
add_iref_reference(uint32_t type,heif_item_id from,std::vector<heif_item_id> to)829 void HeifFile::add_iref_reference(uint32_t type, heif_item_id from,
830                                   std::vector<heif_item_id> to)
831 {
832   if (!m_iref_box) {
833     m_iref_box = std::make_shared<Box_iref>();
834     m_meta_box->append_child_box(m_iref_box);
835   }
836 
837   m_iref_box->add_reference(type, from, to);
838 }
839 
set_auxC_property(heif_item_id id,std::string type)840 void HeifFile::set_auxC_property(heif_item_id id, std::string type)
841 {
842   auto auxC = std::make_shared<Box_auxC>();
843   auxC->set_aux_type(type);
844 
845   int index = m_ipco_box->append_child_box(auxC);
846 
847   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
848 }
849 
set_color_profile(heif_item_id id,const std::shared_ptr<const color_profile> profile)850 void HeifFile::set_color_profile(heif_item_id id, const std::shared_ptr<const color_profile> profile)
851 {
852   auto colr = std::make_shared<Box_colr>();
853   colr->set_color_profile(profile);
854 
855   int index = m_ipco_box->append_child_box(colr);
856   m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
857 }
858 
859 
860 // TODO: the hdlr box is probably not the right place for this. Into which box should we write comments?
set_hdlr_library_info(std::string encoder_plugin_version)861 void HeifFile::set_hdlr_library_info(std::string encoder_plugin_version)
862 {
863   std::stringstream sstr;
864   sstr << "libheif (" << LIBHEIF_VERSION << ") / " << encoder_plugin_version;
865   m_hdlr_box->set_name(sstr.str());
866 }
867 
868 
869 #ifdef _MSC_VER
convert_utf8_path_to_utf16(std::string str)870 std::wstring HeifFile::convert_utf8_path_to_utf16(std::string str)
871 {
872   std::wstring ret;
873   int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
874   if (len > 0)
875   {
876     ret.resize(len);
877     MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len);
878   }
879   return ret;
880 }
881 #endif
882