Commit 513b1d68 for libheif
commit 513b1d6812ccd747ba82e40f4c9344389bed5174
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Tue May 26 16:37:09 2026 +0200
implement 'rref' box (#1823)
diff --git a/libheif/box.cc b/libheif/box.cc
index d36f22bf..25b8de91 100644
--- a/libheif/box.cc
+++ b/libheif/box.cc
@@ -534,6 +534,10 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result, const heif_
box = std::make_shared<Box_iref>();
break;
+ case fourcc("rref"):
+ box = std::make_shared<Box_rref>();
+ break;
+
case fourcc("hvcC"):
box = std::make_shared<Box_hvcC>();
break;
@@ -3994,6 +3998,142 @@ void Box_iref::overwrite_reference(heif_item_id from_id, uint32_t type, uint32_t
}
+Error Box_rref::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+ parse_full_box_header(range);
+
+ if (get_version() > 1) {
+ return unsupported_version_error("rref");
+ }
+
+ size_t remainingBytes = range.get_remaining_bytes();
+
+ // The number of reference types should be stored in uint(8), but the
+ // common test images C043, C044 store them as uint(32).
+ // Let us detect this case by the number of data bytes in this box.
+ bool cnt_as_uint32 = (remainingBytes > 0 && remainingBytes % 4 == 0);
+
+ uint8_t nRefTypes;
+ if (cnt_as_uint32) {
+ // fix broken C043, C044 files
+ // TODO: remove me when the files have been corrected as the above check can misfire when there is extra padding
+ nRefTypes = (uint8_t)range.read32();
+ }
+ else {
+ nRefTypes = range.read8();
+ }
+
+ for (uint8_t i = 0; i < nRefTypes; i++) {
+ uint32_t refType = range.read32();
+
+ if (range.error()) {
+ std::stringstream sstr;
+ sstr << "rref box should contain " << ((int)nRefTypes) << " reference types, but we can only read " << m_reference_types.size() << " reference types.";
+
+ return {
+ heif_error_Invalid_input,
+ heif_suberror_End_of_data,
+ sstr.str()
+ };
+ }
+
+ m_reference_types.push_back(refType);
+ }
+
+ return range.get_error();
+}
+
+
+static constexpr std::array supported_reference_types{
+ fourcc("thmb"),
+ fourcc("base"),
+ fourcc("cdsc"),
+ fourcc("dimg"),
+ fourcc("auxl"),
+ fourcc("prem")
+};
+
+
+bool Box_rref::all_reference_types_supported() const
+{
+ return std::ranges::all_of(m_reference_types, [](uint32_t t) {
+ return std::ranges::find(supported_reference_types, t) != supported_reference_types.end();
+ });
+}
+
+
+Error Box_rref::reference_types_supported_error() const
+{
+ std::vector<uint32_t> unsupported_types;
+
+ for (uint32_t refType : m_reference_types) {
+ if (std::ranges::find(supported_reference_types, refType) == supported_reference_types.end()) {
+ unsupported_types.push_back(refType);
+ }
+ }
+
+ if (unsupported_types.empty()) {
+ return Error::Ok;
+ }
+
+ std::stringstream sstr;
+
+ sstr << "Unsupported reference types: ";
+ for (size_t i = 0; i < unsupported_types.size(); i++) {
+ if (i > 0) {
+ sstr << ", ";
+ }
+ sstr << fourcc_to_string(unsupported_types[i]);
+ }
+
+ return {
+ heif_error_Unsupported_feature,
+ heif_suberror_Unspecified,
+ sstr.str()
+ };
+}
+
+
+Error Box_rref::write(StreamWriter& writer) const
+{
+ size_t box_start = reserve_box_header_space(writer);
+
+ if (m_reference_types.size() > std::numeric_limits<uint8_t>::max()) {
+ return {
+ heif_error_Usage_error,
+ heif_suberror_Unspecified,
+ "Too many rref reference types."
+ };
+ }
+
+ writer.write8(static_cast<uint8_t>(m_reference_types.size()));
+
+ for (uint32_t refType : m_reference_types) {
+ writer.write32(refType);
+ }
+
+ prepend_header(writer, box_start);
+
+ return Error::Ok;
+}
+
+
+std::string Box_rref::dump(Indent& indent) const
+{
+ std::ostringstream sstr;
+ sstr << Box::dump(indent);
+
+ sstr << indent << "reference types: ";
+ for (size_t i = 0; i < m_reference_types.size(); i++) {
+ if (i > 0) sstr << ", ";
+ sstr << fourcc_to_string(m_reference_types[i]);
+ }
+ sstr << "\n";
+
+ return sstr.str();
+}
+
+
Error Box_idat::parse(BitstreamRange& range, const heif_security_limits* limits)
{
//parse_full_box_header(range);
diff --git a/libheif/box.h b/libheif/box.h
index 14a85ca3..15d86138 100644
--- a/libheif/box.h
+++ b/libheif/box.h
@@ -1041,6 +1041,38 @@ private:
};
+class Box_rref : public FullBox
+{
+public:
+ Box_rref()
+ {
+ set_short_type(fourcc("rref"));
+ }
+
+ bool is_essential() const override { return true; }
+
+ std::string dump(Indent&) const override;
+
+ const char* debug_box_name() const override { return "Required Reference Types"; }
+
+ const std::vector<uint32_t>& get_reference_types() const { return m_reference_types; }
+
+ bool all_reference_types_supported() const;
+
+ Error reference_types_supported_error() const;
+
+ void add_reference_type(uint32_t type) { m_reference_types.push_back(type); }
+
+protected:
+ Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+ Error write(StreamWriter& writer) const override;
+
+private:
+ std::vector<uint32_t> m_reference_types;
+};
+
+
class Box_idat : public Box
{
public:
diff --git a/libheif/context.cc b/libheif/context.cc
index 37c9e30a..037f84b6 100644
--- a/libheif/context.cc
+++ b/libheif/context.cc
@@ -679,6 +679,16 @@ Error HeifContext::interpret_heif_file_images()
}
+ // --- Are there any `rref` reference types that we do not process
+
+ auto rrefBox = m_heif_file->get_property_for_item<Box_rref>(pair.first);
+ if (rrefBox) {
+ if (Error err = rrefBox->reference_types_supported_error()) {
+ return err;
+ }
+ }
+
+
// --- Are there any parse errors in optional properties? Attach the errors as warnings to the images.
bool ignore_nonfatal_parse_errors = false; // TODO: this should be a user option. Where should we put this (heif_decoding_options, or while creating the context) ?