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");
+}