Commit 357ead38 for libheif

commit 357ead38957cbe85a34c09b8a286a0e2345b36ac
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Feb 8 23:58:51 2026 +0100

    unci: remove uncompressed_image_type_is_supported() and let each unc_encoder check its own requirements

diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index aeb6e309..194b480b 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -31,9 +31,10 @@
 #include "codecs/decoder.h"

 #include <algorithm>
-#include <map>
-#include <iostream>
 #include <cassert>
+#include <iostream>
+#include <map>
+#include <sstream>
 #include "security_limits.h"


@@ -43,198 +44,6 @@ bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr<const
 }


-static Error uncompressed_image_type_is_supported(const std::shared_ptr<const Box_uncC>& uncC,
-                                                  const std::shared_ptr<const Box_cmpd>& cmpd)
-{
-  if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) {
-    return Error::Ok;
-  }
-  if (!cmpd) {
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 "Missing required cmpd box (no match in uncC box) for uncompressed codec");
-  }
-
-  for (Box_uncC::Component component : uncC->get_components()) {
-    uint16_t component_index = component.component_index;
-    uint16_t component_type = cmpd->get_components()[component_index].component_type;
-    if ((component_type > 7) && (component_type != component_type_padded) && (component_type != component_type_filter_array)) {
-      std::stringstream sstr;
-      sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet";
-      return Error(heif_error_Unsupported_feature,
-                   heif_suberror_Unsupported_data_version,
-                   sstr.str());
-    }
-
-    if ((component.component_bit_depth > 16)) {
-      std::stringstream sstr;
-      sstr << "Uncompressed image with component_bit_depth " << ((int) component.component_bit_depth) << " is not implemented yet";
-      return Error(heif_error_Unsupported_feature,
-                   heif_suberror_Unsupported_data_version,
-                   sstr.str());
-    }
-    if (component.component_format != component_format_unsigned) {
-      std::stringstream sstr;
-      sstr << "Uncompressed image with component_format " << ((int) component.component_format) << " is not implemented yet";
-      return Error(heif_error_Unsupported_feature,
-                   heif_suberror_Unsupported_data_version,
-                   sstr.str());
-    }
-    if (component.component_align_size > 2) {
-      std::stringstream sstr;
-      sstr << "Uncompressed image with component_align_size " << ((int) component.component_align_size) << " is not implemented yet";
-      return Error(heif_error_Unsupported_feature,
-                   heif_suberror_Unsupported_data_version,
-                   sstr.str());
-    }
-  }
-  if ((uncC->get_sampling_type() != sampling_mode_no_subsampling)
-      && (uncC->get_sampling_type() != sampling_mode_422)
-      && (uncC->get_sampling_type() != sampling_mode_420)
-      ) {
-    std::stringstream sstr;
-    sstr << "Uncompressed sampling_type of " << ((int) uncC->get_sampling_type()) << " is not implemented yet";
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 sstr.str());
-  }
-  if ((uncC->get_interleave_type() != interleave_mode_component)
-      && (uncC->get_interleave_type() != interleave_mode_pixel)
-      && (uncC->get_interleave_type() != interleave_mode_mixed)
-      && (uncC->get_interleave_type() != interleave_mode_row)
-      && (uncC->get_interleave_type() != interleave_mode_tile_component)
-      ) {
-    std::stringstream sstr;
-    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 sstr.str());
-  }
-  // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.3
-  if (uncC->get_sampling_type() == sampling_mode_422) {
-    // We check Y Cb and Cr appear in the chroma test
-    // TODO: error for tile width not multiple of 2
-    if ((uncC->get_interleave_type() != interleave_mode_component)
-        && (uncC->get_interleave_type() != interleave_mode_mixed)
-        && (uncC->get_interleave_type() != interleave_mode_multi_y)) {
-      std::stringstream sstr;
-      sstr << "YCbCr 4:2:2 subsampling is only valid with component, mixed or multi-Y interleave mode (ISO/IEC 23001-17 5.2.1.5.3).";
-      return Error(heif_error_Invalid_input,
-                   heif_suberror_Invalid_parameter_value,
-                   sstr.str());
-    }
-    if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) {
-      if (uncC->get_row_align_size() % 2 != 0) {
-        std::stringstream sstr;
-        sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3).";
-        return Error(heif_error_Invalid_input,
-                     heif_suberror_Invalid_parameter_value,
-                     sstr.str());
-      }
-    }
-    if (uncC->get_tile_align_size() != 0) {
-      if (uncC->get_tile_align_size() % 2 != 0) {
-        std::stringstream sstr;
-        sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3).";
-        return Error(heif_error_Invalid_input,
-                     heif_suberror_Invalid_parameter_value,
-                     sstr.str());
-      }
-    }
-  }
-  // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.4
-  if (uncC->get_sampling_type() == sampling_mode_422) {
-    // We check Y Cb and Cr appear in the chroma test
-    // TODO: error for tile width not multiple of 2
-    if ((uncC->get_interleave_type() != interleave_mode_component)
-        && (uncC->get_interleave_type() != interleave_mode_mixed)) {
-      std::stringstream sstr;
-      sstr << "YCbCr 4:2:0 subsampling is only valid with component or mixed interleave mode (ISO/IEC 23001-17 5.2.1.5.4).";
-      return Error(heif_error_Invalid_input,
-                   heif_suberror_Invalid_parameter_value,
-                   sstr.str());
-    }
-    if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) {
-      if (uncC->get_row_align_size() % 2 != 0) {
-        std::stringstream sstr;
-        sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.4).";
-        return Error(heif_error_Invalid_input,
-                     heif_suberror_Invalid_parameter_value,
-                     sstr.str());
-      }
-    }
-    if (uncC->get_tile_align_size() != 0) {
-      if (uncC->get_tile_align_size() % 4 != 0) {
-        std::stringstream sstr;
-        sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 4 (ISO/IEC 23001-17 5.2.1.5.3).";
-        return Error(heif_error_Invalid_input,
-                     heif_suberror_Invalid_parameter_value,
-                     sstr.str());
-      }
-    }
-  }
-  if ((uncC->get_interleave_type() == interleave_mode_mixed) && (uncC->get_sampling_type() == sampling_mode_no_subsampling)) {
-    std::stringstream sstr;
-    sstr << "Interleave interleave mode is not valid with subsampling mode (ISO/IEC 23001-17 5.2.1.6.4).";
-    return Error(heif_error_Invalid_input,
-                 heif_suberror_Invalid_parameter_value,
-                 sstr.str());
-  }
-  if ((uncC->get_interleave_type() == interleave_mode_multi_y)
-      && ((uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_411))) {
-    std::stringstream sstr;
-    sstr << "Multi-Y interleave mode is only valid with 4:2:2 and 4:1:1 subsampling modes (ISO/IEC 23001-17 5.2.1.6.7).";
-    return Error(heif_error_Invalid_input,
-                 heif_suberror_Invalid_parameter_value,
-                 sstr.str());
-  }
-  // TODO: throw error if mixed and Cb and Cr are not adjacent.
-
-  if (uncC->get_block_size() != 0) {
-    std::stringstream sstr;
-    sstr << "Uncompressed block_size of " << ((int) uncC->get_block_size()) << " is not implemented yet";
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 sstr.str());
-  }
-
-  if (uncC->is_components_little_endian()) {
-    const auto& comps = uncC->get_components();
-    bool all_8_bit = std::all_of(comps.begin(), comps.end(),
-                                 [](const Box_uncC::Component& c) { return c.component_bit_depth==8; });
-    if (!all_8_bit) {
-      return Error(heif_error_Unsupported_feature,
-                   heif_suberror_Unsupported_data_version,
-                   "Uncompressed components_little_endian == 1 is not implemented yet");
-    }
-  }
-
-  if (uncC->is_block_pad_lsb()) {
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 "Uncompressed block_pad_lsb == 1 is not implemented yet");
-  }
-  if (uncC->is_block_little_endian()) {
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 "Uncompressed block_little_endian == 1 is not implemented yet");
-  }
-  if (uncC->is_block_reversed()) {
-    return Error(heif_error_Unsupported_feature,
-                 heif_suberror_Unsupported_data_version,
-                 "Uncompressed block_reversed == 1 is not implemented yet");
-  }
-  if ((uncC->get_pixel_size() != 0) && ((uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_multi_y))) {
-    std::stringstream sstr;
-    sstr << "Uncompressed pixel_size of " << ((int) uncC->get_pixel_size()) << " is only valid with interleave_type 1 or 5 (ISO/IEC 23001-17 5.2.1.7)";
-    return Error(heif_error_Invalid_input,
-                 heif_suberror_Invalid_parameter_value,
-                 sstr.str());
-  }
-  return Error::Ok;
-}
-
-
 Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr<const Box_uncC>& uncC,
                                                            const std::shared_ptr<const Box_cmpd>& cmpd,
                                                            heif_chroma* out_chroma, heif_colorspace* out_colourspace,
@@ -567,6 +376,15 @@ Error UncompressedImageCodec::check_header_validity(std::optional<const std::sha
                 heif_suberror_Unspecified,
                 "Invalid component index in uncC box"};
       }
+
+      uint16_t component_type = cmpd->get_components()[comp.component_index].component_type;
+      if (component_type > 7 && component_type != component_type_padded && component_type != component_type_filter_array) {
+        std::stringstream sstr;
+        sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet";
+        return {heif_error_Unsupported_feature,
+                heif_suberror_Unsupported_data_version,
+                sstr.str()};
+      }
     }
   }

@@ -628,13 +446,6 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte
     return error;
   }

-  // check if we support the type of image
-
-  error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO
-  if (error) {
-    return error;
-  }
-
   assert(ispe);
   uint32_t width = ispe->get_width();
   uint32_t height = ispe->get_height();
@@ -698,13 +509,6 @@ UncompressedImageCodec::decode_uncompressed_image(const UncompressedImageCodec::
     return error;
   }

-  // check if we support the type of image
-
-  error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO
-  if (error) {
-    return error;
-  }
-
   assert(ispe);
   uint32_t width = ispe->get_width();
   uint32_t height = ispe->get_height();
diff --git a/libheif/codecs/uncompressed/unc_decoder.cc b/libheif/codecs/uncompressed/unc_decoder.cc
index 9b1096c9..4b72e0d4 100644
--- a/libheif/codecs/uncompressed/unc_decoder.cc
+++ b/libheif/codecs/uncompressed/unc_decoder.cc
@@ -18,6 +18,7 @@
  * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
  */

+#include <algorithm>
 #include <cassert>
 #include <sstream>

@@ -289,6 +290,50 @@ Error unc_decoder::decode_image(const DataExtent& extent,

 // --- unc_decoder_factory ---

+bool unc_decoder_factory::check_common_requirements(const std::shared_ptr<const Box_uncC>& uncC)
+{
+  if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) {
+    return true;
+  }
+
+  for (const auto& component : uncC->get_components()) {
+    if (component.component_bit_depth > 16) {
+      return false;
+    }
+    if (component.component_format != component_format_unsigned) {
+      return false;
+    }
+    if (component.component_align_size > 2) {
+      return false;
+    }
+  }
+
+  if (uncC->get_block_size() != 0) {
+    return false;
+  }
+  if (uncC->is_block_pad_lsb()) {
+    return false;
+  }
+  if (uncC->is_block_little_endian()) {
+    return false;
+  }
+  if (uncC->is_block_reversed()) {
+    return false;
+  }
+
+  if (uncC->is_components_little_endian()) {
+    const auto& comps = uncC->get_components();
+    bool all_8_bit = std::all_of(comps.begin(), comps.end(),
+                                 [](const Box_uncC::Component& c) { return c.component_bit_depth == 8; });
+    if (!all_8_bit) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+
 Result<std::unique_ptr<unc_decoder>> unc_decoder_factory::get_unc_decoder(
     uint32_t width, uint32_t height,
     const std::shared_ptr<const Box_cmpd>& cmpd,
diff --git a/libheif/codecs/uncompressed/unc_decoder.h b/libheif/codecs/uncompressed/unc_decoder.h
index 1c304eea..895f53ef 100644
--- a/libheif/codecs/uncompressed/unc_decoder.h
+++ b/libheif/codecs/uncompressed/unc_decoder.h
@@ -95,7 +95,9 @@ public:
       const std::shared_ptr<const Box_cmpd>& cmpd,
       const std::shared_ptr<const Box_uncC>& uncC);

-private:
+protected:
+  static bool check_common_requirements(const std::shared_ptr<const Box_uncC>& uncC);
+
   virtual bool can_decode(const std::shared_ptr<const Box_uncC>& uncC) const = 0;

   virtual std::unique_ptr<unc_decoder> create(
diff --git a/libheif/codecs/uncompressed/unc_decoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_component_interleave.cc
index 4ed1a4e7..7faf0244 100644
--- a/libheif/codecs/uncompressed/unc_decoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_component_interleave.cc
@@ -120,8 +120,45 @@ Error unc_decoder_component_interleave::decode_tile(const std::vector<uint8_t>&

 bool unc_decoder_factory_component_interleave::can_decode(const std::shared_ptr<const Box_uncC>& uncC) const
 {
-  return uncC->get_interleave_type() == interleave_mode_component ||
-         uncC->get_interleave_type() == interleave_mode_tile_component;
+  if (!check_common_requirements(uncC)) {
+    return false;
+  }
+
+  if (uncC->get_interleave_type() != interleave_mode_component &&
+      uncC->get_interleave_type() != interleave_mode_tile_component) {
+    return false;
+  }
+
+  auto sampling = uncC->get_sampling_type();
+  if (sampling != sampling_mode_no_subsampling &&
+      sampling != sampling_mode_422 &&
+      sampling != sampling_mode_420) {
+    return false;
+  }
+
+  if (sampling == sampling_mode_422 || sampling == sampling_mode_420) {
+    if (uncC->get_row_align_size() != 0 && uncC->get_row_align_size() % 2 != 0) {
+      return false;
+    }
+  }
+
+  if (sampling == sampling_mode_422) {
+    if (uncC->get_tile_align_size() != 0 && uncC->get_tile_align_size() % 2 != 0) {
+      return false;
+    }
+  }
+
+  if (sampling == sampling_mode_420) {
+    if (uncC->get_tile_align_size() != 0 && uncC->get_tile_align_size() % 4 != 0) {
+      return false;
+    }
+  }
+
+  if (uncC->get_pixel_size() != 0) {
+    return false;
+  }
+
+  return true;
 }

 std::unique_ptr<unc_decoder> unc_decoder_factory_component_interleave::create(
diff --git a/libheif/codecs/uncompressed/unc_decoder_mixed_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_mixed_interleave.cc
index 969e7b9a..81b1e8b2 100644
--- a/libheif/codecs/uncompressed/unc_decoder_mixed_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_mixed_interleave.cc
@@ -115,7 +115,36 @@ void unc_decoder_mixed_interleave::processTile(UncompressedBitReader& srcBits, u

 bool unc_decoder_factory_mixed_interleave::can_decode(const std::shared_ptr<const Box_uncC>& uncC) const
 {
-  return uncC->get_interleave_type() == interleave_mode_mixed;
+  if (!check_common_requirements(uncC)) {
+    return false;
+  }
+
+  if (uncC->get_interleave_type() != interleave_mode_mixed) {
+    return false;
+  }
+
+  auto sampling = uncC->get_sampling_type();
+  if (sampling != sampling_mode_422 && sampling != sampling_mode_420) {
+    return false;
+  }
+
+  if (sampling == sampling_mode_422) {
+    if (uncC->get_tile_align_size() != 0 && uncC->get_tile_align_size() % 2 != 0) {
+      return false;
+    }
+  }
+
+  if (sampling == sampling_mode_420) {
+    if (uncC->get_tile_align_size() != 0 && uncC->get_tile_align_size() % 4 != 0) {
+      return false;
+    }
+  }
+
+  if (uncC->get_pixel_size() != 0) {
+    return false;
+  }
+
+  return true;
 }

 std::unique_ptr<unc_decoder> unc_decoder_factory_mixed_interleave::create(
diff --git a/libheif/codecs/uncompressed/unc_decoder_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_pixel_interleave.cc
index 87566b98..2b1ed74c 100644
--- a/libheif/codecs/uncompressed/unc_decoder_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_pixel_interleave.cc
@@ -111,7 +111,19 @@ Error unc_decoder_pixel_interleave::processTile(UncompressedBitReader& srcBits,

 bool unc_decoder_factory_pixel_interleave::can_decode(const std::shared_ptr<const Box_uncC>& uncC) const
 {
-  return uncC->get_interleave_type() == interleave_mode_pixel;
+  if (!check_common_requirements(uncC)) {
+    return false;
+  }
+
+  if (uncC->get_interleave_type() != interleave_mode_pixel) {
+    return false;
+  }
+
+  if (uncC->get_sampling_type() != sampling_mode_no_subsampling) {
+    return false;
+  }
+
+  return true;
 }

 std::unique_ptr<unc_decoder> unc_decoder_factory_pixel_interleave::create(
diff --git a/libheif/codecs/uncompressed/unc_decoder_row_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_row_interleave.cc
index fafa2b08..3136209f 100644
--- a/libheif/codecs/uncompressed/unc_decoder_row_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_row_interleave.cc
@@ -98,7 +98,23 @@ void unc_decoder_row_interleave::processTile(UncompressedBitReader& srcBits, uin

 bool unc_decoder_factory_row_interleave::can_decode(const std::shared_ptr<const Box_uncC>& uncC) const
 {
-  return uncC->get_interleave_type() == interleave_mode_row;
+  if (!check_common_requirements(uncC)) {
+    return false;
+  }
+
+  if (uncC->get_interleave_type() != interleave_mode_row) {
+    return false;
+  }
+
+  if (uncC->get_sampling_type() != sampling_mode_no_subsampling) {
+    return false;
+  }
+
+  if (uncC->get_pixel_size() != 0) {
+    return false;
+  }
+
+  return true;
 }

 std::unique_ptr<unc_decoder> unc_decoder_factory_row_interleave::create(