Commit be4374aa for libheif

commit be4374aafdf3380f20c30f26e39bbee5eeb7164f
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Feb 12 20:34:45 2026 +0100

    unci: decoding of multi-component images

diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index 82eb29ad..c522cab4 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -195,6 +195,26 @@ bool map_uncompressed_component_to_channel(const std::shared_ptr<const Box_cmpd>
 }


+heif_channel_datatype unc_component_format_to_datatype(uint8_t format)
+{
+  switch (format) {
+    case component_format_unsigned:
+      return heif_channel_datatype_unsigned_integer;
+
+    case component_format_signed:
+      return heif_channel_datatype_signed_integer;
+
+    case component_format_float:
+      return heif_channel_datatype_floating_point;
+
+    case component_format_complex:
+      return heif_channel_datatype_complex_number;
+
+    default:
+      return heif_channel_datatype_undefined;
+  }
+}
+

 Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(const std::shared_ptr<const Box_cmpd> cmpd,
                                                                              const std::shared_ptr<const Box_uncC> uncC,
@@ -202,6 +222,8 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con
                                                                              uint32_t height,
                                                                              const heif_security_limits* limits)
 {
+  const auto& components = cmpd->get_components();
+
   auto img = std::make_shared<HeifPixelImage>();
   heif_chroma chroma = heif_chroma_undefined;
   heif_colorspace colourspace = heif_colorspace_undefined;
@@ -215,24 +237,37 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con
               chroma);

   for (Box_uncC::Component component : uncC->get_components()) {
-    heif_channel channel;
-    if (map_uncompressed_component_to_channel(cmpd, component, &channel)) {
-      if (img->has_channel(channel)) {
-        return Error{heif_error_Unsupported_feature,
-                     heif_suberror_Unspecified,
-                     "Cannot generate image with several similar heif_channels."};
-      }
+    if (component.component_index >= components.size()) {
+      return Error{
+        heif_error_Invalid_input,
+        heif_suberror_Unspecified,
+        "Component index out of range."
+      };
+    }

-      if ((channel == heif_channel_Cb) || (channel == heif_channel_Cr)) {
-        if (auto err = img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth,
-                                      limits)) {
-          return err;
-        }
+    auto component_type = components[component.component_index].component_type;
+
+    if ((component_type == heif_uncompressed_component_type::component_type_Cb) ||
+        (component_type == heif_uncompressed_component_type::component_type_Cr)) {
+      Result<uint32_t> result = img->add_component((width / chroma_h_subsampling(chroma)),
+                                                   (height / chroma_v_subsampling(chroma)),
+                                                   component_type,
+                                                   unc_component_format_to_datatype(component.component_format),
+                                                   component.component_bit_depth,
+                                                   limits);
+      if (result.is_error()) {
+        return result.error();
       }
-      else {
-        if (auto err = img->add_plane(channel, width, height, component.component_bit_depth, limits)) {
-          return err;
-        }
+    }
+    else {
+      Result<uint32_t> result = img->add_component(width,
+                                                   height,
+                                                   component_type,
+                                                   unc_component_format_to_datatype(component.component_format),
+                                                   component.component_bit_depth,
+                                                   limits);
+      if (result.is_error()) {
+        return result.error();
       }
     }
   }
diff --git a/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
index d38a7dcb..daf5696b 100644
--- a/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
@@ -84,10 +84,9 @@ Error unc_decoder_bytealign_component_interleave::decode_tile(const std::vector<
     const auto& c = components[i];
     comp[i].bytes_per_sample = (c.component_bit_depth + 7) / 8;

-    heif_channel channel;
-    comp[i].use = map_uncompressed_component_to_channel(m_cmpd, c, &channel);
+    comp[i].use = true; // map_uncompressed_component_to_channel(m_cmpd, c, &channel);
     if (comp[i].use) {
-      comp[i].dst_plane = img->get_plane(channel, &comp[i].dst_plane_stride);
+      comp[i].dst_plane = img->get_component(i, &comp[i].dst_plane_stride);
     }
     else {
       comp[i].dst_plane = nullptr;
@@ -152,7 +151,7 @@ Error unc_decoder_bytealign_component_interleave::decode_tile(const std::vector<
             }
             std::memcpy(dst, &value, 4);
           }
-          else {
+          else if (comp[c].bytes_per_sample == 8) {
             // 8-byte sample
             uint64_t value;
             if (little_endian) {
@@ -177,6 +176,50 @@ Error unc_decoder_bytealign_component_interleave::decode_tile(const std::vector<
             }
             std::memcpy(dst, &value, 8);
           }
+          else if (comp[c].bytes_per_sample == 16) {
+            // 16-byte sample (2* 8 complex)
+            uint64_t value[2];
+            if (little_endian) {
+              value[0] = static_cast<uint64_t>(src[0])
+                         | (static_cast<uint64_t>(src[1]) << 8)
+                         | (static_cast<uint64_t>(src[2]) << 16)
+                         | (static_cast<uint64_t>(src[3]) << 24)
+                         | (static_cast<uint64_t>(src[4]) << 32)
+                         | (static_cast<uint64_t>(src[5]) << 40)
+                         | (static_cast<uint64_t>(src[6]) << 48)
+                         | (static_cast<uint64_t>(src[7]) << 56);
+              value[1] = static_cast<uint64_t>(src[8])
+                         | (static_cast<uint64_t>(src[9]) << 8)
+                         | (static_cast<uint64_t>(src[10]) << 16)
+                         | (static_cast<uint64_t>(src[11]) << 24)
+                         | (static_cast<uint64_t>(src[12]) << 32)
+                         | (static_cast<uint64_t>(src[13]) << 40)
+                         | (static_cast<uint64_t>(src[14]) << 48)
+                         | (static_cast<uint64_t>(src[15]) << 56);
+            }
+            else {
+              value[0] = (static_cast<uint64_t>(src[0]) << 56)
+                         | (static_cast<uint64_t>(src[1]) << 48)
+                         | (static_cast<uint64_t>(src[2]) << 40)
+                         | (static_cast<uint64_t>(src[3]) << 32)
+                         | (static_cast<uint64_t>(src[4]) << 24)
+                         | (static_cast<uint64_t>(src[5]) << 16)
+                         | (static_cast<uint64_t>(src[6]) << 8)
+                         | static_cast<uint64_t>(src[7]);
+              value[1] = (static_cast<uint64_t>(src[8]) << 56)
+                         | (static_cast<uint64_t>(src[9]) << 48)
+                         | (static_cast<uint64_t>(src[10]) << 40)
+                         | (static_cast<uint64_t>(src[11]) << 32)
+                         | (static_cast<uint64_t>(src[12]) << 24)
+                         | (static_cast<uint64_t>(src[13]) << 16)
+                         | (static_cast<uint64_t>(src[14]) << 8)
+                         | static_cast<uint64_t>(src[15]);
+            }
+            std::memcpy(dst, &value, 2*8);
+          }
+          else {
+            assert(false);
+          }
         }

         src += aligned_bytes_per_sample;
@@ -213,10 +256,11 @@ bool unc_decoder_factory_bytealign_component_interleave::can_decode(const std::s

   for (const auto& component : uncC->get_components()) {
     uint32_t d = component.component_bit_depth;
-    if (d != 8 && d != 16 && d != 32 && d != 64) {
+    if (d != 8 && d != 16 && d != 32 && d != 64 && d != 128) {
       return false;
     }
-    if (component.component_format != component_format_unsigned) {
+
+    if (d == 128 && component.component_format != heif_uncompressed_component_format::component_format_complex) {
       return false;
     }
   }
diff --git a/libheif/codecs/uncompressed/unc_types.h b/libheif/codecs/uncompressed/unc_types.h
index 4406a716..8213051b 100644
--- a/libheif/codecs/uncompressed/unc_types.h
+++ b/libheif/codecs/uncompressed/unc_types.h
@@ -132,7 +132,7 @@ enum heif_uncompressed_component_type : uint16_t
  *
  * See ISO/IEC 23001-17 Table 2.
  */
-enum heif_uncompressed_component_format
+enum heif_uncompressed_component_format : uint8_t
 {
   /**
    * Unsigned integer.