Commit ba8319ec for libheif

commit ba8319ecb0e61a5d70d26c4337260b65990909df
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Tue Feb 10 19:47:37 2026 +0100

    add heif_image_add_component() API and multi-component test

diff --git a/libheif/api/libheif/heif_experimental.cc b/libheif/api/libheif/heif_experimental.cc
index 608cdf63..2b27a91b 100644
--- a/libheif/api/libheif/heif_experimental.cc
+++ b/libheif/api/libheif/heif_experimental.cc
@@ -431,6 +431,7 @@ type* heif_image_get_channel_ ## name (struct heif_image* image, \
 heif_image_get_channel_X(uint16, uint16_t, heif_channel_datatype_unsigned_integer, 16)
 heif_image_get_channel_X(uint32, uint32_t, heif_channel_datatype_unsigned_integer, 32)
 heif_image_get_channel_X(uint64, uint64_t, heif_channel_datatype_unsigned_integer, 64)
+heif_image_get_channel_X(int8, int8_t, heif_channel_datatype_signed_integer, 8)
 heif_image_get_channel_X(int16, int16_t, heif_channel_datatype_signed_integer, 16)
 heif_image_get_channel_X(int32, int32_t, heif_channel_datatype_signed_integer, 32)
 heif_image_get_channel_X(int64, int64_t, heif_channel_datatype_signed_integer, 64)
@@ -442,7 +443,7 @@ heif_image_get_channel_X(complex64, heif_complex64, heif_channel_datatype_comple

 // --- index-based component access

-int heif_image_get_number_of_components(const heif_image* image)
+uint32_t heif_image_get_number_of_components(const heif_image* image)
 {
   if (!image || !image->image) {
     return 0;
@@ -451,7 +452,7 @@ int heif_image_get_number_of_components(const heif_image* image)
 }


-heif_channel heif_image_get_component_channel(const heif_image* image, int component_idx)
+heif_channel heif_image_get_component_channel(const heif_image* image, uint32_t component_idx)
 {
   if (!image || !image->image) {
     return heif_channel_Y;
@@ -460,25 +461,25 @@ heif_channel heif_image_get_component_channel(const heif_image* image, int compo
 }


-int heif_image_get_component_width(const heif_image* image, int component_idx)
+uint32_t heif_image_get_component_width(const heif_image* image, uint32_t component_idx)
 {
   if (!image || !image->image) {
     return 0;
   }
-  return static_cast<int>(image->image->get_component_width(component_idx));
+  return image->image->get_component_width(component_idx);
 }


-int heif_image_get_component_height(const heif_image* image, int component_idx)
+uint32_t heif_image_get_component_height(const heif_image* image, uint32_t component_idx)
 {
   if (!image || !image->image) {
     return 0;
   }
-  return static_cast<int>(image->image->get_component_height(component_idx));
+  return image->image->get_component_height(component_idx);
 }


-int heif_image_get_component_bits_per_pixel(const heif_image* image, int component_idx)
+int heif_image_get_component_bits_per_pixel(const heif_image* image, uint32_t component_idx)
 {
   if (!image || !image->image) {
     return 0;
@@ -487,7 +488,40 @@ int heif_image_get_component_bits_per_pixel(const heif_image* image, int compone
 }


-const uint8_t* heif_image_get_component_readonly(const heif_image* image, int component_idx, size_t* out_stride)
+uint16_t heif_image_get_component_type(const heif_image* image, uint32_t component_idx)
+{
+  if (!image || !image->image) {
+    return 0;
+  }
+  return image->image->get_component_type(component_idx);
+}
+
+
+heif_error heif_image_add_component(heif_image* image,
+                                    int width, int height,
+                                    uint16_t component_type,
+                                    heif_channel_datatype datatype,
+                                    int bit_depth,
+                                    uint32_t* out_component_idx)
+{
+  if (!image || !image->image) {
+    return heif_error_null_pointer_argument;
+  }
+
+  auto result = image->image->add_component(width, height, component_type, datatype, bit_depth, nullptr);
+  if (!result) {
+    return result.error_struct(image->image.get());
+  }
+
+  if (out_component_idx) {
+    *out_component_idx = *result;
+  }
+
+  return heif_error_success;
+}
+
+
+const uint8_t* heif_image_get_component_readonly(const heif_image* image, uint32_t component_idx, size_t* out_stride)
 {
   if (!image || !image->image) {
     if (out_stride) *out_stride = 0;
@@ -497,7 +531,7 @@ const uint8_t* heif_image_get_component_readonly(const heif_image* image, int co
 }


-uint8_t* heif_image_get_component(heif_image* image, int component_idx, size_t* out_stride)
+uint8_t* heif_image_get_component(heif_image* image, uint32_t component_idx, size_t* out_stride)
 {
   if (!image || !image->image) {
     if (out_stride) *out_stride = 0;
@@ -509,7 +543,7 @@ uint8_t* heif_image_get_component(heif_image* image, int component_idx, size_t*

 #define heif_image_get_component_X(name, type) \
 const type* heif_image_get_component_ ## name ## _readonly(const struct heif_image* image, \
-                                                            int component_idx, \
+                                                            uint32_t component_idx, \
                                                             size_t* out_stride) \
 {                                                            \
   if (!image || !image->image) {                             \
@@ -520,7 +554,7 @@ const type* heif_image_get_component_ ## name ## _readonly(const struct heif_ima
 }                                                            \
                                                              \
 type* heif_image_get_component_ ## name (struct heif_image* image, \
-                                         int component_idx,       \
+                                         uint32_t component_idx,  \
                                          size_t* out_stride)      \
 {                                                            \
   if (!image || !image->image) {                             \
@@ -533,6 +567,7 @@ type* heif_image_get_component_ ## name (struct heif_image* image, \
 heif_image_get_component_X(uint16, uint16_t)
 heif_image_get_component_X(uint32, uint32_t)
 heif_image_get_component_X(uint64, uint64_t)
+heif_image_get_component_X(int8, int8_t)
 heif_image_get_component_X(int16, int16_t)
 heif_image_get_component_X(int32, int32_t)
 heif_image_get_component_X(int64, int64_t)
diff --git a/libheif/api/libheif/heif_experimental.h b/libheif/api/libheif/heif_experimental.h
index 1175dd07..f0f2cb83 100644
--- a/libheif/api/libheif/heif_experimental.h
+++ b/libheif/api/libheif/heif_experimental.h
@@ -237,6 +237,11 @@ const uint64_t* heif_image_get_channel_uint64_readonly(const heif_image*,
                                                        enum heif_channel channel,
                                                        size_t* out_stride);

+LIBHEIF_API
+const int8_t* heif_image_get_channel_int8_readonly(const heif_image*,
+                                                    enum heif_channel channel,
+                                                    size_t* out_stride);
+
 LIBHEIF_API
 const int16_t* heif_image_get_channel_int16_readonly(const heif_image*,
                                                      enum heif_channel channel,
@@ -287,6 +292,11 @@ uint64_t* heif_image_get_channel_uint64(heif_image*,
                                         enum heif_channel channel,
                                         size_t* out_stride);

+LIBHEIF_API
+int8_t* heif_image_get_channel_int8(heif_image*,
+                                     enum heif_channel channel,
+                                     size_t* out_stride);
+
 LIBHEIF_API
 int16_t* heif_image_get_channel_int16(heif_image*,
                                       enum heif_channel channel,
@@ -326,85 +336,102 @@ heif_complex64* heif_image_get_channel_complex64(heif_image*,
 // --- index-based component access (for ISO 23001-17 multi-component images)

 LIBHEIF_API
-int heif_image_get_number_of_components(const heif_image*);
+uint32_t heif_image_get_number_of_components(const heif_image*);
+
+LIBHEIF_API
+enum heif_channel heif_image_get_component_channel(const heif_image*, uint32_t component_idx);
+
+LIBHEIF_API
+uint32_t heif_image_get_component_width(const heif_image*, uint32_t component_idx);
+
+LIBHEIF_API
+uint32_t heif_image_get_component_height(const heif_image*, uint32_t component_idx);
+
+LIBHEIF_API
+int heif_image_get_component_bits_per_pixel(const heif_image*, uint32_t component_idx);

 LIBHEIF_API
-enum heif_channel heif_image_get_component_channel(const heif_image*, int component_idx);
+uint16_t heif_image_get_component_type(const heif_image*, uint32_t component_idx);

 LIBHEIF_API
-int heif_image_get_component_width(const heif_image*, int component_idx);
+heif_error heif_image_add_component(heif_image* image,
+                                    int width, int height,
+                                    uint16_t component_type,
+                                    enum heif_channel_datatype datatype,
+                                    int bit_depth,
+                                    uint32_t* out_component_idx);

 LIBHEIF_API
-int heif_image_get_component_height(const heif_image*, int component_idx);
+const uint8_t* heif_image_get_component_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-int heif_image_get_component_bits_per_pixel(const heif_image*, int component_idx);
+uint8_t* heif_image_get_component(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const uint8_t* heif_image_get_component_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const uint16_t* heif_image_get_component_uint16_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-uint8_t* heif_image_get_component(heif_image*, int component_idx, size_t* out_stride);
+uint16_t* heif_image_get_component_uint16(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const uint16_t* heif_image_get_component_uint16_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const uint32_t* heif_image_get_component_uint32_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-uint16_t* heif_image_get_component_uint16(heif_image*, int component_idx, size_t* out_stride);
+uint32_t* heif_image_get_component_uint32(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const uint32_t* heif_image_get_component_uint32_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const uint64_t* heif_image_get_component_uint64_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-uint32_t* heif_image_get_component_uint32(heif_image*, int component_idx, size_t* out_stride);
+uint64_t* heif_image_get_component_uint64(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const uint64_t* heif_image_get_component_uint64_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const int8_t* heif_image_get_component_int8_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-uint64_t* heif_image_get_component_uint64(heif_image*, int component_idx, size_t* out_stride);
+int8_t* heif_image_get_component_int8(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const int16_t* heif_image_get_component_int16_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const int16_t* heif_image_get_component_int16_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-int16_t* heif_image_get_component_int16(heif_image*, int component_idx, size_t* out_stride);
+int16_t* heif_image_get_component_int16(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const int32_t* heif_image_get_component_int32_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const int32_t* heif_image_get_component_int32_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-int32_t* heif_image_get_component_int32(heif_image*, int component_idx, size_t* out_stride);
+int32_t* heif_image_get_component_int32(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const int64_t* heif_image_get_component_int64_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const int64_t* heif_image_get_component_int64_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-int64_t* heif_image_get_component_int64(heif_image*, int component_idx, size_t* out_stride);
+int64_t* heif_image_get_component_int64(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const float* heif_image_get_component_float32_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const float* heif_image_get_component_float32_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-float* heif_image_get_component_float32(heif_image*, int component_idx, size_t* out_stride);
+float* heif_image_get_component_float32(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const double* heif_image_get_component_float64_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const double* heif_image_get_component_float64_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-double* heif_image_get_component_float64(heif_image*, int component_idx, size_t* out_stride);
+double* heif_image_get_component_float64(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const heif_complex32* heif_image_get_component_complex32_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const heif_complex32* heif_image_get_component_complex32_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-heif_complex32* heif_image_get_component_complex32(heif_image*, int component_idx, size_t* out_stride);
+heif_complex32* heif_image_get_component_complex32(heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-const heif_complex64* heif_image_get_component_complex64_readonly(const heif_image*, int component_idx, size_t* out_stride);
+const heif_complex64* heif_image_get_component_complex64_readonly(const heif_image*, uint32_t component_idx, size_t* out_stride);

 LIBHEIF_API
-heif_complex64* heif_image_get_component_complex64(heif_image*, int component_idx, size_t* out_stride);
+heif_complex64* heif_image_get_component_complex64(heif_image*, uint32_t component_idx, size_t* out_stride);

 #endif

diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index e803107f..54ddb716 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -347,7 +347,10 @@ Result<Encoder::CodedImageData> ImageItem::encode_to_bitstream_and_boxes(const s
       pixi->add_channel_bits(bpp);
     }
   }
-  codedImage.properties.push_back(pixi);
+
+  if (pixi->get_num_channels() != 0) {
+    codedImage.properties.push_back(pixi);
+  }

   // --- generate properties for image extra data

diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index 618dae5e..3ba8a39c 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -1890,57 +1890,81 @@ HeifPixelImage::extract_image_area(uint32_t x0, uint32_t y0, uint32_t w, uint32_

 // --- index-based component access methods

-heif_channel HeifPixelImage::get_component_channel(int component_idx) const
+heif_channel HeifPixelImage::get_component_channel(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   return m_planes[component_idx].m_channel;
 }


-uint32_t HeifPixelImage::get_component_width(int component_idx) const
+uint32_t HeifPixelImage::get_component_width(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   return m_planes[component_idx].m_width;
 }


-uint32_t HeifPixelImage::get_component_height(int component_idx) const
+uint32_t HeifPixelImage::get_component_height(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   return m_planes[component_idx].m_height;
 }


-uint8_t HeifPixelImage::get_component_bits_per_pixel(int component_idx) const
+uint8_t HeifPixelImage::get_component_bits_per_pixel(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   return m_planes[component_idx].m_bit_depth;
 }


-uint8_t HeifPixelImage::get_component_storage_bits_per_pixel(int component_idx) const
+uint8_t HeifPixelImage::get_component_storage_bits_per_pixel(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   uint32_t bpp = m_planes[component_idx].get_bytes_per_pixel() * 8;
   assert(bpp <= 255);
   return static_cast<uint8_t>(bpp);
 }


-heif_channel_datatype HeifPixelImage::get_component_datatype(int component_idx) const
+heif_channel_datatype HeifPixelImage::get_component_datatype(uint32_t component_idx) const
 {
-  assert(component_idx >= 0 && component_idx < static_cast<int>(m_planes.size()));
+  assert(component_idx < m_planes.size());
   return m_planes[component_idx].m_datatype;
 }


-uint8_t* HeifPixelImage::get_component(int component_idx, size_t* out_stride)
+uint16_t HeifPixelImage::get_component_type(uint32_t component_idx) const
+{
+  assert(component_idx < m_planes.size());
+  return m_planes[component_idx].m_component_type;
+}
+
+
+Result<uint32_t> HeifPixelImage::add_component(uint32_t width, uint32_t height,
+                                               uint16_t component_type,
+                                               heif_channel_datatype datatype, int bit_depth,
+                                               const heif_security_limits* limits)
+{
+  ImageComponent plane;
+  plane.m_channel = heif_channel_Y;
+  plane.m_component_type = component_type;
+  if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
+    return err;
+  }
+
+  m_planes.push_back(plane);
+  return static_cast<uint32_t>(m_planes.size() - 1);
+}
+
+
+uint8_t* HeifPixelImage::get_component(uint32_t component_idx, size_t* out_stride)
 {
   return get_component_data<uint8_t>(component_idx, out_stride);
 }


-const uint8_t* HeifPixelImage::get_component(int component_idx, size_t* out_stride) const
+const uint8_t* HeifPixelImage::get_component(uint32_t component_idx, size_t* out_stride) const
 {
   return get_component_data<uint8_t>(component_idx, out_stride);
 }
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index 21af3604..7e8f9744 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -291,23 +291,30 @@ public:

   // --- index-based component access (for ISO 23001-17 multi-component images)

-  int get_number_of_components() const { return static_cast<int>(m_planes.size()); }
+  uint32_t get_number_of_components() const { return static_cast<uint32_t>(m_planes.size()); }

-  heif_channel get_component_channel(int component_idx) const;
+  heif_channel get_component_channel(uint32_t component_idx) const;

-  uint32_t get_component_width(int component_idx) const;
-  uint32_t get_component_height(int component_idx) const;
-  uint8_t get_component_bits_per_pixel(int component_idx) const;
-  uint8_t get_component_storage_bits_per_pixel(int component_idx) const;
-  heif_channel_datatype get_component_datatype(int component_idx) const;
+  uint32_t get_component_width(uint32_t component_idx) const;
+  uint32_t get_component_height(uint32_t component_idx) const;
+  uint8_t get_component_bits_per_pixel(uint32_t component_idx) const;
+  uint8_t get_component_storage_bits_per_pixel(uint32_t component_idx) const;
+  heif_channel_datatype get_component_datatype(uint32_t component_idx) const;

-  uint8_t* get_component(int component_idx, size_t* out_stride);
-  const uint8_t* get_component(int component_idx, size_t* out_stride) const;
+  uint16_t get_component_type(uint32_t component_idx) const;
+
+  Result<uint32_t> add_component(uint32_t width, uint32_t height,
+                                 uint16_t component_type,
+                                 heif_channel_datatype datatype, int bit_depth,
+                                 const heif_security_limits* limits);
+
+  uint8_t* get_component(uint32_t component_idx, size_t* out_stride);
+  const uint8_t* get_component(uint32_t component_idx, size_t* out_stride) const;

   template <typename T>
-  T* get_component_data(int component_idx, size_t* out_stride)
+  T* get_component_data(uint32_t component_idx, size_t* out_stride)
   {
-    if (component_idx < 0 || component_idx >= static_cast<int>(m_planes.size())) {
+    if (component_idx >= m_planes.size()) {
       if (out_stride) *out_stride = 0;
       return nullptr;
     }
@@ -320,7 +327,7 @@ public:
   }

   template <typename T>
-  const T* get_component_data(int component_idx, size_t* out_stride) const
+  const T* get_component_data(uint32_t component_idx, size_t* out_stride) const
   {
     return const_cast<HeifPixelImage*>(this)->get_component_data<T>(component_idx, out_stride);
   }
@@ -387,6 +394,7 @@ private:
   struct ImageComponent
   {
     heif_channel m_channel = heif_channel_Y;
+    uint16_t m_component_type = 0;  // ISO 23001-17 component type (0 = monochrome)

     // limits=nullptr disables the limits
     Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d8fdfd8b..443f584f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -93,6 +93,10 @@ if (WITH_UNCOMPRESSED_CODEC)
     add_libheif_test(uncompressed_decode_ycbcr422)
     add_libheif_test(uncompressed_encode)

+    if (ENABLE_EXPERIMENTAL_FEATURES)
+        add_libheif_test(uncompressed_encode_multicomponent)
+    endif()
+
     if (ZLIB_FOUND)
         add_libheif_test(uncompressed_decode_generic_compression)
     else()
diff --git a/tests/uncompressed_encode_multicomponent.cc b/tests/uncompressed_encode_multicomponent.cc
new file mode 100644
index 00000000..02c3c95c
--- /dev/null
+++ b/tests/uncompressed_encode_multicomponent.cc
@@ -0,0 +1,429 @@
+/*
+  libheif integration tests for uncompressed multi-component images
+
+  MIT License
+
+  Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+#include "catch_amalgamated.hpp"
+#include "libheif/heif.h"
+#include "libheif/heif_experimental.h"
+#include "test_utils.h"
+#include <cstdint>
+#include <cstring>
+#include <cmath>
+#include <string>
+
+static constexpr int kWidth = 8;
+static constexpr int kHeight = 8;
+static constexpr int kNumComponents = 4;
+static constexpr uint16_t kMonoComponentType = 0;
+
+
+template <typename T>
+static T compute_fill_value(uint32_t comp, uint32_t y, uint32_t x)
+{
+  assert(false && "compute_fill_value not specialized for this type");
+  return T{};
+}
+
+template <>
+float compute_fill_value<float>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<float>(comp * 37 + y * kWidth + x + 1) * 0.1f;
+}
+
+template <>
+double compute_fill_value<double>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<double>(comp * 37 + y * kWidth + x + 1) * 0.1;
+}
+
+template <>
+heif_complex32 compute_fill_value<heif_complex32>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  float base = static_cast<float>(comp * 37 + y * kWidth + x + 1);
+  return {base * 0.1f, base * 0.2f};
+}
+
+template <>
+heif_complex64 compute_fill_value<heif_complex64>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  double base = static_cast<double>(comp * 37 + y * kWidth + x + 1);
+  return {base * 0.1, base * 0.2};
+}
+
+template <>
+int8_t compute_fill_value<int8_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<int8_t>(((comp * 37 + y * kWidth + x + 1) % 256) - 128);
+}
+
+template <>
+int16_t compute_fill_value<int16_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<int16_t>(comp * 37 + y * kWidth + x + 1);
+}
+
+template <>
+int32_t compute_fill_value<int32_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<int32_t>(comp * 37 + y * kWidth + x + 1);
+}
+
+template <>
+uint8_t compute_fill_value<uint8_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<uint8_t>((comp * 37 + y * kWidth + x + 1) & 0xFF);
+}
+
+template <>
+uint16_t compute_fill_value<uint16_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return static_cast<uint16_t>(comp * 37 + y * kWidth + x + 1);
+}
+
+template <>
+uint32_t compute_fill_value<uint32_t>(uint32_t comp, uint32_t y, uint32_t x)
+{
+  return comp * 37 + y * kWidth + x + 1;
+}
+
+
+// Typed accessor helpers: get mutable pointer for filling
+template <typename T>
+T* get_component_ptr(heif_image* image, uint32_t idx, size_t* out_stride);
+
+template <>
+uint8_t* get_component_ptr<uint8_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component(image, idx, out_stride);
+}
+
+template <>
+uint16_t* get_component_ptr<uint16_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_uint16(image, idx, out_stride);
+}
+
+template <>
+uint32_t* get_component_ptr<uint32_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_uint32(image, idx, out_stride);
+}
+
+template <>
+int16_t* get_component_ptr<int16_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int16(image, idx, out_stride);
+}
+
+template <>
+int32_t* get_component_ptr<int32_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int32(image, idx, out_stride);
+}
+
+template <>
+float* get_component_ptr<float>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_float32(image, idx, out_stride);
+}
+
+template <>
+double* get_component_ptr<double>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_float64(image, idx, out_stride);
+}
+
+template <>
+heif_complex32* get_component_ptr<heif_complex32>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_complex32(image, idx, out_stride);
+}
+
+template <>
+heif_complex64* get_component_ptr<heif_complex64>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_complex64(image, idx, out_stride);
+}
+
+
+// Typed accessor helpers: get const pointer for reading
+template <typename T>
+const T* get_component_ptr_readonly(const heif_image* image, uint32_t idx, size_t* out_stride);
+
+template <>
+const uint8_t* get_component_ptr_readonly<uint8_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_readonly(image, idx, out_stride);
+}
+
+template <>
+const uint16_t* get_component_ptr_readonly<uint16_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_uint16_readonly(image, idx, out_stride);
+}
+
+template <>
+const uint32_t* get_component_ptr_readonly<uint32_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_uint32_readonly(image, idx, out_stride);
+}
+
+template <>
+const int16_t* get_component_ptr_readonly<int16_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int16_readonly(image, idx, out_stride);
+}
+
+template <>
+const int32_t* get_component_ptr_readonly<int32_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int32_readonly(image, idx, out_stride);
+}
+
+template <>
+const float* get_component_ptr_readonly<float>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_float32_readonly(image, idx, out_stride);
+}
+
+template <>
+const double* get_component_ptr_readonly<double>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_float64_readonly(image, idx, out_stride);
+}
+
+template <>
+const heif_complex32* get_component_ptr_readonly<heif_complex32>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_complex32_readonly(image, idx, out_stride);
+}
+
+template <>
+const heif_complex64* get_component_ptr_readonly<heif_complex64>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_complex64_readonly(image, idx, out_stride);
+}
+
+
+template <>
+int8_t* get_component_ptr<int8_t>(heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int8(image, idx, out_stride);
+}
+
+template <>
+const int8_t* get_component_ptr_readonly<int8_t>(const heif_image* image, uint32_t idx, size_t* out_stride)
+{
+  return heif_image_get_component_int8_readonly(image, idx, out_stride);
+}
+
+
+template <typename T>
+static heif_image* create_and_fill_image(heif_channel_datatype datatype, int bit_depth)
+{
+  heif_image* image = nullptr;
+  heif_error err;
+
+  err = heif_image_create(kWidth, kHeight, heif_colorspace_nonvisual,
+                          heif_chroma_undefined, &image);
+  REQUIRE(err.code == heif_error_Ok);
+  REQUIRE(image != nullptr);
+
+  for (uint32_t c = 0; c < kNumComponents; c++) {
+    uint32_t idx = 0;
+    err = heif_image_add_component(image, kWidth, kHeight,
+                                   kMonoComponentType, datatype, bit_depth, &idx);
+    REQUIRE(err.code == heif_error_Ok);
+    REQUIRE(idx == c);
+
+    size_t stride = 0;
+    T* data = get_component_ptr<T>(image, idx, &stride);
+    REQUIRE(data != nullptr);
+    REQUIRE(stride >= kWidth);
+
+    for (uint32_t y = 0; y < kHeight; y++) {
+      for (uint32_t x = 0; x < kWidth; x++) {
+        data[y * stride + x] = compute_fill_value<T>(c, y, x);
+      }
+    }
+  }
+
+  return image;
+}
+
+
+template <typename T>
+static bool values_equal(T a, T b)
+{
+  return a == b;
+}
+
+template <>
+bool values_equal<float>(float a, float b)
+{
+  return std::abs(a - b) < 1e-5f;
+}
+
+template <>
+bool values_equal<double>(double a, double b)
+{
+  return std::abs(a - b) < 1e-10;
+}
+
+template <>
+bool values_equal<heif_complex32>(heif_complex32 a, heif_complex32 b)
+{
+  return std::abs(a.real - b.real) < 1e-5f && std::abs(a.imaginary - b.imaginary) < 1e-5f;
+}
+
+template <>
+bool values_equal<heif_complex64>(heif_complex64 a, heif_complex64 b)
+{
+  return std::abs(a.real - b.real) < 1e-10 && std::abs(a.imaginary - b.imaginary) < 1e-10;
+}
+
+
+template <typename T>
+static void verify_image_data(const heif_image* image)
+{
+  uint32_t num_components = heif_image_get_number_of_components(image);
+  REQUIRE(num_components == kNumComponents);
+
+  for (uint32_t c = 0; c < kNumComponents; c++) {
+    REQUIRE(heif_image_get_component_width(image, c) == kWidth);
+    REQUIRE(heif_image_get_component_height(image, c) == kHeight);
+    REQUIRE(heif_image_get_component_type(image, c) == kMonoComponentType);
+
+    size_t stride = 0;
+    const T* data = get_component_ptr_readonly<T>(image, c, &stride);
+    REQUIRE(data != nullptr);
+
+    for (uint32_t y = 0; y < kHeight; y++) {
+      for (uint32_t x = 0; x < kWidth; x++) {
+        T expected = compute_fill_value<T>(c, y, x);
+        T actual = data[y * stride + x];
+        REQUIRE(values_equal(expected, actual));
+      }
+    }
+  }
+}
+
+
+template <typename T>
+static void test_multi_mono(heif_channel_datatype datatype, int bit_depth, const char* output_filename)
+{
+  heif_image* image = create_and_fill_image<T>(datatype, bit_depth);
+
+  // Verify that data was written correctly before encode
+  verify_image_data<T>(image);
+
+  // Encode
+  heif_context* ctx = heif_context_alloc();
+  heif_encoder* encoder = nullptr;
+  heif_error err = heif_context_get_encoder_for_format(ctx, heif_compression_uncompressed, &encoder);
+  REQUIRE(err.code == heif_error_Ok);
+
+  err = heif_context_encode_image(ctx, image, encoder, nullptr, nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  // Write to file
+  std::string output_path = get_tests_output_file_path(output_filename);
+  err = heif_context_write_to_file(ctx, output_path.c_str());
+  REQUIRE(err.code == heif_error_Ok);
+
+  heif_encoder_release(encoder);
+  heif_image_release(image);
+  heif_context_free(ctx);
+
+  // Read back
+  heif_context* ctx2 = heif_context_alloc();
+  err = heif_context_read_from_file(ctx2, output_path.c_str(), nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  heif_image_handle* handle = nullptr;
+  err = heif_context_get_primary_image_handle(ctx2, &handle);
+  REQUIRE(err.code == heif_error_Ok);
+
+  heif_image* decoded = nullptr;
+  err = heif_decode_image(handle, &decoded, heif_colorspace_undefined, heif_chroma_undefined, nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  verify_image_data<T>(decoded);
+
+  heif_image_release(decoded);
+  heif_image_handle_release(handle);
+  heif_context_free(ctx2);
+}
+
+
+TEST_CASE("Multi-mono uint8")
+{
+  test_multi_mono<uint8_t>(heif_channel_datatype_unsigned_integer, 8, "multi_mono_uint8.heif");
+}
+
+TEST_CASE("Multi-mono uint16")
+{
+  test_multi_mono<uint16_t>(heif_channel_datatype_unsigned_integer, 16, "multi_mono_uint16.heif");
+}
+
+TEST_CASE("Multi-mono uint32")
+{
+  test_multi_mono<uint32_t>(heif_channel_datatype_unsigned_integer, 32, "multi_mono_uint32.heif");
+}
+
+TEST_CASE("Multi-mono int8")
+{
+  test_multi_mono<int8_t>(heif_channel_datatype_signed_integer, 8, "multi_mono_int8.heif");
+}
+
+TEST_CASE("Multi-mono int16")
+{
+  test_multi_mono<int16_t>(heif_channel_datatype_signed_integer, 16, "multi_mono_int16.heif");
+}
+
+TEST_CASE("Multi-mono int32")
+{
+  test_multi_mono<int32_t>(heif_channel_datatype_signed_integer, 32, "multi_mono_int32.heif");
+}
+
+TEST_CASE("Multi-mono float32")
+{
+  test_multi_mono<float>(heif_channel_datatype_floating_point, 32, "multi_mono_float32.heif");
+}
+
+TEST_CASE("Multi-mono float64")
+{
+  test_multi_mono<double>(heif_channel_datatype_floating_point, 64, "multi_mono_float64.heif");
+}
+
+TEST_CASE("Multi-mono complex32")
+{
+  test_multi_mono<heif_complex32>(heif_channel_datatype_complex_number, 64, "multi_mono_complex32.heif");
+}
+
+TEST_CASE("Multi-mono complex64")
+{
+  test_multi_mono<heif_complex64>(heif_channel_datatype_complex_number, 128, "multi_mono_complex64.heif");
+}