Commit e72315d3 for libheif

commit e72315d3f522e065c4e4b3e170bb477eb5e1a569
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Dec 14 01:03:31 2025 +0100

    [BSD3] 'uncv': alpha channel can be stored in main visual track

diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index a338d93e..b3f49fc0 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -185,6 +185,13 @@ std::string Box_cmpd::Component::get_component_type_name(uint16_t component_type
 }


+bool Box_cmpd::has_component(heif_uncompressed_component_type type) const
+{
+  return std::any_of(m_components.begin(), m_components.end(),
+                     [type](const auto& cmp) { return cmp.component_type == type; });
+}
+
+
 std::string Box_cmpd::dump(Indent& indent) const
 {
   std::ostringstream sstr;
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index dc421fd6..79bdd4eb 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -60,6 +60,8 @@ public:

   const std::vector<Component>& get_components() const { return m_components; }

+  bool has_component(heif_uncompressed_component_type) const;
+
   void add_component(const Component& component)
   {
     m_components.push_back(component);
diff --git a/libheif/sequences/track_visual.cc b/libheif/sequences/track_visual.cc
index 53b056a2..7cf8e6fd 100644
--- a/libheif/sequences/track_visual.cc
+++ b/libheif/sequences/track_visual.cc
@@ -28,6 +28,7 @@
 #include "context.h"
 #include "api_structs.h"
 #include "codecs/hevc_boxes.h"
+#include "codecs/uncompressed/unc_boxes.h"


 Track_Visual::Track_Visual(HeifContext* ctx)
@@ -134,6 +135,31 @@ Track_Visual::Track_Visual(HeifContext* ctx, uint32_t track_id, uint16_t width,
 }


+bool Track_Visual::has_alpha_channel() const
+{
+  if (m_aux_alpha_track != nullptr) {
+    return true;
+  }
+
+  // --- special case: 'uncv' with alpha component
+
+  if (m_stsd) {
+    auto sampleEntry = m_stsd->get_sample_entry(0);
+    if (sampleEntry) {
+      if (auto box_uncv = std::dynamic_pointer_cast<const Box_uncv>(sampleEntry)) {
+        if (auto cmpd = box_uncv->get_child_box<const Box_cmpd>()) {
+          if (cmpd->has_component(component_type_alpha)) {
+            return true;
+          }
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+
 Result<std::shared_ptr<HeifPixelImage> > Track_Visual::decode_next_image_sample(const heif_decoding_options& options)
 {
   // --- If we ignore the editlist, we stop when we reached the end of the original samples.
@@ -356,7 +382,8 @@ Error Track_Visual::encode_image(std::shared_ptr<HeifPixelImage> image,

   // --- If input has an alpha channel, add an alpha auxiliary track.

-  if (in_options->save_alpha_channel && image->has_alpha() && !m_aux_alpha_track) {
+  if (in_options->save_alpha_channel && image->has_alpha() && !m_aux_alpha_track &&
+      h_encoder->plugin->compression_format != heif_compression_uncompressed) { // TODO: ask plugin
     if (m_active_encoder) {
       return {
         heif_error_Usage_error,
diff --git a/libheif/sequences/track_visual.h b/libheif/sequences/track_visual.h
index 4eeab87f..e99534e5 100644
--- a/libheif/sequences/track_visual.h
+++ b/libheif/sequences/track_visual.h
@@ -47,7 +47,7 @@ public:

   uint16_t get_height() const { return m_height; }

-  bool has_alpha_channel() const override { return m_aux_alpha_track != nullptr; }
+  bool has_alpha_channel() const override;

   Result<std::shared_ptr<HeifPixelImage>> decode_next_image_sample(const heif_decoding_options& options);