Commit 8b62c508 for libheif
commit 8b62c5088a7ad02b81682e97dfbfbcc8fbca2a0f
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Feb 20 01:16:52 2026 +0100
unci: support multi-component images in encoder_component_interleave
diff --git a/libheif/codecs/uncompressed/unc_decoder_legacybase.cc b/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
index 37fec032..b28d9f9a 100644
--- a/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
@@ -58,9 +58,11 @@ void unc_decoder_legacybase::ensureChannelList(std::shared_ptr<HeifPixelImage>&
void unc_decoder_legacybase::buildChannelList(std::shared_ptr<HeifPixelImage>& img)
{
+ uint32_t idx = 0;
for (Box_uncC::Component component : m_uncC->get_components()) {
- ChannelListEntry entry = buildChannelListEntry(component, img);
+ ChannelListEntry entry = buildChannelListEntry(idx, component, img);
channelList.push_back(entry);
+ idx++;
}
}
@@ -134,12 +136,13 @@ void unc_decoder_legacybase::processComponentTileRow(ChannelListEntry& entry, Un
}
-unc_decoder_legacybase::ChannelListEntry unc_decoder_legacybase::buildChannelListEntry(Box_uncC::Component component,
+unc_decoder_legacybase::ChannelListEntry unc_decoder_legacybase::buildChannelListEntry(uint32_t component_idx,
+ Box_uncC::Component component,
std::shared_ptr<HeifPixelImage>& img)
{
ChannelListEntry entry;
entry.use_channel = map_uncompressed_component_to_channel(m_cmpd, component, &(entry.channel));
- entry.dst_plane = img->get_plane(entry.channel, &(entry.dst_plane_stride));
+ entry.dst_plane = img->get_component(component_idx, &(entry.dst_plane_stride));
entry.tile_width = m_tile_width;
entry.tile_height = m_tile_height;
entry.other_chroma_dst_plane_stride = 0; // will be overwritten below if used
diff --git a/libheif/codecs/uncompressed/unc_decoder_legacybase.h b/libheif/codecs/uncompressed/unc_decoder_legacybase.h
index db88f064..79b93fcc 100644
--- a/libheif/codecs/uncompressed/unc_decoder_legacybase.h
+++ b/libheif/codecs/uncompressed/unc_decoder_legacybase.h
@@ -198,7 +198,7 @@ protected:
private:
void buildChannelList(std::shared_ptr<HeifPixelImage>& img);
- ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr<HeifPixelImage>& img);
+ ChannelListEntry buildChannelListEntry(uint32_t component_idx, Box_uncC::Component component, std::shared_ptr<HeifPixelImage>& img);
};
#endif
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index 36aebf3f..ae5837bd 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -27,22 +27,17 @@
bool unc_encoder_factory_component_interleave::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
+ const heif_encoding_options& options) const
{
if (image->has_channel(heif_channel_interleaved)) {
return false;
}
// Check if any component has non-byte-aligned bpp
- for (auto channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr,
- heif_channel_R, heif_channel_G, heif_channel_B,
- heif_channel_Alpha, heif_channel_filter_array,
- heif_channel_depth, heif_channel_disparity}) {
- if (image->has_channel(channel)) {
- int bpp = image->get_bits_per_pixel(channel);
- if (bpp % 8 != 0) {
- return true;
- }
+ uint32_t n = image->get_number_of_components();
+ for (uint32_t i = 0; i < n; i++) {
+ if (image->get_component_bits_per_pixel(i) % 8 != 0) {
+ return true;
}
}
@@ -51,48 +46,38 @@ bool unc_encoder_factory_component_interleave::can_encode(const std::shared_ptr<
std::unique_ptr<const unc_encoder> unc_encoder_factory_component_interleave::create(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
+ const heif_encoding_options& options) const
{
return std::make_unique<unc_encoder_component_interleave>(image, options);
}
-void unc_encoder_component_interleave::add_channel_if_exists(const std::shared_ptr<const HeifPixelImage>& image, heif_channel channel)
+unc_encoder_component_interleave::unc_encoder_component_interleave(const std::shared_ptr<const HeifPixelImage>& image,
+ const heif_encoding_options& options)
{
- if (image->has_channel(channel)) {
- uint8_t bpp = image->get_bits_per_pixel(channel);
- m_components.push_back({channel, heif_channel_to_component_type(channel), bpp});
- }
-}
+ bool is_nonvisual = (image->get_colorspace() == heif_colorspace_nonvisual);
+ uint32_t num_components = image->get_number_of_components();
+ for (uint32_t idx = 0; idx < num_components; idx++) {
+ heif_uncompressed_component_type comp_type;
+ heif_channel ch = heif_channel_Y; // default for nonvisual
-unc_encoder_component_interleave::unc_encoder_component_interleave(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options)
-{
- // Special case for heif_channel_Y:
- // - if this is a YCbCr image, use component_type_Y,
- // - otherwise, use component_type_monochrome
-
- if (image->has_channel(heif_channel_Y)) {
- uint8_t bpp = image->get_bits_per_pixel(heif_channel_Y);
- if (image->has_channel(heif_channel_Cb) && image->has_channel(heif_channel_Cr)) {
- m_components.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_Y, bpp});
+ if (is_nonvisual) {
+ comp_type = static_cast<heif_uncompressed_component_type>(image->get_component_type(idx));
}
else {
- m_components.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_monochrome, bpp});
+ ch = image->get_component_channel(idx);
+ if (ch == heif_channel_Y && !image->has_channel(heif_channel_Cb)) {
+ comp_type = component_type_monochrome;
+ }
+ else {
+ comp_type = heif_channel_to_component_type(ch);
+ }
}
- }
-
- add_channel_if_exists(image, heif_channel_Cb);
- add_channel_if_exists(image, heif_channel_Cr);
- add_channel_if_exists(image, heif_channel_R);
- add_channel_if_exists(image, heif_channel_G);
- add_channel_if_exists(image, heif_channel_B);
- add_channel_if_exists(image, heif_channel_Alpha);
- add_channel_if_exists(image, heif_channel_filter_array);
- add_channel_if_exists(image, heif_channel_depth);
- add_channel_if_exists(image, heif_channel_disparity);
+ uint8_t bpp = image->get_component_bits_per_pixel(idx);
+ m_components.push_back({idx, ch, comp_type, bpp});
+ }
uint16_t index = 0;
for (const auto& comp : m_components) {
@@ -149,12 +134,12 @@ std::vector<uint8_t> unc_encoder_component_interleave::encode_tile(const std::sh
data.reserve(total_size);
for (const auto& comp : m_components) {
- uint32_t plane_width = src_image->get_width(comp.channel);
- uint32_t plane_height = src_image->get_height(comp.channel);
+ uint32_t plane_width = src_image->get_component_width(comp.component_idx);
+ uint32_t plane_height = src_image->get_component_height(comp.component_idx);
uint8_t bpp = comp.bpp;
size_t src_stride;
- const uint8_t* src_data = src_image->get_plane(comp.channel, &src_stride);
+ const uint8_t* src_data = src_image->get_component(comp.component_idx, &src_stride);
for (uint32_t y = 0; y < plane_height; y++) {
const uint8_t* row = src_data + src_stride * y;
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
index 8ee45e1b..6e837090 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
@@ -40,14 +40,13 @@ public:
private:
struct channel_component
{
+ uint32_t component_idx;
heif_channel channel;
heif_uncompressed_component_type component_type;
uint8_t bpp;
};
std::vector<channel_component> m_components;
-
- void add_channel_if_exists(const std::shared_ptr<const HeifPixelImage>& image, heif_channel channel);
};
diff --git a/tests/uncompressed_encode_multicomponent.cc b/tests/uncompressed_encode_multicomponent.cc
index 02c3c95c..96bb8105 100644
--- a/tests/uncompressed_encode_multicomponent.cc
+++ b/tests/uncompressed_encode_multicomponent.cc
@@ -388,6 +388,11 @@ TEST_CASE("Multi-mono uint16")
test_multi_mono<uint16_t>(heif_channel_datatype_unsigned_integer, 16, "multi_mono_uint16.heif");
}
+TEST_CASE("Multi-mono uint12")
+{
+ test_multi_mono<uint16_t>(heif_channel_datatype_unsigned_integer, 12, "multi_mono_uint12.heif");
+}
+
TEST_CASE("Multi-mono uint32")
{
test_multi_mono<uint32_t>(heif_channel_datatype_unsigned_integer, 32, "multi_mono_uint32.heif");