Commit ec7d3f8b for libheif
commit ec7d3f8b41d1797e0d1df5a1cab079ada3b89257
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Thu Mar 12 15:15:00 2026 +0100
unci: build cmpd in HeifPixelImage
diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index d5381863..2bd50224 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -130,6 +130,16 @@ template <typename T> const char* get_name(T val, const std::map<T, const char*>
}
}
+void Box_cmpd::set_components(const std::vector<uint16_t>& components)
+{
+ m_components.clear();
+
+ for (const auto& component : components) {
+ m_components.push_back({component, {}});
+ }
+}
+
+
Error Box_cmpd::parse(BitstreamRange& range, const heif_security_limits* limits)
{
uint32_t component_count = range.read32();
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index b2c54183..4ea310f2 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -73,6 +73,8 @@ public:
return index;
}
+ void set_components(const std::vector<uint16_t>&);
+
protected:
Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index feb1bb7c..c07fcc41 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -83,6 +83,8 @@ unc_encoder::unc_encoder(const std::shared_ptr<const HeifPixelImage>& image)
m_cmpd = std::make_shared<Box_cmpd>();
m_uncC = std::make_shared<Box_uncC>();
+ m_cmpd->set_components(image->get_cmpd_component_types());
+
// --- Bayer pattern: add reference components to cmpd and generate cpat box
if (image->has_bayer_pattern()) {
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index 620134b6..d5e31cc2 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -72,14 +72,16 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
auto comp_format = to_unc_component_format(image->get_component_datatype(idx));
bool aligned = (bpp % 8 == 0);
- m_components.push_back({idx, ch, comp_type, comp_format, bpp, aligned});
+ uint16_t cmpd_idx = image->get_component_cmpd_index();
+
+ m_components.push_back({cmpd_idx, ch, comp_type, comp_format, bpp, aligned});
}
// Build cmpd/uncC boxes
bool little_endian = false;
for (const auto& comp : m_components) {
- uint16_t cmpd_index = m_cmpd->add_component({static_cast<uint16_t>(comp.component_type)});
+ //uint16_t cmpd_index = m_cmpd->add_component({static_cast<uint16_t>(comp.component_type)});
uint8_t component_align_size = 0;
@@ -87,7 +89,7 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
little_endian = true;
}
- m_uncC->add_component({cmpd_index, comp.bpp, comp.component_format, component_align_size});
+ m_uncC->add_component({comp.component_idx, comp.bpp, comp.component_format, component_align_size});
}
m_uncC->set_interleave_type(interleave_mode_component);
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
index c5322292..98ae382d 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
@@ -40,7 +40,7 @@ public:
private:
struct channel_component
{
- uint32_t component_idx;
+ uint16_t component_idx;
heif_channel channel;
heif_uncompressed_component_type component_type;
heif_uncompressed_component_format component_format;
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
index 8b21f55d..80b18c89 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
@@ -59,9 +59,8 @@ unc_encoder_rgb_block_pixel_interleave::unc_encoder_rgb_block_pixel_interleave(c
const heif_encoding_options& options)
: unc_encoder(image)
{
- uint16_t idx_r = m_cmpd->add_component({heif_uncompressed_component_type_red});
- uint16_t idx_g = m_cmpd->add_component({heif_uncompressed_component_type_green});
- uint16_t idx_b = m_cmpd->add_component({heif_uncompressed_component_type_blue});
+ auto cmpd_idx = image->get_component_cmpd_indices_interleaved();
+ assert(cmpd_idx.size() == 3);
uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);
@@ -74,9 +73,9 @@ unc_encoder_rgb_block_pixel_interleave::unc_encoder_rgb_block_pixel_interleave(c
m_uncC->set_sampling_type(sampling_mode_no_subsampling);
m_uncC->set_block_little_endian(true);
- m_uncC->add_component({idx_r, bpp, component_format_unsigned, 0});
- m_uncC->add_component({idx_g, bpp, component_format_unsigned, 0});
- m_uncC->add_component({idx_b, bpp, component_format_unsigned, 0});
+ m_uncC->add_component({cmpd_idx[0], bpp, component_format_unsigned, 0});
+ m_uncC->add_component({cmpd_idx[1], bpp, component_format_unsigned, 0});
+ m_uncC->add_component({cmpd_idx[2], bpp, component_format_unsigned, 0});
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
index dc183cf4..1797dc15 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
@@ -59,18 +59,12 @@ unc_encoder_rgb_bytealign_pixel_interleave::unc_encoder_rgb_bytealign_pixel_inte
const heif_encoding_options& options)
: unc_encoder(image)
{
- uint16_t idx_r = m_cmpd->add_component({heif_uncompressed_component_type_red});
- uint16_t idx_g = m_cmpd->add_component({heif_uncompressed_component_type_green});
- uint16_t idx_b = m_cmpd->add_component({heif_uncompressed_component_type_blue});
+ auto cmpd_idx = image->get_component_cmpd_indices_interleaved();
bool save_alpha = image->has_alpha();
- uint16_t idx_a = 0;
-
- if (save_alpha) {
- idx_a = m_cmpd->add_component({heif_uncompressed_component_type_alpha});
- }
m_bytes_per_pixel = save_alpha ? 8 : 6;
+ assert(cmpd_idx.size() == m_bytes_per_pixel);
bool little_endian = (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE ||
image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE);
@@ -90,11 +84,11 @@ unc_encoder_rgb_bytealign_pixel_interleave::unc_encoder_rgb_bytealign_pixel_inte
m_uncC->set_components_little_endian(false); // little_endian);
m_uncC->set_pixel_size(m_bytes_per_pixel);
- m_uncC->add_component({idx_r, bpp, component_format_unsigned, component_align_size});
- m_uncC->add_component({idx_g, bpp, component_format_unsigned, component_align_size});
- m_uncC->add_component({idx_b, bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[0], bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[1], bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[2], bpp, component_format_unsigned, component_align_size});
if (save_alpha) {
- m_uncC->add_component({idx_a, bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[3], bpp, component_format_unsigned, component_align_size});
}
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
index 871d314d..bfac3a46 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
@@ -55,18 +55,11 @@ unc_encoder_rgb_pixel_interleave::unc_encoder_rgb_pixel_interleave(const std::sh
const heif_encoding_options& options)
: unc_encoder(image)
{
- uint16_t idx_r = m_cmpd->add_component({heif_uncompressed_component_type_red});
- uint16_t idx_g = m_cmpd->add_component({heif_uncompressed_component_type_green});
- uint16_t idx_b = m_cmpd->add_component({heif_uncompressed_component_type_blue});
-
+ auto cmpd_idx = image->get_component_cmpd_indices_interleaved();
bool save_alpha = image->has_alpha();
- uint16_t idx_a = 0;
-
- if (save_alpha) {
- idx_a = m_cmpd->add_component({heif_uncompressed_component_type_alpha});
- }
m_bytes_per_pixel = save_alpha ? 4 : 3;
+ assert(cmpd_idx.size() == m_bytes_per_pixel);
uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);
@@ -86,11 +79,11 @@ unc_encoder_rgb_pixel_interleave::unc_encoder_rgb_pixel_interleave(const std::sh
m_uncC->set_interleave_type(interleave_mode_pixel);
m_uncC->set_sampling_type(sampling_mode_no_subsampling);
- m_uncC->add_component({idx_r, bpp, component_format_unsigned, component_align_size});
- m_uncC->add_component({idx_g, bpp, component_format_unsigned, component_align_size});
- m_uncC->add_component({idx_b, bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[0], bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[1], bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[2], bpp, component_format_unsigned, component_align_size});
if (save_alpha) {
- m_uncC->add_component({idx_a, bpp, component_format_unsigned, component_align_size});
+ m_uncC->add_component({cmpd_idx[3], bpp, component_format_unsigned, component_align_size});
}
}
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index b8cdc479..fdecd125 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -129,33 +129,57 @@ heif_channel map_uncompressed_component_to_channel(uint16_t component_type)
}
-static uint16_t map_channel_to_component_type(heif_channel channel)
+static std::vector<uint16_t> map_channel_to_component_type(heif_channel channel, heif_chroma chroma)
{
switch (channel) {
case heif_channel_Y:
- return heif_uncompressed_component_type_Y;
+ return {heif_uncompressed_component_type_Y};
case heif_channel_Cb:
- return heif_uncompressed_component_type_Cb;
+ return {heif_uncompressed_component_type_Cb};
case heif_channel_Cr:
- return heif_uncompressed_component_type_Cr;
+ return {heif_uncompressed_component_type_Cr};
case heif_channel_R:
- return heif_uncompressed_component_type_red;
+ return {heif_uncompressed_component_type_red};
case heif_channel_G:
- return heif_uncompressed_component_type_green;
+ return {heif_uncompressed_component_type_green};
case heif_channel_B:
- return heif_uncompressed_component_type_blue;
+ return {heif_uncompressed_component_type_blue};
case heif_channel_Alpha:
- return heif_uncompressed_component_type_alpha;
+ return {heif_uncompressed_component_type_alpha};
case heif_channel_filter_array:
- return heif_uncompressed_component_type_filter_array;
+ return {heif_uncompressed_component_type_filter_array};
case heif_channel_depth:
- return heif_uncompressed_component_type_depth;
+ return {heif_uncompressed_component_type_depth};
case heif_channel_disparity:
- return heif_uncompressed_component_type_disparity;
+ return {heif_uncompressed_component_type_disparity};
+ case heif_channel_interleaved:
+ switch (chroma) {
+ case heif_chroma_interleaved_RGB:
+ case heif_chroma_interleaved_RRGGBB_BE:
+ case heif_chroma_interleaved_RRGGBB_LE:
+ return {
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_blue
+ };
+ case heif_chroma_interleaved_RGBA:
+ case heif_chroma_interleaved_RRGGBBAA_BE:
+ case heif_chroma_interleaved_RRGGBBAA_LE:
+ return {
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_alpha
+ };
+ default:
+ assert(false);
+ return {static_cast<uint16_t>(1000 + channel)};
+ break;
+ }
default:
- // For interleaved and other channels without a direct match,
+ // For other channels without a direct match,
// use an internal custom value.
- return static_cast<uint16_t>(1000 + channel);
+ return {static_cast<uint16_t>(1000 + channel)};
}
}
@@ -469,13 +493,32 @@ static uint32_t rounded_size(uint32_t s)
return s;
}
-Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth,
- const heif_security_limits* limits)
+HeifPixelImage::ImageComponent HeifPixelImage::new_image_plane_for_channel(heif_channel channel)
{
ImageComponent plane;
+
+ // libheif channel type
+
plane.m_channel = channel;
- m_cmpd_component_types.push_back(map_channel_to_component_type(channel));
- plane.m_component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
+
+ // ISO 23001-17 component types
+ // For interleaved planes, several component types are added to cmpd
+
+ auto cmpd = map_channel_to_component_type(channel, m_chroma);
+ for (size_t i = 0; i < cmpd.size(); i++) {
+ plane.m_component_index.push_back(static_cast<uint32_t>(m_cmpd_component_types.size()));
+ m_cmpd_component_types.push_back(cmpd[i]);
+ }
+
+ return plane;
+}
+
+
+Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth,
+ const heif_security_limits* limits)
+{
+ ImageComponent plane = new_image_plane_for_channel(channel);
+
int num_interleaved_pixels = num_interleaved_components_per_plane(m_chroma);
// for backwards compatibility, allow for 24/32 bits for RGB/RGBA interleaved chromas
@@ -501,10 +544,8 @@ Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t h
Error HeifPixelImage::add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
const heif_security_limits* limits)
{
- ImageComponent plane;
- plane.m_channel = channel;
- m_cmpd_component_types.push_back(map_channel_to_component_type(channel));
- plane.m_component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
+ ImageComponent plane = new_image_plane_for_channel(channel);
+
if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
return err;
}
@@ -831,7 +872,7 @@ uint32_t HeifPixelImage::get_primary_component() const
case heif_channel_G:
case heif_channel_B:
case heif_channel_filter_array:
- return m_planes[idx].m_component_index;
+ return m_planes[idx].m_component_index[0];
default:
; // NOP
}
@@ -841,7 +882,7 @@ uint32_t HeifPixelImage::get_primary_component() const
if (!m_cmpd_component_types.empty()) {
for (uint32_t idx=0; idx<m_planes.size(); idx++) {
- uint16_t comp_type = get_component_type(m_planes[idx].m_component_index);
+ uint16_t comp_type = get_component_type(m_planes[idx].m_component_index[0]);
switch (comp_type) {
case heif_uncompressed_component_type_Y:
case heif_uncompressed_component_type_monochrome:
@@ -854,7 +895,7 @@ uint32_t HeifPixelImage::get_primary_component() const
case heif_uncompressed_component_type_key_black:
case heif_uncompressed_component_type_filter_array:
case heif_uncompressed_component_type_palette:
- return m_planes[idx].m_component_index;
+ return m_planes[idx].m_component_index[0];
default:
; // NOP
@@ -865,7 +906,7 @@ uint32_t HeifPixelImage::get_primary_component() const
// third pass: allow anything
if (!m_planes.empty()) {
- return m_planes[0].m_component_index;
+ return m_planes[0].m_component_index[0];
}
return 0;
}
@@ -2110,7 +2151,10 @@ HeifPixelImage::extract_image_area(uint32_t x0, uint32_t y0, uint32_t w, uint32_
HeifPixelImage::ImageComponent* HeifPixelImage::find_component_by_index(uint32_t component_index)
{
for (auto& plane : m_planes) {
- if (plane.m_component_index == component_index) {
+ // we search through all indices in case we have an interleaved plane
+ if (std::find(plane.m_component_index.begin(),
+ plane.m_component_index.end(),
+ component_index) != plane.m_component_index.end()) {
return &plane;
}
}
@@ -2183,6 +2227,14 @@ uint16_t HeifPixelImage::get_component_type(uint32_t component_idx) const
}
+std::vector<uint16_t> HeifPixelImage::get_component_cmpd_indices_interleaved() const
+{
+ const ImageComponent* comp = find_component_for_channel(heif_channel_interleaved);
+ assert(comp);
+ return comp->m_component_index;
+}
+
+
Result<uint32_t> HeifPixelImage::add_component(uint32_t width, uint32_t height,
uint16_t component_type,
heif_channel_datatype datatype, int bit_depth,
@@ -2190,11 +2242,11 @@ Result<uint32_t> HeifPixelImage::add_component(uint32_t width, uint32_t height,
{
// Auto-generate component_index by appending to cmpd table
m_cmpd_component_types.push_back(component_type);
- uint32_t component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
+ uint16_t component_index = static_cast<uint16_t>(m_cmpd_component_types.size() - 1);
ImageComponent plane;
plane.m_channel = map_uncompressed_component_to_channel(component_type);
- plane.m_component_index = component_index;
+ plane.m_component_index = std::vector{component_index};
if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
return err;
}
@@ -2218,7 +2270,7 @@ Result<uint32_t> HeifPixelImage::add_component_for_index(uint32_t component_inde
ImageComponent plane;
plane.m_channel = map_uncompressed_component_to_channel(component_type);
- plane.m_component_index = component_index;
+ plane.m_component_index = std::vector{static_cast<uint16_t>(component_index)};
if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
return err;
}
@@ -2233,7 +2285,7 @@ std::vector<uint32_t> HeifPixelImage::get_used_component_indices() const
std::vector<uint32_t> indices;
indices.reserve(m_planes.size());
for (const auto& plane : m_planes) {
- indices.push_back(plane.m_component_index);
+ indices.insert(indices.end(), plane.m_component_index.begin(), plane.m_component_index.end());
}
return indices;
}
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index c01f18c5..002b7b31 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -441,6 +441,12 @@ public:
// even those that have no image plane (e.g. bayer reference components).
uint16_t get_component_type(uint32_t component_idx) const;
+ std::vector<uint16_t> get_cmpd_component_types() const { return m_cmpd_component_types; }
+
+ std::vector<uint16_t> get_component_cmpd_indices_interleaved() const;
+
+ uint16_t get_component_cmpd_index() const { assert(m_cmpd_component_types.size()==1); return m_cmpd_component_types[0]; }
+
// Encoder path: auto-generates component_index by appending to cmpd table.
Result<uint32_t> add_component(uint32_t width, uint32_t height,
uint16_t component_type,
@@ -456,6 +462,8 @@ public:
// Populate the cmpd component types table (decoder path).
void set_cmpd_component_types(std::vector<uint16_t> types) { m_cmpd_component_types = std::move(types); }
+ const std::vector<uint16_t>& get_cmpd_component_types() { return m_cmpd_component_types; }
+
// Returns the sorted list of component_indices of all planes that have pixel data.
std::vector<uint32_t> get_used_component_indices() const;
@@ -545,7 +553,10 @@ private:
struct ImageComponent
{
heif_channel m_channel = heif_channel_Y;
- uint32_t m_component_index = 0; // index into the cmpd component definition table
+
+ // index into the cmpd component definition table
+ // Interleaved channels will have a list of indices in the order R,G,B,A
+ std::vector<uint16_t> m_component_index;
// limits=nullptr disables the limits
Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
@@ -590,6 +601,8 @@ private:
ImageComponent* find_component_by_index(uint32_t component_index);
const ImageComponent* find_component_by_index(uint32_t component_index) const;
+ ImageComponent new_image_plane_for_channel(heif_channel channel);
+
uint32_t m_width = 0;
uint32_t m_height = 0;
heif_colorspace m_colorspace = heif_colorspace_undefined;