diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 7f6048e371..ddf55a2cef 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -305,6 +305,13 @@ heif_brand2 heif_fourcc_to_brand(const char* fourcc_string) return fourcc(fourcc_string); } +heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len) +{ + if (len < 16) { + return heif_unknown_brand; + } + return heif_fourcc_to_brand((char*) (data + 12)); +} void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc) { diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 7739d772f3..756c3fd921 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -248,6 +248,9 @@ enum heif_suberror_code heif_suberror_No_avcC_box = 143, + // we got a mini box, but could not read it properly + heif_suberror_No_mini_box = 149, + // Decompressing generic compression or header compression data failed (e.g. bitstream corruption) heif_suberror_Decompression_invalid_data = 150, @@ -770,6 +773,13 @@ typedef uint32_t heif_brand2; */ #define heif_brand2_mif2 heif_fourcc('m','i','f','2') +/** + * HEIF image structural brand (`mif3`). + * + * This indicates the low-overhead (ftyp+mini) structure. + */ +#define heif_brand2_mif3 heif_fourcc('m','i','f','3') + /** * HEIF image sequence structural brand (`msf1`). * @@ -871,6 +881,10 @@ typedef uint32_t heif_brand2; LIBHEIF_API heif_brand2 heif_read_main_brand(const uint8_t* data, int len); +// input data should be at least 16 bytes +LIBHEIF_API +heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len); + // 'brand_fourcc' must be 4 character long, but need not be 0-terminated LIBHEIF_API heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc); diff --git a/libheif/api/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h index 518f92e300..6d2946c6f9 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -360,6 +360,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_Missing_grid_images", heif_suberror_Missing_grid_images) .value("heif_suberror_No_av1C_box", heif_suberror_No_av1C_box) .value("heif_suberror_No_avcC_box", heif_suberror_No_avcC_box) + .value("heif_suberror_No_mini_box", heif_suberror_No_mini_box) .value("heif_suberror_Invalid_clean_aperture", heif_suberror_Invalid_clean_aperture) .value("heif_suberror_Invalid_overlay_data", heif_suberror_Invalid_overlay_data) .value("heif_suberror_Overlay_image_outside_of_canvas", heif_suberror_Overlay_image_outside_of_canvas) diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc index 3a0c9d454b..b6191e0650 100644 --- a/libheif/bitstream.cc +++ b/libheif/bitstream.cc @@ -489,6 +489,20 @@ uint32_t BitReader::get_bits32(int n) return static_cast(get_bits(n)); } +bool BitReader::get_flag() +{ + return (get_bits(1) == 0x01); +} + +std::vector BitReader::read_bytes(uint32_t n) +{ + // TODO: this implementation isn't very efficient + std::vector bytes; + for (uint32_t i = 0; i < n; i++) { + bytes.push_back(get_bits8(8)); + } + return bytes; +} int BitReader::get_bits_fast(int n) { diff --git a/libheif/bitstream.h b/libheif/bitstream.h index 8d186d73a4..836af466a5 100644 --- a/libheif/bitstream.h +++ b/libheif/bitstream.h @@ -385,6 +385,15 @@ class BitReader uint32_t get_bits32(int n); + /** + * Get a one-bit flag value. + * + * @returns true if the next bit value is 1, otherwise false + */ + bool get_flag(); + + std::vector read_bytes(uint32_t n); + int get_bits_fast(int n); int peek_bits(int n); @@ -411,10 +420,21 @@ class BitReader return ((int64_t) bytes_remaining) * 8 + nextbits_cnt; } + void set_start_offset(uint64_t offset) + { + start_offset = offset; + } + + uint64_t get_file_offset() const + { + return start_offset + (data_length - bytes_remaining - (nextbits_cnt / 8)); + } + private: const uint8_t* data; int data_length; int bytes_remaining; + uint64_t start_offset = 0; uint64_t nextbits; // left-aligned bits int nextbits_cnt; diff --git a/libheif/box.cc b/libheif/box.cc index 752d99993e..e576e1f748 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1053,7 +1053,7 @@ Error Box_ftyp::parse(BitstreamRange& range, const heif_security_limits* limits) m_minor_version = range.read32(); uint64_t box_size = get_box_size(); - if (box_size < 8 || box_size - 8 <= get_header_size()) { + if (box_size < 8 || box_size - 8 < get_header_size()) { // Sanity check. return Error(heif_error_Invalid_input, heif_suberror_Invalid_box_size, @@ -1086,8 +1086,15 @@ std::string Box_ftyp::dump(Indent& indent) const sstr << BoxHeader::dump(indent); sstr << indent << "major brand: " << fourcc_to_string(m_major_brand) << "\n" - << indent << "minor version: " << m_minor_version << "\n" - << indent << "compatible brands: "; + << indent << "minor version: "; + if (m_minor_version < ('A' << 24)) { + // This is probably a version number + sstr << m_minor_version; + } else { + // probably a 4CC, as used for mif3 + sstr << fourcc_to_string(m_minor_version); + } + sstr << "\n" << indent << "compatible brands: "; bool first = true; for (uint32_t brand : m_compatible_brands) { diff --git a/libheif/box.h b/libheif/box.h index b84ea715ea..946267e110 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -386,8 +386,12 @@ class Box_ftyp : public Box std::vector list_brands() const { return m_compatible_brands; } + uint32_t get_major_brand() const { return m_major_brand; } + void set_major_brand(heif_brand2 major_brand) { m_major_brand = major_brand; } + uint32_t get_minor_version() const { return m_minor_version; } + void set_minor_version(uint32_t minor_version) { m_minor_version = minor_version; } void clear_compatible_brands() { m_compatible_brands.clear(); } @@ -551,6 +555,8 @@ class Box_iloc : public FullBox Error write_mdat_after_iloc(StreamWriter& writer); + void append_item(Item &item) { m_items.push_back(item); } + protected: Error parse(BitstreamRange& range, const heif_security_limits*) override; diff --git a/libheif/codecs/avif_boxes.h b/libheif/codecs/avif_boxes.h index b233c6c2d7..8b678e511a 100644 --- a/libheif/codecs/avif_boxes.h +++ b/libheif/codecs/avif_boxes.h @@ -35,6 +35,10 @@ class Box_av1C : public Box { + +// allow access to protected parse() method +friend class HeifFile; + public: Box_av1C() { diff --git a/libheif/error.cc b/libheif/error.cc index 52ee52bf62..43e976a5ca 100644 --- a/libheif/error.cc +++ b/libheif/error.cc @@ -174,6 +174,8 @@ const char* Error::get_error_string(heif_suberror_code err) return "Invalid data in generic compression inflation"; case heif_suberror_No_icbr_box: return "No 'icbr' box"; + case heif_suberror_No_mini_box: + return "No 'mini' box"; // --- Memory_allocation_error --- diff --git a/libheif/file.cc b/libheif/file.cc index d48a6ccaf4..5a522049eb 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -26,6 +26,7 @@ #include "image-items/jpeg2000.h" #include "image-items/jpeg.h" #include "image-items/vvc.h" +#include "codecs/avif_boxes.h" #include "codecs/uncompressed/unc_boxes.h" #include