Commit 1d18b1eb for libheif

commit 1d18b1eb6d2dc5aa2247a982d3905ea89abd7fd0
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Fri Feb 27 15:43:46 2026 +0100

    set GIMI component content IDs through image handle

diff --git a/libheif/api/libheif/heif_image_handle.cc b/libheif/api/libheif/heif_image_handle.cc
index d996a7d0..eb4facc9 100644
--- a/libheif/api/libheif/heif_image_handle.cc
+++ b/libheif/api/libheif/heif_image_handle.cc
@@ -192,13 +192,32 @@ void heif_image_handle_set_gimi_content_id(heif_image_handle* handle, const char
 }


+#if WITH_UNCOMPRESSED_CODEC
+static std::shared_ptr<Box_cmpd> get_effective_cmpd(const heif_image_handle* handle)
+{
+  // Try explicit cmpd property first.
+  auto cmpd = handle->image->get_property<Box_cmpd>();
+  if (cmpd) {
+    return cmpd;
+  }
+
+  // For profile-based uncC (version 1), the cmpd is synthesized inside the uncC box.
+  auto uncC = handle->image->get_property<Box_uncC>();
+  if (uncC) {
+    fill_uncC_and_cmpd_from_profile(uncC, cmpd);
+  }
+  return cmpd;
+}
+#endif
+
+
 uint32_t heif_image_handle_get_number_of_cmpd_components(const heif_image_handle* handle)
 {
 #if WITH_UNCOMPRESSED_CODEC
   if (!handle) {
     return 0;
   }
-  auto cmpd = handle->image->get_property<Box_cmpd>();
+  auto cmpd = get_effective_cmpd(handle);
   if (!cmpd) {
     return 0;
   }
@@ -215,7 +234,7 @@ uint16_t heif_image_handle_get_cmpd_component_type(const heif_image_handle* hand
   if (!handle) {
     return 0;
   }
-  auto cmpd = handle->image->get_property<Box_cmpd>();
+  auto cmpd = get_effective_cmpd(handle);
   if (!cmpd) {
     return 0;
   }
@@ -236,7 +255,7 @@ const char* heif_image_handle_get_cmpd_component_type_uri(const heif_image_handl
   if (!handle) {
     return nullptr;
   }
-  auto cmpd = handle->image->get_property<Box_cmpd>();
+  auto cmpd = get_effective_cmpd(handle);
   if (!cmpd) {
     return nullptr;
   }
@@ -295,3 +314,34 @@ const char* heif_image_handle_get_gimi_component_content_id(const heif_image_han
   return nullptr;
 #endif
 }
+
+
+void heif_image_handle_set_gimi_component_content_id(heif_image_handle* handle,
+                                                     uint32_t component_idx,
+                                                     const char* content_id)
+{
+#if WITH_UNCOMPRESSED_CODEC
+  if (!handle || !content_id) {
+    return;
+  }
+
+  auto box = handle->image->get_property<Box_gimi_component_content_ids>();
+  if (!box) {
+    // Create a new box and add it as property.
+    auto new_box = std::make_shared<Box_gimi_component_content_ids>();
+    std::vector<std::string> ids(component_idx + 1);
+    ids[component_idx] = content_id;
+    new_box->set_content_ids(ids);
+    handle->context->add_property(handle->image->get_id(), new_box, false);
+  }
+  else {
+    // Mutate the existing box in-place.
+    auto ids = box->get_content_ids();
+    if (component_idx >= ids.size()) {
+      ids.resize(component_idx + 1);
+    }
+    ids[component_idx] = content_id;
+    box->set_content_ids(ids);
+  }
+#endif
+}
diff --git a/libheif/api/libheif/heif_image_handle.h b/libheif/api/libheif/heif_image_handle.h
index 3b4f811b..5bd3f29a 100644
--- a/libheif/api/libheif/heif_image_handle.h
+++ b/libheif/api/libheif/heif_image_handle.h
@@ -156,6 +156,14 @@ int heif_image_handle_has_gimi_component_content_ids(const heif_image_handle*);
 LIBHEIF_API
 const char* heif_image_handle_get_gimi_component_content_id(const heif_image_handle*, uint32_t component_idx);

+// Set a GIMI component content ID for a single component.
+// If an ItemComponentContentIDProperty does not yet exist, one will be created.
+// The content IDs array is resized as needed (new entries default to empty).
+LIBHEIF_API
+void heif_image_handle_set_gimi_component_content_id(heif_image_handle*,
+                                                     uint32_t component_idx,
+                                                     const char* content_id);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libheif/api/libheif/heif_uncompressed.cc b/libheif/api/libheif/heif_uncompressed.cc
index 5ed77ef6..22ea296b 100644
--- a/libheif/api/libheif/heif_uncompressed.cc
+++ b/libheif/api/libheif/heif_uncompressed.cc
@@ -763,40 +763,12 @@ heif_image_get_component_X(complex32, heif_complex32)
 heif_image_get_component_X(complex64, heif_complex64)


-// --- GIMI component content IDs
-
-int heif_image_has_component_content_ids(const heif_image* image)
-{
-  if (!image || !image->image) {
-    return 0;
-  }
-  return static_cast<int>(image->image->get_component_content_ids().size());
-}
-
-
-const char* heif_image_get_component_content_id(const heif_image* image, uint32_t component_idx)
-{
-  if (!image || !image->image || !image->image->has_component_content_ids()) {
-    return nullptr;
-  }
-
-  const auto& ids = image->image->get_component_content_ids();
-  if (component_idx >= ids.size()) {
-    return nullptr;
-  }
-
-  char* idstring = new char[ids[component_idx].size() + 1];
-  strcpy(idstring, ids[component_idx].c_str());
-  return idstring;
-}
-
-
-heif_error heif_image_set_component_content_id(heif_image* image,
-                                               uint32_t component_idx,
-                                               const char* content_id)
+void heif_image_set_gimi_component_content_id(heif_image* image,
+                                              uint32_t component_idx,
+                                              const char* content_id)
 {
   if (!image || !image->image || !content_id) {
-    return heif_error_null_pointer_argument;
+    return;
   }

   auto ids = image->image->get_component_content_ids();
@@ -805,8 +777,6 @@ heif_error heif_image_set_component_content_id(heif_image* image,
   }
   ids[component_idx] = content_id;
   image->image->set_component_content_ids(ids);
-
-  return heif_error_success;
 }


diff --git a/libheif/api/libheif/heif_uncompressed.h b/libheif/api/libheif/heif_uncompressed.h
index ea36071f..2166aabd 100644
--- a/libheif/api/libheif/heif_uncompressed.h
+++ b/libheif/api/libheif/heif_uncompressed.h
@@ -388,24 +388,15 @@ LIBHEIF_API
 heif_complex64* heif_image_get_component_complex64(heif_image*, uint32_t component_idx, size_t* out_stride);


-// --- GIMI component content IDs
+// --- GIMI component content IDs (set before encoding)

-// Returns non-zero (the number of content IDs) if component content IDs are set, 0 otherwise.
-LIBHEIF_API
-int heif_image_has_component_content_ids(const heif_image*);
-
-// Get a component content ID by cmpd component index.
-// Returns NULL if no content IDs are set or if the index is out of range.
-// The returned string must be freed with heif_string_release().
-LIBHEIF_API
-const char* heif_image_get_component_content_id(const heif_image*, uint32_t component_idx);
-
-// Set a component content ID for a single cmpd component.
+// Set a GIMI component content ID for a single cmpd component.
 // If the internal array is too small, it will be resized (new entries default to empty strings).
+// These are written into an ItemComponentContentIDProperty box during encoding.
 LIBHEIF_API
-heif_error heif_image_set_component_content_id(heif_image*,
-                                               uint32_t component_idx,
-                                               const char* content_id);
+void heif_image_set_gimi_component_content_id(heif_image*,
+                                              uint32_t component_idx,
+                                              const char* content_id);

 #ifdef __cplusplus
 }
diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index 18ae7f7f..ab3bf239 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -37,10 +37,6 @@
 #include "plugin_registry.h"
 #include "security_limits.h"

-#if WITH_UNCOMPRESSED_CODEC
-#include "codecs/uncompressed/unc_boxes.h"
-#endif
-
 #include <limits>
 #include <cassert>
 #include <cstring>
@@ -935,15 +931,6 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_image(const heif_decod
       img->set_gimi_sample_content_id(gimi_content_id->get_content_id());
     }

-#if WITH_UNCOMPRESSED_CODEC
-    // GIMI component content IDs
-
-    auto gimi_comp_ids = get_property<Box_gimi_component_content_ids>();
-    if (gimi_comp_ids) {
-      img->set_component_content_ids(gimi_comp_ids->get_content_ids());
-    }
-#endif
-
 #if HEIF_WITH_OMAF
     // Image projection (OMAF)
     auto prfr = get_property<Box_prfr>();
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index a0e0126c..0b95a8df 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -1973,10 +1973,6 @@ void HeifPixelImage::forward_all_metadata_from(const std::shared_ptr<const HeifP
     set_gimi_sample_content_id(src_image->get_gimi_sample_content_id());
   }

-  if (src_image->has_component_content_ids()) {
-    set_component_content_ids(src_image->get_component_content_ids());
-  }
-
   if (auto* tai = src_image->get_tai_timestamp()) {
     set_tai_timestamp(tai);
   }