Commit e01c2164 for libheif

commit e01c216466cc20e3f48e6e4f1e3ba8a3f3749046
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Mar 12 11:18:40 2026 +0100

    unci: generate unci metadata boxes in unc_encoder constructor

diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index a7fa4a30..b2c54183 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -66,9 +66,11 @@ public:

   bool has_component(heif_uncompressed_component_type) const;

-  void add_component(const Component& component)
+  uint16_t add_component(const Component& component)
   {
+    auto index = static_cast<uint16_t>(m_components.size());
     m_components.push_back(component);
+    return index;
   }

 protected:
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index b37ad91c..feb1bb7c 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -22,6 +22,8 @@

 #include <cassert>
 #include <cstring>
+#include <map>
+#include <set>

 #include "pixelimage.h"
 #include "unc_boxes.h"
@@ -76,10 +78,79 @@ heif_uncompressed_component_format to_unc_component_format(heif_channel_datatype
 }


-unc_encoder::unc_encoder()
+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>();
+
+  // --- Bayer pattern: add reference components to cmpd and generate cpat box
+
+  if (image->has_bayer_pattern()) {
+    const BayerPattern& bayer = image->get_bayer_pattern();
+
+    // The bayer pattern stores component_index values. When the image has a cmpd
+    // table (add_component path), we look up the component type from it. When it
+    // doesn't (legacy add_plane path), the component_index IS the component type.
+
+    // Collect unique component types from the pattern (in order of first appearance)
+    std::vector<uint16_t> unique_types;
+    std::set<uint16_t> seen;
+    for (const auto& pixel : bayer.pixels) {
+      uint16_t comp_type = pixel.component_index;  // legacy: index IS the type
+      if (seen.insert(comp_type).second) {
+        unique_types.push_back(comp_type);
+      }
+    }
+
+    // Add reference components to cmpd (these have no uncC entries).
+    std::map<uint16_t, uint16_t> type_to_cmpd_index;
+    for (uint16_t type : unique_types) {
+      type_to_cmpd_index[type] = m_cmpd->add_component({type});
+    }
+
+    // Build cpat box with resolved cmpd indices
+    BayerPattern cpat_pattern;
+    cpat_pattern.pattern_width = bayer.pattern_width;
+    cpat_pattern.pattern_height = bayer.pattern_height;
+    cpat_pattern.pixels.resize(bayer.pixels.size());
+    for (size_t i = 0; i < bayer.pixels.size(); i++) {
+      uint16_t comp_type = bayer.pixels[i].component_index;  // legacy: index IS the type
+      cpat_pattern.pixels[i].component_index = type_to_cmpd_index[comp_type];
+      cpat_pattern.pixels[i].component_gain = bayer.pixels[i].component_gain;
+    }
+
+    m_cpat = std::make_shared<Box_cpat>();
+    m_cpat->set_pattern(cpat_pattern);
+  }
+
+  if (image->has_polarization_patterns()) {
+    for (const auto& pol : image->get_polarization_patterns()) {
+      auto splz = std::make_shared<Box_splz>();
+      splz->set_pattern(pol);
+      m_splz.push_back(splz);
+    }
+  }
+
+  if (image->has_sensor_bad_pixels_maps()) {
+    for (const auto& bpm : image->get_sensor_bad_pixels_maps()) {
+      auto sbpm = std::make_shared<Box_sbpm>();
+      sbpm->set_bad_pixels_map(bpm);
+      m_sbpm.push_back(sbpm);
+    }
+  }
+
+  if (image->has_sensor_nuc()) {
+    for (const auto& nuc : image->get_sensor_nuc()) {
+      auto snuc = std::make_shared<Box_snuc>();
+      snuc->set_nuc(nuc);
+      m_snuc.push_back(snuc);
+    }
+  }
+
+  if (image->has_chroma_location()) {
+    m_cloc = std::make_shared<Box_cloc>();
+    m_cloc->set_chroma_location(image->get_chroma_location());
+  }
 }


diff --git a/libheif/codecs/uncompressed/unc_encoder.h b/libheif/codecs/uncompressed/unc_encoder.h
index 9b70a725..40d2a8d9 100644
--- a/libheif/codecs/uncompressed/unc_encoder.h
+++ b/libheif/codecs/uncompressed/unc_encoder.h
@@ -46,7 +46,7 @@ heif_uncompressed_component_format to_unc_component_format(heif_channel_datatype
 class unc_encoder
 {
 public:
-  unc_encoder();
+  explicit unc_encoder(const std::shared_ptr<const HeifPixelImage>& image);

   virtual ~unc_encoder() = default;

diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index 0fac9295..620134b6 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -21,8 +21,6 @@
 #include "unc_encoder_component_interleave.h"

 #include <cstring>
-#include <map>
-#include <set>

 #include "pixelimage.h"
 #include "unc_boxes.h"
@@ -48,6 +46,7 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_component_interleave::cre

 unc_encoder_component_interleave::unc_encoder_component_interleave(const std::shared_ptr<const HeifPixelImage>& image,
                                                                    const heif_encoding_options& options)
+    : unc_encoder(image)
 {
   bool is_nonvisual = (image->get_colorspace() == heif_colorspace_nonvisual);
   uint32_t num_components = image->get_number_of_used_components();
@@ -79,9 +78,8 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
   // Build cmpd/uncC boxes
   bool little_endian = false;

-  uint16_t box_index = 0;
   for (const auto& comp : m_components) {
-    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;

@@ -89,8 +87,7 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
       little_endian = true;
     }

-    m_uncC->add_component({box_index, comp.bpp, comp.component_format, component_align_size});
-    box_index++;
+    m_uncC->add_component({cmpd_index, comp.bpp, comp.component_format, component_align_size});
   }

   m_uncC->set_interleave_type(interleave_mode_component);
@@ -106,78 +103,6 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
   else {
     m_uncC->set_sampling_type(sampling_mode_no_subsampling);
   }
-
-  // --- Bayer pattern: add reference components to cmpd and generate cpat box
-
-  if (image->has_bayer_pattern()) {
-    const BayerPattern& bayer = image->get_bayer_pattern();
-
-    // The bayer pattern stores component_index values. When the image has a cmpd
-    // table (add_component path), we look up the component type from it. When it
-    // doesn't (legacy add_plane path), the component_index IS the component type.
-
-    // Collect unique component types from the pattern (in order of first appearance)
-    std::vector<uint16_t> unique_types;
-    std::set<uint16_t> seen;
-    for (const auto& pixel : bayer.pixels) {
-      uint16_t comp_type = pixel.component_index;  // legacy: index IS the type
-      if (seen.insert(comp_type).second) {
-        unique_types.push_back(comp_type);
-      }
-    }
-
-    // Add reference components to cmpd (these have no uncC entries).
-    // box_index is already at the next available index after data components.
-    std::map<uint16_t, uint16_t> type_to_cmpd_index;
-    for (uint16_t type : unique_types) {
-      type_to_cmpd_index[type] = box_index;
-      m_cmpd->add_component({type});
-      box_index++;
-    }
-
-    // Build cpat box with resolved cmpd indices
-    BayerPattern cpat_pattern;
-    cpat_pattern.pattern_width = bayer.pattern_width;
-    cpat_pattern.pattern_height = bayer.pattern_height;
-    cpat_pattern.pixels.resize(bayer.pixels.size());
-    for (size_t i = 0; i < bayer.pixels.size(); i++) {
-      uint16_t comp_type = bayer.pixels[i].component_index;  // legacy: index IS the type
-      cpat_pattern.pixels[i].component_index = type_to_cmpd_index[comp_type];
-      cpat_pattern.pixels[i].component_gain = bayer.pixels[i].component_gain;
-    }
-
-    m_cpat = std::make_shared<Box_cpat>();
-    m_cpat->set_pattern(cpat_pattern);
-  }
-
-  if (image->has_polarization_patterns()) {
-    for (const auto& pol : image->get_polarization_patterns()) {
-      auto splz = std::make_shared<Box_splz>();
-      splz->set_pattern(pol);
-      m_splz.push_back(splz);
-    }
-  }
-
-  if (image->has_sensor_bad_pixels_maps()) {
-    for (const auto& bpm : image->get_sensor_bad_pixels_maps()) {
-      auto sbpm = std::make_shared<Box_sbpm>();
-      sbpm->set_bad_pixels_map(bpm);
-      m_sbpm.push_back(sbpm);
-    }
-  }
-
-  if (image->has_sensor_nuc()) {
-    for (const auto& nuc : image->get_sensor_nuc()) {
-      auto snuc = std::make_shared<Box_snuc>();
-      snuc->set_nuc(nuc);
-      m_snuc.push_back(snuc);
-    }
-  }
-
-  if (image->has_chroma_location()) {
-    m_cloc = std::make_shared<Box_cloc>();
-    m_cloc->set_chroma_location(image->get_chroma_location());
-  }
 }


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 16be6a0e..8b21f55d 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
@@ -57,10 +57,11 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_block_pixel_interleav

 unc_encoder_rgb_block_pixel_interleave::unc_encoder_rgb_block_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
                                                                              const heif_encoding_options& options)
+    : unc_encoder(image)
 {
-  m_cmpd->add_component({heif_uncompressed_component_type_red});
-  m_cmpd->add_component({heif_uncompressed_component_type_green});
-  m_cmpd->add_component({heif_uncompressed_component_type_blue});
+  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});

   uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);

@@ -73,9 +74,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({0, bpp, component_format_unsigned, 0});
-  m_uncC->add_component({1, bpp, component_format_unsigned, 0});
-  m_uncC->add_component({2, bpp, component_format_unsigned, 0});
+  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});
 }


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 d103abb4..dc183cf4 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
@@ -57,15 +57,17 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_bytealign_pixel_inter

 unc_encoder_rgb_bytealign_pixel_interleave::unc_encoder_rgb_bytealign_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
                                        const heif_encoding_options& options)
+    : unc_encoder(image)
 {
-  m_cmpd->add_component({heif_uncompressed_component_type_red});
-  m_cmpd->add_component({heif_uncompressed_component_type_green});
-  m_cmpd->add_component({heif_uncompressed_component_type_blue});
+  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});

   bool save_alpha = image->has_alpha();
+  uint16_t idx_a = 0;

   if (save_alpha) {
-    m_cmpd->add_component({heif_uncompressed_component_type_alpha});
+    idx_a = m_cmpd->add_component({heif_uncompressed_component_type_alpha});
   }

   m_bytes_per_pixel = save_alpha ? 8 : 6;
@@ -88,11 +90,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({0, bpp, component_format_unsigned, component_align_size});
-  m_uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
-  m_uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
+  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});
   if (save_alpha) {
-    m_uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
+    m_uncC->add_component({idx_a, 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 6e2fc457..871d314d 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
@@ -53,15 +53,17 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_pixel_interleave::cre

 unc_encoder_rgb_pixel_interleave::unc_encoder_rgb_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
                                              const heif_encoding_options& options)
+    : unc_encoder(image)
 {
-  m_cmpd->add_component({heif_uncompressed_component_type_red});
-  m_cmpd->add_component({heif_uncompressed_component_type_green});
-  m_cmpd->add_component({heif_uncompressed_component_type_blue});
+  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});

   bool save_alpha = image->has_alpha();
+  uint16_t idx_a = 0;

   if (save_alpha) {
-    m_cmpd->add_component({heif_uncompressed_component_type_alpha});
+    idx_a = m_cmpd->add_component({heif_uncompressed_component_type_alpha});
   }

   m_bytes_per_pixel = save_alpha ? 4 : 3;
@@ -84,11 +86,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({0, bpp, component_format_unsigned, component_align_size});
-  m_uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
-  m_uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
+  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});
   if (save_alpha) {
-    m_uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
+    m_uncC->add_component({idx_a, bpp, component_format_unsigned, component_align_size});
   }
 }