Commit f45e5917 for libheif

commit f45e591708cf6635800883fa36e1486b920bf92a
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Mon Feb 9 00:32:37 2026 +0100

    unci: add full list of uncC profiles

diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index 87fd6c12..ff2702c0 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -232,26 +232,29 @@ Error Box_uncC::parse(BitstreamRange& range, const heif_security_limits* limits)
   m_profile = range.read32();

   if (get_version() == 1) {
-    if (m_profile == fourcc("rgb3")) {
-      Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0};
-      add_component(component0);
-      Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0};
-      add_component(component1);
-      Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0};
-      add_component(component2);
-    }
-    else if ((m_profile == fourcc("rgba")) || (m_profile == fourcc("abgr"))) {
-      Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0};
-      add_component(component0);
-      Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0};
-      add_component(component1);
-      Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0};
-      add_component(component2);
-      Box_uncC::Component component3 = {3, 8, component_format_unsigned, 0};
-      add_component(component3);
-    }
-    else {
-        return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"};
+    switch (m_profile) {
+      case fourcc("rgb3"):
+      case fourcc("rgba"):
+      case fourcc("abgr"):
+      case fourcc("2vuy"):
+      case fourcc("yuv2"):
+      case fourcc("yvyu"):
+      case fourcc("vyuy"):
+      case fourcc("yuv1"):
+      case fourcc("v308"):
+      case fourcc("v408"):
+      case fourcc("y210"):
+      case fourcc("v410"):
+      case fourcc("v210"):
+      case fourcc("i420"):
+      case fourcc("nv12"):
+      case fourcc("nv21"):
+      case fourcc("yu22"):
+      case fourcc("yv22"):
+      case fourcc("yv20"):
+        break;
+      default:
+        return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Unknown uncC v1 profile"};
     }
   } else if (get_version() == 0) {

@@ -414,6 +417,289 @@ Error Box_uncC::write(StreamWriter& writer) const
 }


+void fill_uncC_and_cmpd_from_profile(Box_uncC& uncC,
+                                     std::shared_ptr<Box_cmpd>& cmpd)
+{
+  if (uncC.get_version() != 1 || cmpd) {
+    return;
+  }
+
+  // Return cached synthetic cmpd if we already created one.
+  if (auto synthetic = uncC.get_synthetic_cmpd()) {
+    cmpd = synthetic;
+    return;
+  }
+
+  uint32_t profile = uncC.get_profile();
+  cmpd = std::make_shared<Box_cmpd>();
+
+  // Profiles from ISO/IEC 23001-17 Table 5.
+  // Format: {profile, [{component_type, bit_depth_minus_1}, ...], sampling_type, interleave_type}
+  // The implicit cmpd is the unique component_types in order of first appearance.
+  // The uncC component_index refers into that cmpd.
+
+  if (profile == fourcc("rgb3")) {
+    // {'rgb3', [{4,7},{5,7},{6,7}], 0, 1}
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_red});
+    cmpd->add_component({component_type_green});
+    cmpd->add_component({component_type_blue});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+  }
+  else if (profile == fourcc("rgba")) {
+    // {'rgba', [{4,7},{5,7},{6,7},{7,7}], 0, 1}
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    uncC.add_component({3, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_red});
+    cmpd->add_component({component_type_green});
+    cmpd->add_component({component_type_blue});
+    cmpd->add_component({component_type_alpha});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+  }
+  else if (profile == fourcc("abgr")) {
+    // {'abgr', [{7,7},{6,7},{5,7},{4,7}], 0, 1}
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    uncC.add_component({3, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_alpha});
+    cmpd->add_component({component_type_blue});
+    cmpd->add_component({component_type_green});
+    cmpd->add_component({component_type_red});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+  }
+  else if (profile == fourcc("2vuy")) {
+    // {'2vuy', [{2,7},{1,7},{3,7},{1,7}], 1, 5}  Cb Y0 Cr Y1
+    // cmpd: Cb(0) Y(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+  }
+  else if (profile == fourcc("yuv2")) {
+    // {'yuv2', [{1,7},{2,7},{1,7},{3,7}], 1, 5}  Y0 Cb Y1 Cr
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+  }
+  else if (profile == fourcc("yvyu")) {
+    // {'yvyu', [{1,7},{3,7},{1,7},{2,7}], 1, 5}  Y0 Cr Y1 Cb
+    // cmpd: Y(0) Cr(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+  }
+  else if (profile == fourcc("vyuy")) {
+    // {'vyuy', [{3,7},{1,7},{2,7},{1,7}], 1, 5}  Cr Y0 Cb Y1
+    // cmpd: Cr(0) Y(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+  }
+  else if (profile == fourcc("yuv1")) {
+    // {'yuv1', [{1,7},{1,7},{2,7},{1,7},{1,7},{3,7}], 3, 5}  Y0 Y1 Cb Y2 Y3 Cr
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_411);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+  }
+  else if (profile == fourcc("v308")) {
+    // {'v308', [{3,7},{1,7},{2,7}], 0, 1}  Cr Y Cb
+    // cmpd: Cr(0) Y(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+  }
+  else if (profile == fourcc("v408")) {
+    // {'v408', [{2,7},{1,7},{3,7},{7,7}], 0, 1}  Cb Y Cr A
+    // cmpd: Cb(0) Y(1) Cr(2) alpha(3)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    uncC.add_component({3, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_alpha});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+  }
+  else if (profile == fourcc("y210")) {
+    // {'y210', [{1,9},{2,9},{1,9},{3,9}], 1, 5}  Y0 Cb Y1 Cr
+    // block_size=2, block_little_endian=1, block_pad_lsb=1
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 10, component_format_unsigned, 0});
+    uncC.add_component({1, 10, component_format_unsigned, 0});
+    uncC.add_component({0, 10, component_format_unsigned, 0});
+    uncC.add_component({2, 10, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+    uncC.set_block_size(2);
+    uncC.set_block_little_endian(true);
+    uncC.set_block_pad_lsb(true);
+  }
+  else if (profile == fourcc("v410")) {
+    // {'v410', [{2,9},{1,9},{3,9}], 0, 1}  Cb Y Cr
+    // block_size=4, block_little_endian=1, block_pad_lsb=1, block_reversed=1
+    // cmpd: Cb(0) Y(1) Cr(2)
+    uncC.add_component({0, 10, component_format_unsigned, 0});
+    uncC.add_component({1, 10, component_format_unsigned, 0});
+    uncC.add_component({2, 10, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_no_subsampling);
+    uncC.set_interleave_type(interleave_mode_pixel);
+    uncC.set_block_size(4);
+    uncC.set_block_little_endian(true);
+    uncC.set_block_pad_lsb(true);
+    uncC.set_block_reversed(true);
+  }
+  else if (profile == fourcc("v210")) {
+    // {'v210', [{2,9},{1,9},{3,9},{1,9}], 1, 5}  Cb Y0 Cr Y1
+    // block_size=4, block_little_endian=1, block_reversed=1
+    // cmpd: Cb(0) Y(1) Cr(2)
+    uncC.add_component({0, 10, component_format_unsigned, 0});
+    uncC.add_component({1, 10, component_format_unsigned, 0});
+    uncC.add_component({2, 10, component_format_unsigned, 0});
+    uncC.add_component({1, 10, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_multi_y);
+    uncC.set_block_size(4);
+    uncC.set_block_little_endian(true);
+    uncC.set_block_reversed(true);
+  }
+  else if (profile == fourcc("i420")) {
+    // {'i420', [{1,7},{2,7},{3,7}], 2, 0}  planar YCbCr
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_420);
+    uncC.set_interleave_type(interleave_mode_component);
+  }
+  else if (profile == fourcc("nv12")) {
+    // {'nv12', [{1,7},{2,7},{3,7}], 2, 2}  semi-planar YCbCr
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_420);
+    uncC.set_interleave_type(interleave_mode_mixed);
+  }
+  else if (profile == fourcc("nv21")) {
+    // {'nv21', [{1,7},{3,7},{2,7}], 2, 2}  semi-planar YCrCb
+    // cmpd: Y(0) Cr(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_420);
+    uncC.set_interleave_type(interleave_mode_mixed);
+  }
+  else if (profile == fourcc("yu22")) {
+    // {'yu22', [{1,7},{2,7},{3,7}], 1, 0}  planar YCbCr
+    // cmpd: Y(0) Cb(1) Cr(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cb});
+    cmpd->add_component({component_type_Cr});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_component);
+  }
+  else if (profile == fourcc("yv22")) {
+    // {'yv22', [{1,7},{3,7},{2,7}], 1, 0}  planar YCrCb
+    // cmpd: Y(0) Cr(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_422);
+    uncC.set_interleave_type(interleave_mode_component);
+  }
+  else if (profile == fourcc("yv20")) {
+    // {'yv20', [{1,7},{3,7},{2,7}], 2, 0}  planar YCrCb
+    // cmpd: Y(0) Cr(1) Cb(2)
+    uncC.add_component({0, 8, component_format_unsigned, 0});
+    uncC.add_component({1, 8, component_format_unsigned, 0});
+    uncC.add_component({2, 8, component_format_unsigned, 0});
+    cmpd->add_component({component_type_Y});
+    cmpd->add_component({component_type_Cr});
+    cmpd->add_component({component_type_Cb});
+    uncC.set_sampling_type(sampling_mode_420);
+    uncC.set_interleave_type(interleave_mode_component);
+  }
+  else {
+    cmpd.reset();
+    return;
+  }
+
+  uncC.set_synthetic_cmpd(cmpd);
+}
+
+
 Error Box_cmpC::parse(BitstreamRange& range, const heif_security_limits* limits)
 {
   parse_full_box_header(range);
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index 4a6ab69a..c496f308 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -216,6 +216,10 @@ public:

   uint32_t get_number_of_tiles() const { return m_num_tile_rows * m_num_tile_rows; }

+  std::shared_ptr<Box_cmpd> get_synthetic_cmpd() const { return m_synthetic_cmpd; }
+
+  void set_synthetic_cmpd(std::shared_ptr<Box_cmpd> cmpd) { m_synthetic_cmpd = std::move(cmpd); }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits* limits) override;

@@ -235,6 +239,8 @@ protected:
   uint32_t m_tile_align_size = 0;
   uint32_t m_num_tile_cols = 1;
   uint32_t m_num_tile_rows = 1;
+
+  std::shared_ptr<Box_cmpd> m_synthetic_cmpd;
 };


@@ -376,6 +382,10 @@ protected:
 };


+void fill_uncC_and_cmpd_from_profile(Box_uncC& uncC,
+                                     std::shared_ptr<Box_cmpd>& cmpd);
+
+
 class Box_uncv : public Box_VisualSampleEntry
 {
 public:
diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index 194b480b..73de7112 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -488,8 +488,13 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte
 void UncompressedImageCodec::unci_properties::fill_from_image_item(const std::shared_ptr<const ImageItem>& image)
 {
   ispe = image->get_property<Box_ispe>();
-  cmpd = image->get_property<Box_cmpd>();
-  uncC = image->get_property<Box_uncC>();
+  auto cmpd_mut = image->get_property<Box_cmpd>();
+  auto uncC_mut = image->get_property<Box_uncC>();
+  if (uncC_mut) {
+    fill_uncC_and_cmpd_from_profile(*uncC_mut, cmpd_mut);
+  }
+  cmpd = cmpd_mut;
+  uncC = uncC_mut;
   cmpC = image->get_property<Box_cmpC>();
   icef = image->get_property<Box_icef>();
 }
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index 5151e00b..fbded5b8 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -349,6 +349,8 @@ Error ImageItem_uncompressed::initialize_decoder()
                  "No 'uncC' box found."};
   }

+  fill_uncC_and_cmpd_from_profile(*uncC, cmpd);
+
   m_decoder = std::make_shared<Decoder_uncompressed>(uncC, cmpd, ispe);

   return Error::Ok;