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(