Commit 1ac5b974 for libheif
commit 1ac5b9745b994a4a57de5198fdd155268eef56a3
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Thu Apr 9 15:05:57 2026 +0200
mini box: updated to current DAmd2 spec (#1752)
diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc
index 4ac2805d..aa2f1ad2 100644
--- a/libheif/bitstream.cc
+++ b/libheif/bitstream.cc
@@ -649,7 +649,7 @@ int BitReader::peek_bits(int n)
return (int) val;
}
-void BitReader::skip_bytes(int nBytes)
+void BitReader::skip_bytes(uint32_t nBytes)
{
// TODO: this is slow
while (nBytes--) {
diff --git a/libheif/bitstream.h b/libheif/bitstream.h
index 70c462a0..649285dc 100644
--- a/libheif/bitstream.h
+++ b/libheif/bitstream.h
@@ -435,7 +435,7 @@ public:
int peek_bits(int n);
- void skip_bytes(int nBytes);
+ void skip_bytes(uint32_t nBytes);
void skip_bits(int n);
diff --git a/libheif/mini.cc b/libheif/mini.cc
index dc689b18..6752c484 100644
--- a/libheif/mini.cc
+++ b/libheif/mini.cc
@@ -1,6 +1,7 @@
/*
* HEIF codec.
* Copyright (c) 2024 Brad Hards <bradh@frogmouth.net>
+ * Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
*
* This file is part of libheif.
*
@@ -35,9 +36,12 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
{
uint64_t start_offset = range.get_istream()->get_position();
std::size_t length = range.get_remaining_bytes();
+
std::vector<uint8_t> mini_data(length);
range.read(mini_data.data(), mini_data.size());
+
BitReader bits(mini_data.data(), (int)(mini_data.size()));
+
m_version = bits.get_bits8(2);
m_explicit_codec_types_flag = bits.get_flag();
m_float_flag = bits.get_flag();
@@ -50,38 +54,33 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
m_xmp_flag = bits.get_flag();
m_chroma_subsampling = bits.get_bits8(2);
m_orientation = bits.get_bits8(3) + 1;
- bool small_dimensions_flag = bits.get_flag();
- if (small_dimensions_flag)
- {
- m_width = 1 + bits.get_bits32(7);
- m_height = 1 + bits.get_bits32(7);
- }
- else
- {
- m_width = 1 + bits.get_bits32(15);
- m_height = 1 + bits.get_bits32(15);
- }
+
+ bool large_dimensions_flag = bits.get_flag();
+ // large_dimensions_flag = !large_dimensions_flag; // HACK to get old behavior
+ m_width = 1 + bits.get_bits32(large_dimensions_flag ? 15 : 7);
+ m_height = 1 + bits.get_bits32(large_dimensions_flag ? 15 : 7);
+
if ((m_chroma_subsampling == 1) || (m_chroma_subsampling == 2))
{
- m_chroma_is_horizontally_centred = bits.get_flag();
+ m_chroma_is_horizontally_centered = bits.get_flag();
}
if (m_chroma_subsampling == 1)
{
- m_chroma_is_vertically_centred = bits.get_flag();
+ m_chroma_is_vertically_centered = bits.get_flag();
}
bool high_bit_depth_flag = false;
if (m_float_flag)
{
- uint8_t bit_depth_log2_minus4 = bits.get_bits8(2);
- m_bit_depth = (uint8_t)powl(2, (bit_depth_log2_minus4 + 4));
+ uint8_t bit_depth_log2 = bits.get_bits8(2) + 4; // [4;7]
+ m_bit_depth = (uint8_t)powl(2, (bit_depth_log2)); // [16,32,64,128]
}
else
{
high_bit_depth_flag = bits.get_flag();
if (high_bit_depth_flag)
{
- m_bit_depth = 9 + bits.get_bits8(3);
+ m_bit_depth = bits.get_bits8(3) + 9; // [9;16]
}
}
@@ -115,15 +114,15 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
m_infe_type = bits.get_bits32(32);
m_codec_config_type = bits.get_bits32(32);
}
+
if (m_hdr_flag)
{
m_gainmap_flag = bits.get_flag();
if (m_gainmap_flag)
{
- uint32_t gainmap_width_minus1 = bits.get_bits32(small_dimensions_flag ? 7 : 15);
- m_gainmap_width = gainmap_width_minus1 + 1;
- uint32_t gainmap_height_minus1 = bits.get_bits32(small_dimensions_flag ? 7 : 15);
- m_gainmap_height = gainmap_height_minus1 + 1;
+ m_gainmap_width = bits.get_bits32(large_dimensions_flag ? 15 : 7) + 1;
+ m_gainmap_height = bits.get_bits32(large_dimensions_flag ? 15 : 7) + 1;
+
m_gainmap_matrix_coefficients = bits.get_bits8(8);
m_gainmap_full_range_flag = bits.get_flag();
m_gainmap_chroma_subsampling = bits.get_bits8(2);
@@ -135,13 +134,14 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
{
m_gainmap_chroma_is_vertically_centred = bits.get_flag();
}
+
m_gainmap_float_flag = bits.get_flag();
bool gainmap_high_bit_depth_flag = false;
if (m_gainmap_float_flag)
{
- uint8_t bit_depth_log2_minus4 = bits.get_bits8(2);
- m_gainmap_bit_depth = (uint8_t)powl(2, (bit_depth_log2_minus4 + 4));
+ uint8_t bit_depth_log2 = bits.get_bits8(2) + 4;
+ m_gainmap_bit_depth = (uint8_t)powl(2, (bit_depth_log2));
}
else
{
@@ -151,6 +151,7 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
m_gainmap_bit_depth = 9 + bits.get_bits8(3);
}
}
+
m_tmap_icc_flag = bits.get_flag();
m_tmap_explicit_cicp_flag = bits.get_flag();
if (m_tmap_explicit_cicp_flag)
@@ -348,99 +349,125 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
}
// Chunk sizes
- bool few_metadata_bytes_flag = false;
+ bool large_metadata_flag = false;
if (m_icc_flag || m_exif_flag || m_xmp_flag || (m_hdr_flag && m_gainmap_flag))
{
- few_metadata_bytes_flag = bits.get_flag();
+ large_metadata_flag = bits.get_flag();
}
- bool few_codec_config_bytes_flag = bits.get_flag();
- bool few_item_data_bytes_flag = bits.get_flag();
+
+ bool large_codec_config_flag = bits.get_flag();
+ bool large_item_data_flag = bits.get_flag();
uint32_t icc_data_size = 0;
if (m_icc_flag)
{
- icc_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1;
+ icc_data_size = bits.get_bits32(large_metadata_flag ? 20 : 10) + 1;
}
+
uint32_t tmap_icc_data_size = 0;
if (m_hdr_flag && m_gainmap_flag && m_tmap_icc_flag)
{
- tmap_icc_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1;
+ tmap_icc_data_size = bits.get_bits32(large_metadata_flag ? 20 : 10) + 1;
}
+
uint32_t gainmap_metadata_size = 0;
if (m_hdr_flag && m_gainmap_flag)
{
- gainmap_metadata_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20);
+ gainmap_metadata_size = bits.get_bits32(large_metadata_flag ? 20 : 10);
}
+
if (m_hdr_flag && m_gainmap_flag)
{
- m_gainmap_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28);
+ m_gainmap_item_data_size = bits.get_bits32(large_item_data_flag ? 28 : 15);
}
+
uint32_t gainmap_item_codec_config_size = 0;
- if (m_hdr_flag && m_gainmap_flag && (m_gainmap_item_data_size > 0))
+ if (m_hdr_flag && m_gainmap_flag && m_gainmap_item_data_size > 0)
{
- gainmap_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12);
+ gainmap_item_codec_config_size = bits.get_bits32(large_codec_config_flag ? 12 : 3);
}
- uint32_t main_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12);
- m_main_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28) + 1;
+ uint32_t main_item_codec_config_size = bits.get_bits32(large_codec_config_flag ? 12 : 3);
+ m_main_item_data_size = bits.get_bits32(large_item_data_flag ? 28 : 15) + 1;
if (m_alpha_flag)
{
- m_alpha_item_data_size = bits.get_bits32(few_item_data_bytes_flag ? 15 : 28);
+ m_alpha_item_data_size = bits.get_bits32(large_item_data_flag ? 28 : 15);
}
+
uint32_t alpha_item_codec_config_size = 0;
- if (m_alpha_flag && (m_alpha_item_data_size > 0))
+ if (m_alpha_flag && m_alpha_item_data_size > 0)
+ {
+ alpha_item_codec_config_size = bits.get_bits32(large_codec_config_flag ? 12 : 3);
+ }
+
+ if (m_exif_flag || m_xmp_flag)
{
- alpha_item_codec_config_size = bits.get_bits32(few_codec_config_bytes_flag ? 3 : 12);
+ m_exif_xmp_compressed_flag = bits.get_flag();
}
if (m_exif_flag)
{
- m_exif_item_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1;
+ m_exif_data_size = bits.get_bits32(large_metadata_flag ? 20 : 10) + 1;
}
if (m_xmp_flag)
{
- m_xmp_item_data_size = bits.get_bits32(few_metadata_bytes_flag ? 10 : 20) + 1;
+ m_xmp_data_size = bits.get_bits32(large_metadata_flag ? 20 : 10) + 1;
}
bits.skip_to_byte_boundary();
- // Chunks
- if (m_alpha_flag && (m_alpha_item_data_size > 0) && (alpha_item_codec_config_size > 0))
+ if (main_item_codec_config_size > 0)
{
- m_alpha_item_codec_config = bits.read_bytes(alpha_item_codec_config_size);
+ m_main_item_codec_config = bits.read_bytes(main_item_codec_config_size);
}
- if (m_hdr_flag && m_gainmap_flag && (gainmap_item_codec_config_size > 0))
- {
- m_gainmap_item_codec_config = bits.read_bytes(gainmap_item_codec_config_size);
+
+ // Chunks
+ if (m_alpha_flag && m_alpha_item_data_size > 0) {
+ if (alpha_item_codec_config_size == 0) {
+ m_alpha_item_codec_config = m_main_item_codec_config;
+ }
+ else {
+ // TODO: should we have a flag indicating that we have explicit config for alpha?
+ m_alpha_item_codec_config = bits.read_bytes(alpha_item_codec_config_size);
+ }
}
- if (main_item_codec_config_size > 0)
+
+ if (m_hdr_flag && m_gainmap_flag && m_gainmap_item_data_size > 0)
{
- m_main_item_codec_config = bits.read_bytes(main_item_codec_config_size);
+ if (gainmap_item_codec_config_size == 0) {
+ m_gainmap_item_codec_config = m_main_item_codec_config;
+ }
+ else {
+ // TODO: should we have a flag indicating that we have explicit config for the gain map?
+ m_gainmap_item_codec_config = bits.read_bytes(gainmap_item_codec_config_size);
+ }
}
if (m_icc_flag)
{
m_icc_data = bits.read_bytes(icc_data_size);
}
+
if (m_hdr_flag && m_gainmap_flag && m_tmap_icc_flag)
{
m_tmap_icc_data = bits.read_bytes(tmap_icc_data_size);
}
- if (m_hdr_flag && m_gainmap_flag && (gainmap_metadata_size > 0))
+
+ if (m_hdr_flag && m_gainmap_flag && gainmap_metadata_size > 0)
{
m_gainmap_metadata = bits.read_bytes(gainmap_metadata_size);
}
- if (m_alpha_flag && (m_alpha_item_data_size > 0))
+ if (m_alpha_flag && m_alpha_item_data_size > 0)
{
m_alpha_item_data_offset = bits.get_current_byte_index() + start_offset;
bits.skip_bytes(m_alpha_item_data_size);
}
- if (m_alpha_flag && m_gainmap_flag && (m_gainmap_item_data_size > 0))
+ if (m_alpha_flag && m_gainmap_flag && m_gainmap_item_data_size > 0)
{
m_gainmap_item_data_offset = bits.get_current_byte_index() + start_offset;
- bits.skip_bits(m_gainmap_item_data_size);
+ bits.skip_bytes(m_gainmap_item_data_size);
}
m_main_item_data_offset = bits.get_current_byte_index() + start_offset;
@@ -449,13 +476,14 @@ Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits)
if (m_exif_flag)
{
m_exif_item_data_offset = bits.get_current_byte_index() + start_offset;
- bits.skip_bytes(m_exif_item_data_size);
+ bits.skip_bytes(m_exif_data_size);
}
if (m_xmp_flag)
{
m_xmp_item_data_offset = bits.get_current_byte_index() + start_offset;
- bits.skip_bytes(m_xmp_item_data_size);
+ bits.skip_bytes(m_xmp_data_size);
}
+
return range.get_error();
}
@@ -483,11 +511,11 @@ std::string Box_mini::dump(Indent &indent) const
if ((m_chroma_subsampling == 1) || (m_chroma_subsampling == 2))
{
- sstr << indent << "chroma_is_horizontally_centered: " << m_chroma_is_horizontally_centred << "\n";
+ sstr << indent << "chroma_is_horizontally_centered: " << m_chroma_is_horizontally_centered << "\n";
}
if (m_chroma_subsampling == 1)
{
- sstr << indent << "chroma_is_vertically_centered: " << m_chroma_is_vertically_centred << "\n";
+ sstr << indent << "chroma_is_vertically_centered: " << m_chroma_is_vertically_centered << "\n";
}
sstr << "bit_depth: " << (int)m_bit_depth << "\n";
@@ -733,11 +761,11 @@ std::string Box_mini::dump(Indent &indent) const
if (m_exif_flag)
{
- sstr << "exif_data offset: " << m_exif_item_data_offset << ", size: " << m_exif_item_data_size << "\n";
+ sstr << "exif_data offset: " << m_exif_item_data_offset << ", size: " << m_exif_data_size << "\n";
}
if (m_xmp_flag)
{
- sstr << "xmp_data offset: " << m_xmp_item_data_offset << ", size: " << m_xmp_item_data_size << "\n";
+ sstr << "xmp_data offset: " << m_xmp_item_data_offset << ", size: " << m_xmp_data_size << "\n";
}
return sstr.str();
}
@@ -800,6 +828,9 @@ Error Box_mini::create_expanded_boxes(class HeifFile* file)
exif_infe_box->set_flags(1);
exif_infe_box->set_item_ID(6);
exif_infe_box->set_item_type_4cc(fourcc("Exif"));
+ if (m_exif_xmp_compressed_flag) {
+ exif_infe_box->set_content_encoding("deflate");
+ }
file->add_infe_box(6, exif_infe_box);
}
@@ -810,6 +841,9 @@ Error Box_mini::create_expanded_boxes(class HeifFile* file)
xmp_infe_box->set_item_ID(7);
xmp_infe_box->set_item_type_4cc(fourcc("mime"));
xmp_infe_box->set_content_type("application/rdf+xml");
+ if (m_exif_xmp_compressed_flag) {
+ xmp_infe_box->set_content_encoding("deflate");
+ }
file->add_infe_box(7, xmp_infe_box);
}
diff --git a/libheif/mini.h b/libheif/mini.h
index 2358246a..0bbad99b 100644
--- a/libheif/mini.h
+++ b/libheif/mini.h
@@ -51,6 +51,8 @@ public:
std::vector<uint8_t> get_main_item_codec_config() const { return m_main_item_codec_config; }
std::vector<uint8_t> get_alpha_item_codec_config() const { return m_alpha_item_codec_config; }
+ std::vector<uint8_t> get_gainmap_item_codec_config() const { return m_gainmap_item_codec_config; }
+
std::vector<uint8_t> get_icc_data() const { return m_icc_data; }
uint64_t get_main_item_data_offset() const { return m_main_item_data_offset; }
@@ -58,9 +60,9 @@ public:
uint64_t get_alpha_item_data_offset() const { return m_alpha_item_data_offset; }
uint32_t get_alpha_item_data_size() const { return m_alpha_item_data_size; }
uint64_t get_exif_item_data_offset() const { return m_exif_item_data_offset; }
- uint32_t get_exif_item_data_size() const { return m_exif_item_data_size; }
+ uint32_t get_exif_item_data_size() const { return m_exif_data_size; }
uint64_t get_xmp_item_data_offset() const { return m_xmp_item_data_offset; }
- uint32_t get_xmp_item_data_size() const { return m_xmp_item_data_size; }
+ uint32_t get_xmp_item_data_size() const { return m_xmp_data_size; }
uint16_t get_colour_primaries() const { return m_colour_primaries; }
uint16_t get_transfer_characteristics() const { return m_transfer_characteristics; }
@@ -83,14 +85,15 @@ private:
bool m_icc_flag = false;
bool m_exif_flag = false;
bool m_xmp_flag = false;
+ bool m_exif_xmp_compressed_flag = false; // when enabled, data is compressed with 'deflate'
uint8_t m_chroma_subsampling = 0;
uint8_t m_orientation = 0;
uint32_t m_width = 0;
uint32_t m_height = 0;
uint8_t m_bit_depth = 8;
- bool m_chroma_is_horizontally_centred = false;
- bool m_chroma_is_vertically_centred = false;
+ bool m_chroma_is_horizontally_centered = false;
+ bool m_chroma_is_vertically_centered = false;
bool m_alpha_is_premultiplied = false;
uint16_t m_colour_primaries = 0;
uint16_t m_transfer_characteristics = 0;
@@ -149,9 +152,9 @@ private:
uint64_t m_gainmap_item_data_offset = 0;
uint32_t m_gainmap_item_data_size = 0;
uint64_t m_exif_item_data_offset = 0;
- uint32_t m_exif_item_data_size = 0;
+ uint32_t m_exif_data_size = 0;
uint64_t m_xmp_item_data_offset = 0;
- uint32_t m_xmp_item_data_size = 0;
+ uint32_t m_xmp_data_size = 0;
};
#endif