Commit 92a4a312 for libheif

commit 92a4a31209fe8a8d8454dbaab993a7abd99b2176
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Feb 26 01:17:45 2026 +0100

    unci: implement writing filter-array images. Add heif-gen-bayer example to encode PNG as filter-array

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index f6e76805..9eb96b7b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -75,6 +75,14 @@ if (WITH_HEADER_COMPRESSION)
 endif ()


+if (PNG_FOUND)
+    add_executable(heif-gen-bayer
+            heif_gen_bayer.cc)
+    target_link_libraries(heif-gen-bayer PRIVATE heif heifio)
+    target_include_directories(heif-gen-bayer PRIVATE ${libheif_SOURCE_DIR})
+endif()
+
+
 if (WITH_EXAMPLE_HEIF_THUMB AND PNG_FOUND)
     add_executable(heif-thumbnailer ${getopt_sources}
             heif_thumbnailer.cc
diff --git a/examples/heif_gen_bayer.cc b/examples/heif_gen_bayer.cc
new file mode 100644
index 00000000..adc17e6e
--- /dev/null
+++ b/examples/heif_gen_bayer.cc
@@ -0,0 +1,357 @@
+/*
+  libheif example application "heif-gen-bayer".
+
+  MIT License
+
+  Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <getopt.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libheif/heif.h>
+#include <libheif/heif_uncompressed.h>
+
+#include "heifio/decoder_png.h"
+
+
+struct PatternDefinition
+{
+  const char* name;
+  uint16_t width;
+  uint16_t height;
+  std::vector<heif_bayer_pattern_pixel> cpat;
+};
+
+
+static const PatternDefinition patterns[] = {
+  // RGGB (standard Bayer)
+  //   R G
+  //   G B
+  {
+    "rggb", 2, 2,
+    {
+      {heif_uncompressed_component_type_red,   1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+    }
+  },
+
+  // GBRG
+  //   G B
+  //   R G
+  {
+    "gbrg", 2, 2,
+    {
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+    }
+  },
+
+  // RGBW (Red-Green-Blue-White) — 4×4
+  //   W G W R
+  //   G W B W
+  //   W B W G
+  //   R W G W
+  // White is an unfiltered (panchromatic) pixel → Y component type.
+  {
+    "rgbw", 4, 4,
+    {
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+
+      {heif_uncompressed_component_type_red,   1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_Y,     1.0f},
+    }
+  },
+
+  // QBC (Quad Bayer Coding) — 4×4
+  //   G G R R
+  //   G G R R
+  //   B B G G
+  //   B B G G
+  {
+    "qbc", 4, 4,
+    {
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+      {heif_uncompressed_component_type_red,   1.0f},
+
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_blue,  1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+      {heif_uncompressed_component_type_green, 1.0f},
+    }
+  },
+};
+
+static constexpr int num_patterns = sizeof(patterns) / sizeof(patterns[0]);
+
+
+static const PatternDefinition* find_pattern(const char* name)
+{
+  for (int i = 0; i < num_patterns; i++) {
+    if (strcasecmp(patterns[i].name, name) == 0) {
+      return &patterns[i];
+    }
+  }
+  return nullptr;
+}
+
+
+static void print_usage()
+{
+  std::cerr << "Usage: heif-gen-bayer [options] <input.png> <output.heif>\n\n"
+            << "Options:\n"
+            << "  -h, --help              show this help\n"
+            << "  -p, --pattern <name>    filter array pattern (default: rggb)\n\n"
+            << "Patterns:\n";
+  for (int i = 0; i < num_patterns; i++) {
+    std::cerr << "  " << patterns[i].name
+              << " (" << patterns[i].width << "x" << patterns[i].height << ")"
+              << (i == 0 ? "  [default]" : "")
+              << "\n";
+  }
+}
+
+
+static struct option long_options[] = {
+    {(char* const) "help",    no_argument,       nullptr, 'h'},
+    {(char* const) "pattern", required_argument, nullptr, 'p'},
+    {nullptr, 0, nullptr, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+  const PatternDefinition* pat = &patterns[0]; // default: RGGB
+
+  while (true) {
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "hp:", long_options, &option_index);
+    if (c == -1)
+      break;
+
+    switch (c) {
+      case 'h':
+        print_usage();
+        return 0;
+
+      case 'p':
+        pat = find_pattern(optarg);
+        if (!pat) {
+          std::cerr << "Unknown pattern: " << optarg << "\n";
+          print_usage();
+          return 1;
+        }
+        break;
+
+      default:
+        print_usage();
+        return 1;
+    }
+  }
+
+  if (argc - optind != 2) {
+    print_usage();
+    return 1;
+  }
+
+  const char* input_filename = argv[optind];
+  const char* output_filename = argv[optind + 1];
+
+  // --- Load PNG
+
+  InputImage input_image;
+  heif_error err = loadPNG(input_filename, 8, &input_image);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot load PNG: " << err.message << "\n";
+    return 1;
+  }
+
+  heif_image* src_img = input_image.image.get();
+
+  int width = heif_image_get_primary_width(src_img);
+  int height = heif_image_get_primary_height(src_img);
+
+  int bpp = heif_image_get_bits_per_pixel_range(src_img, heif_channel_interleaved);
+  if (bpp != 8) {
+    std::cerr << "Only 8-bit PNG input is supported. Got " << bpp << " bits per pixel.\n";
+    return 1;
+  }
+
+  if (width % pat->width != 0 || height % pat->height != 0) {
+    std::cerr << "Image dimensions must be multiples of the pattern size ("
+              << pat->width << "x" << pat->height << "). Got "
+              << width << "x" << height << "\n";
+    return 1;
+  }
+
+  // --- Get source RGB data
+
+  int src_stride;
+  const uint8_t* src_data = heif_image_get_plane_readonly(src_img, heif_channel_interleaved, &src_stride);
+  if (!src_data) {
+    std::cerr << "Failed to get interleaved RGB plane from PNG.\n";
+    return 1;
+  }
+
+  // --- Create Bayer image
+
+  heif_image* bayer_img = nullptr;
+  err = heif_image_create(width, height,
+                          heif_colorspace_monochrome,
+                          heif_chroma_monochrome,
+                          &bayer_img);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot create image: " << err.message << "\n";
+    return 1;
+  }
+
+  err = heif_image_add_plane(bayer_img, heif_channel_filter_array, width, height, 8);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot add plane: " << err.message << "\n";
+    heif_image_release(bayer_img);
+    return 1;
+  }
+
+  int dst_stride;
+  uint8_t* dst_data = heif_image_get_plane(bayer_img, heif_channel_filter_array, &dst_stride);
+
+  // --- Convert RGB to filter array using the selected pattern
+
+  for (int y = 0; y < height; y++) {
+    const uint8_t* src_row = src_data + y * src_stride;
+    uint8_t* dst_row = dst_data + y * dst_stride;
+
+    for (int x = 0; x < width; x++) {
+      uint8_t r = src_row[x * 3 + 0];
+      uint8_t g = src_row[x * 3 + 1];
+      uint8_t b = src_row[x * 3 + 2];
+
+      int px = x % pat->width;
+      int py = y % pat->height;
+      uint16_t comp_type = pat->cpat[py * pat->width + px].component_type;
+
+      switch (comp_type) {
+        case heif_uncompressed_component_type_red:   dst_row[x] = r; break;
+        case heif_uncompressed_component_type_green: dst_row[x] = g; break;
+        case heif_uncompressed_component_type_blue:  dst_row[x] = b; break;
+        case heif_uncompressed_component_type_Y: dst_row[x] = static_cast<uint8_t>((r + g + b) / 3); break; // Y / white
+        default:
+          assert(false);
+      }
+    }
+  }
+
+  // --- Set Bayer pattern metadata
+
+  err = heif_image_set_bayer_pattern(bayer_img,
+                                     pat->width, pat->height,
+                                     pat->cpat.data());
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot set Bayer pattern: " << err.message << "\n";
+    heif_image_release(bayer_img);
+    return 1;
+  }
+
+  // --- Encode
+
+  heif_context* ctx = heif_context_alloc();
+
+  heif_encoder* encoder = nullptr;
+  err = heif_context_get_encoder_for_format(ctx, heif_compression_uncompressed, &encoder);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot get uncompressed encoder: " << err.message << "\n";
+    heif_image_release(bayer_img);
+    heif_context_free(ctx);
+    return 1;
+  }
+
+  heif_encoding_options* options = heif_encoding_options_alloc();
+
+  heif_image_handle* handle = nullptr;
+  err = heif_context_encode_image(ctx, bayer_img, encoder, options, &handle);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot encode image: " << err.message << "\n";
+    heif_encoding_options_free(options);
+    heif_encoder_release(encoder);
+    heif_image_release(bayer_img);
+    heif_context_free(ctx);
+    return 1;
+  }
+
+  heif_encoding_options_free(options);
+  heif_encoder_release(encoder);
+  heif_image_handle_release(handle);
+  heif_image_release(bayer_img);
+
+  // --- Write file
+
+  err = heif_context_write_to_file(ctx, output_filename);
+  if (err.code != heif_error_Ok) {
+    std::cerr << "Cannot write file: " << err.message << "\n";
+    heif_context_free(ctx);
+    return 1;
+  }
+
+  heif_context_free(ctx);
+
+  std::cout << "Wrote " << pat->name << " ("
+            << pat->width << "x" << pat->height
+            << ") Bayer image to " << output_filename << "\n";
+
+  return 0;
+}
diff --git a/libheif/api/libheif/heif_uncompressed.cc b/libheif/api/libheif/heif_uncompressed.cc
index 0064ab35..c9b21f98 100644
--- a/libheif/api/libheif/heif_uncompressed.cc
+++ b/libheif/api/libheif/heif_uncompressed.cc
@@ -21,6 +21,7 @@
 #include "heif_uncompressed.h"
 #include "context.h"
 #include "api_structs.h"
+#include "pixelimage.h"
 #include "image-items/unc_image.h"

 #include <array>
@@ -28,6 +29,82 @@
 #include <algorithm>


+heif_error heif_image_set_bayer_pattern(heif_image* image,
+                                        uint16_t pattern_width,
+                                        uint16_t pattern_height,
+                                        const struct heif_bayer_pattern_pixel* patternPixels)
+{
+  if (image == nullptr || patternPixels == nullptr) {
+    return heif_error_null_pointer_argument;
+  }
+
+  if (pattern_width == 0 || pattern_height == 0) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Bayer pattern dimensions must be non-zero."};
+  }
+
+  BayerPattern pattern;
+  pattern.pattern_width = pattern_width;
+  pattern.pattern_height = pattern_height;
+
+  size_t num_pixels = size_t{pattern_width} * pattern_height;
+  pattern.pixels.assign(patternPixels, patternPixels + num_pixels);
+
+  image->image->set_bayer_pattern(pattern);
+
+  return heif_error_success;
+}
+
+
+int heif_image_has_bayer_pattern(const heif_image* image,
+                                 uint16_t* out_pattern_width,
+                                 uint16_t* out_pattern_height)
+{
+  if (image == nullptr || !image->image->has_bayer_pattern()) {
+    if (out_pattern_width) {
+      *out_pattern_width = 0;
+    }
+    if (out_pattern_height) {
+      *out_pattern_height = 0;
+    }
+    return 0;
+  }
+
+  const BayerPattern& pattern = image->image->get_bayer_pattern();
+
+  if (out_pattern_width) {
+    *out_pattern_width = pattern.pattern_width;
+  }
+  if (out_pattern_height) {
+    *out_pattern_height = pattern.pattern_height;
+  }
+
+  return 1;
+}
+
+
+heif_error heif_image_get_bayer_pattern(const heif_image* image,
+                                        struct heif_bayer_pattern_pixel* out_patternPixels)
+{
+  if (image == nullptr || out_patternPixels == nullptr) {
+    return heif_error_null_pointer_argument;
+  }
+
+  if (!image->image->has_bayer_pattern()) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Image does not have a Bayer pattern."};
+  }
+
+  const BayerPattern& pattern = image->image->get_bayer_pattern();
+  size_t num_pixels = size_t{pattern.pattern_width} * pattern.pattern_height;
+  std::copy(pattern.pixels.begin(), pattern.pixels.begin() + num_pixels, out_patternPixels);
+
+  return heif_error_success;
+}
+
+
 heif_unci_image_parameters* heif_unci_image_parameters_alloc()
 {
   auto* params = new heif_unci_image_parameters();
diff --git a/libheif/api/libheif/heif_uncompressed.h b/libheif/api/libheif/heif_uncompressed.h
index 427c7a1f..5273f8c3 100644
--- a/libheif/api/libheif/heif_uncompressed.h
+++ b/libheif/api/libheif/heif_uncompressed.h
@@ -34,6 +34,66 @@ extern "C" {
  *        See heif_metadata_compression for more information.
  */

+// --- ISO 23001-17 component types (Table 1)
+
+enum heif_uncompressed_component_type
+{
+  heif_uncompressed_component_type_monochrome = 0,
+  heif_uncompressed_component_type_Y = 1,
+  heif_uncompressed_component_type_Cb = 2,
+  heif_uncompressed_component_type_Cr = 3,
+  heif_uncompressed_component_type_red = 4,
+  heif_uncompressed_component_type_green = 5,
+  heif_uncompressed_component_type_blue = 6,
+  heif_uncompressed_component_type_alpha = 7,
+  heif_uncompressed_component_type_depth = 8,
+  heif_uncompressed_component_type_disparity = 9,
+  heif_uncompressed_component_type_palette = 10,
+  heif_uncompressed_component_type_filter_array = 11,
+  heif_uncompressed_component_type_padded = 12,
+  heif_uncompressed_component_type_cyan = 13,
+  heif_uncompressed_component_type_magenta = 14,
+  heif_uncompressed_component_type_yellow = 15,
+  heif_uncompressed_component_type_key_black = 16
+};
+
+
+// --- Bayer / filter array pattern
+
+struct heif_bayer_pattern_pixel
+{
+  uint16_t component_type;  // one of heif_uncompressed_component_type values
+  float component_gain;
+};
+
+// Set a Bayer / filter array pattern on an image.
+// The pattern is a 2D array of component types with dimensions pattern_width x pattern_height.
+// The number of entries in patternPixels must be pattern_width * pattern_height.
+// The component_type values correspond to the ISO 23001-17 component types
+// (e.g. heif_uncompressed_component_type_red=4, heif_uncompressed_component_type_green=5, heif_uncompressed_component_type_blue=6).
+// The encoder resolves these component types to cmpd indices when writing the cpat box.
+LIBHEIF_API
+heif_error heif_image_set_bayer_pattern(heif_image*,
+                                        uint16_t pattern_width,
+                                        uint16_t pattern_height,
+                                        const struct heif_bayer_pattern_pixel* patternPixels);
+
+// Returns whether the image has a Bayer / filter array pattern.
+// If the image has a pattern, out_pattern_width and out_pattern_height are set.
+// Either output pointer may be NULL if the caller does not need that value.
+LIBHEIF_API
+int heif_image_has_bayer_pattern(const heif_image*,
+                                 uint16_t* out_pattern_width,
+                                 uint16_t* out_pattern_height);
+
+// Get the Bayer / filter array pattern pixels.
+// The caller must provide an array large enough for pattern_width * pattern_height entries
+// (use heif_image_has_bayer_pattern() to query the dimensions first).
+// Returns heif_error_Ok on success, or an error if no pattern is set.
+LIBHEIF_API
+heif_error heif_image_get_bayer_pattern(const heif_image*,
+                                        struct heif_bayer_pattern_pixel* out_patternPixels);
+
 // --- 'unci' images

 // This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index 67797b78..26396f6d 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -96,27 +96,27 @@ bool is_predefined_component_type(uint16_t type)
 {
   // check whether the component type can be mapped to heif_uncompressed_component_type and we have a name defined for
   // it in sNames_uncompressed_component_type.
-  return type <= component_type_max_valid;
+  return type <= heif_uncompressed_component_type_max_valid;
 }

 static std::map<heif_uncompressed_component_type, const char*> sNames_uncompressed_component_type{
-    {component_type_monochrome,   "monochrome"},
-    {component_type_Y,            "Y"},
-    {component_type_Cb,           "Cb"},
-    {component_type_Cr,           "Cr"},
-    {component_type_red,          "red"},
-    {component_type_green,        "green"},
-    {component_type_blue,         "blue"},
-    {component_type_alpha,        "alpha"},
-    {component_type_depth,        "depth"},
-    {component_type_disparity,    "disparity"},
-    {component_type_palette,      "palette"},
-    {component_type_filter_array, "filter-array"},
-    {component_type_padded,       "padded"},
-    {component_type_cyan,         "cyan"},
-    {component_type_magenta,      "magenta"},
-    {component_type_yellow,       "yellow"},
-    {component_type_key_black,    "key (black)"}
+    {heif_uncompressed_component_type_monochrome,   "monochrome"},
+    {heif_uncompressed_component_type_Y,            "Y"},
+    {heif_uncompressed_component_type_Cb,           "Cb"},
+    {heif_uncompressed_component_type_Cr,           "Cr"},
+    {heif_uncompressed_component_type_red,          "red"},
+    {heif_uncompressed_component_type_green,        "green"},
+    {heif_uncompressed_component_type_blue,         "blue"},
+    {heif_uncompressed_component_type_alpha,        "alpha"},
+    {heif_uncompressed_component_type_depth,        "depth"},
+    {heif_uncompressed_component_type_disparity,    "disparity"},
+    {heif_uncompressed_component_type_palette,      "palette"},
+    {heif_uncompressed_component_type_filter_array, "filter-array"},
+    {heif_uncompressed_component_type_padded,       "padded"},
+    {heif_uncompressed_component_type_cyan,         "cyan"},
+    {heif_uncompressed_component_type_magenta,      "magenta"},
+    {heif_uncompressed_component_type_yellow,       "yellow"},
+    {heif_uncompressed_component_type_key_black,    "key (black)"}
 };

 template <typename T> const char* get_name(T val, const std::map<T, const char*>& table)
@@ -444,9 +444,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_red});
+    cmpd->add_component({heif_uncompressed_component_type_green});
+    cmpd->add_component({heif_uncompressed_component_type_blue});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
   }
@@ -456,10 +456,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_red});
+    cmpd->add_component({heif_uncompressed_component_type_green});
+    cmpd->add_component({heif_uncompressed_component_type_blue});
+    cmpd->add_component({heif_uncompressed_component_type_alpha});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
   }
@@ -469,10 +469,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_alpha});
+    cmpd->add_component({heif_uncompressed_component_type_blue});
+    cmpd->add_component({heif_uncompressed_component_type_green});
+    cmpd->add_component({heif_uncompressed_component_type_red});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
   }
@@ -483,9 +483,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
   }
@@ -496,9 +496,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
   }
@@ -509,9 +509,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
   }
@@ -522,9 +522,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
   }
@@ -537,9 +537,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_411);
     uncC->set_interleave_type(interleave_mode_multi_y);
   }
@@ -549,9 +549,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
   }
@@ -562,10 +562,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_alpha});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
   }
@@ -577,9 +577,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
     uncC->set_block_size(2);
@@ -593,9 +593,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_no_subsampling);
     uncC->set_interleave_type(interleave_mode_pixel);
     uncC->set_block_size(4);
@@ -611,9 +611,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_multi_y);
     uncC->set_block_size(4);
@@ -626,9 +626,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_420);
     uncC->set_interleave_type(interleave_mode_component);
   }
@@ -638,9 +638,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_420);
     uncC->set_interleave_type(interleave_mode_mixed);
   }
@@ -650,9 +650,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_420);
     uncC->set_interleave_type(interleave_mode_mixed);
   }
@@ -662,9 +662,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_component);
   }
@@ -674,9 +674,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_422);
     uncC->set_interleave_type(interleave_mode_component);
   }
@@ -686,9 +686,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
     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});
+    cmpd->add_component({heif_uncompressed_component_type_Y});
+    cmpd->add_component({heif_uncompressed_component_type_Cr});
+    cmpd->add_component({heif_uncompressed_component_type_Cb});
     uncC->set_sampling_type(sampling_mode_420);
     uncC->set_interleave_type(interleave_mode_component);
   }
@@ -956,31 +956,30 @@ Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits)
     return unsupported_version_error("cpat");
   }

-  m_pattern.m_pattern_width = range.read16();
-  m_pattern.m_pattern_height = range.read16();
+  m_pattern.pattern_width = range.read16();
+  m_pattern.pattern_height = range.read16();

-  if (m_pattern.m_pattern_width == 0 || m_pattern.m_pattern_height == 0) {
+  if (m_pattern.pattern_width == 0 || m_pattern.pattern_height == 0) {
     return {heif_error_Invalid_input,
             heif_suberror_Invalid_parameter_value,
             "Zero Bayer pattern size."};
   }

   auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels;
-  if (max_bayer_pattern_size && m_pattern.m_pattern_height > max_bayer_pattern_size / m_pattern.m_pattern_width) {
+  if (max_bayer_pattern_size && m_pattern.pattern_height > max_bayer_pattern_size / m_pattern.pattern_width) {
     return {heif_error_Invalid_input,
             heif_suberror_Security_limit_exceeded,
             "Maximum Bayer pattern size exceeded."};
   }

-  m_pattern.m_components.resize(size_t{m_pattern.m_pattern_width} * m_pattern.m_pattern_height);
+  size_t num_pixels = size_t{m_pattern.pattern_width} * m_pattern.pattern_height;
+  m_pattern.pixels.resize(num_pixels);

-  for (uint16_t i = 0; i < m_pattern.m_pattern_height; i++) {
-    for (uint16_t j = 0; j < m_pattern.m_pattern_width; j++) {
-      BayerPattern::PatternComponent component{};
-      component.component_index = range.read32();
-      component.component_gain = range.read_float32();
-      m_pattern.m_components[i] = component;
-    }
+  for (size_t i = 0; i < num_pixels; i++) {
+    heif_bayer_pattern_pixel pixel{};
+    pixel.component_type = static_cast<uint16_t>(range.read32());
+    pixel.component_gain = range.read_float32();
+    m_pattern.pixels[i] = pixel;
   }

   return range.get_error();
@@ -995,8 +994,8 @@ std::string Box_cpat::dump(Indent& indent) const
   sstr << indent << "pattern_width: " << get_pattern_width() << "\n";
   sstr << indent << "pattern_height: " << get_pattern_height() << "\n";

-  for (const auto& component : m_pattern.m_components) {
-    sstr << indent << "component index: " << component.component_index << ", gain: " << component.component_gain << "\n";
+  for (const auto& pixel : m_pattern.pixels) {
+    sstr << indent << "component index: " << pixel.component_type << ", gain: " << pixel.component_gain << "\n";
   }
   return sstr.str();
 }
@@ -1006,28 +1005,21 @@ Error Box_cpat::write(StreamWriter& writer) const
 {
   size_t box_start = reserve_box_header_space(writer);

-  if (m_pattern.m_pattern_width * size_t{m_pattern.m_pattern_height} != m_pattern.m_components.size()) {
-    // needs to be rectangular
+  if (m_pattern.pattern_width * size_t{m_pattern.pattern_height} != m_pattern.pixels.size()) {
     return {heif_error_Usage_error,
             heif_suberror_Invalid_parameter_value,
             "incorrect number of pattern components"};
   }

-  writer.write16(m_pattern.m_pattern_width);
-  writer.write16(m_pattern.m_pattern_height);
+  writer.write16(m_pattern.pattern_width);
+  writer.write16(m_pattern.pattern_height);

-  for (const auto& component : m_pattern.m_components) {
-    writer.write32(component.component_index);
-    writer.write_float32(component.component_gain);
+  for (const auto& pixel : m_pattern.pixels) {
+    writer.write32(pixel.component_type);
+    writer.write_float32(pixel.component_gain);
   }

   prepend_header(writer, box_start);

   return Error::Ok;
 }
-
-
-void Box_cpat::set_bayer_pattern(const BayerPattern& pattern)
-{
-  m_pattern = pattern;
-}
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index 85d7e803..013f357d 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -24,6 +24,7 @@

 #include "box.h"
 #include "bitstream.h"
+#include "pixelimage.h"
 #include "unc_types.h"
 #include "sequences/seq_boxes.h"

@@ -354,19 +355,13 @@ public:
     set_short_type(fourcc("cpat"));
   }

-  uint16_t get_pattern_width() const
-  {
-    return m_pattern.m_pattern_width;
-  }
+  uint16_t get_pattern_width() const { return m_pattern.pattern_width; }

-  uint16_t get_pattern_height() const
-  {
-    return m_pattern.m_pattern_height;
-  }
+  uint16_t get_pattern_height() const { return m_pattern.pattern_height; }

-  const BayerPattern& get_bayer_pattern() const { return m_pattern; }
+  const BayerPattern& get_pattern() const { return m_pattern; }

-  void set_bayer_pattern(const BayerPattern& pattern);
+  void set_pattern(const BayerPattern& pattern) { m_pattern = pattern; }

   std::string dump(Indent&) const override;

diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index b3253a86..76ea42bf 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -87,27 +87,27 @@ Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr
     uint16_t component_index = component.component_index;
     uint16_t component_type = cmpd->get_components()[component_index].component_type;

-    if (component_type > component_type_max_valid) {
+    if (component_type > heif_uncompressed_component_type_max_valid) {
       std::stringstream sstr;
-      sstr << "a component_type > " << component_type_max_valid << " is not supported";
+      sstr << "a component_type > " << heif_uncompressed_component_type_max_valid << " is not supported";
       return {heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()};
     }
-    if (component_type == component_type_padded) {
+    if (component_type == heif_uncompressed_component_type_padded) {
       // not relevant for determining chroma
       continue;
     }
     componentSet |= (1 << component_type);
   }

-  *out_has_alpha = (componentSet & (1 << component_type_alpha)) != 0;
+  *out_has_alpha = (componentSet & (1 << heif_uncompressed_component_type_alpha)) != 0;

-  if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) ||
-      componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) {
+  if (componentSet == ((1 << heif_uncompressed_component_type_red) | (1 << heif_uncompressed_component_type_green) | (1 << heif_uncompressed_component_type_blue)) ||
+      componentSet == ((1 << heif_uncompressed_component_type_red) | (1 << heif_uncompressed_component_type_green) | (1 << heif_uncompressed_component_type_blue) | (1 << heif_uncompressed_component_type_alpha))) {
     *out_chroma = heif_chroma_444;
     *out_colourspace = heif_colorspace_RGB;
   }

-  if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) {
+  if (componentSet == ((1 << heif_uncompressed_component_type_Y) | (1 << heif_uncompressed_component_type_Cb) | (1 << heif_uncompressed_component_type_Cr))) {
     switch (uncC->get_sampling_type()) {
       case sampling_mode_no_subsampling:
         *out_chroma = heif_chroma_444;
@@ -122,15 +122,16 @@ Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr
     *out_colourspace = heif_colorspace_YCbCr;
   }

-  if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) {
+  if (componentSet == ((1 << heif_uncompressed_component_type_monochrome)) || componentSet == ((1 << heif_uncompressed_component_type_monochrome) | (1 << heif_uncompressed_component_type_alpha))) {
     // mono or mono + alpha input, mono output.
     *out_chroma = heif_chroma_monochrome;
     *out_colourspace = heif_colorspace_monochrome;
   }

-  if (componentSet == (1 << component_type_filter_array)) {
-    *out_chroma = heif_chroma_monochrome; // TODO: ?
-    *out_colourspace = heif_colorspace_filter_array;
+  if (componentSet == (1 << heif_uncompressed_component_type_filter_array)) {
+    // TODO - we should look up the components
+    *out_chroma = heif_chroma_monochrome;
+    *out_colourspace = heif_colorspace_monochrome;
   }

   // TODO: more combinations
@@ -214,8 +215,8 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con

     auto component_type = components[component.component_index].component_type;

-    if ((component_type == heif_uncompressed_component_type::component_type_Cb) ||
-        (component_type == heif_uncompressed_component_type::component_type_Cr)) {
+    if ((component_type == heif_uncompressed_component_type_Cb) ||
+        (component_type == heif_uncompressed_component_type_Cr)) {
       Result<uint32_t> result = img->add_component((width / chroma_h_subsampling(chroma)),
                                                    (height / chroma_v_subsampling(chroma)),
                                                    component_type,
@@ -326,7 +327,7 @@ Error UncompressedImageCodec::check_header_validity(std::optional<const std::sha
       }

       uint16_t component_type = cmpd->get_components()[comp.component_index].component_type;
-      if (component_type > 7 && component_type != component_type_padded && component_type != component_type_filter_array) {
+      if (component_type > 7 && component_type != heif_uncompressed_component_type_padded && component_type != heif_uncompressed_component_type_filter_array) {
         std::stringstream sstr;
         sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet";
         return {heif_error_Unsupported_feature,
diff --git a/libheif/codecs/uncompressed/unc_dec.cc b/libheif/codecs/uncompressed/unc_dec.cc
index 49295730..194398c7 100644
--- a/libheif/codecs/uncompressed/unc_dec.cc
+++ b/libheif/codecs/uncompressed/unc_dec.cc
@@ -65,14 +65,14 @@ int Decoder_uncompressed::get_luma_bits_per_pixel() const
     }
     auto component_type = m_cmpd->get_components()[component_index].component_type;
     switch (component_type) {
-      case component_type_monochrome:
-      case component_type_red:
-      case component_type_green:
-      case component_type_blue:
-      case component_type_filter_array:
+      case heif_uncompressed_component_type_monochrome:
+      case heif_uncompressed_component_type_red:
+      case heif_uncompressed_component_type_green:
+      case heif_uncompressed_component_type_blue:
+      case heif_uncompressed_component_type_filter_array:
         alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth);
         break;
-      case component_type_Y:
+      case heif_uncompressed_component_type_Y:
         luma_bits = std::max(luma_bits, (int) component.component_bit_depth);
         break;
         // TODO: there are other things we'll need to handle eventually, like palette.
@@ -109,15 +109,15 @@ int Decoder_uncompressed::get_chroma_bits_per_pixel() const
     }
     auto component_type = m_cmpd->get_components()[component_index].component_type;
     switch (component_type) {
-      case component_type_monochrome:
-      case component_type_red:
-      case component_type_green:
-      case component_type_blue:
-      case component_type_filter_array:
+      case heif_uncompressed_component_type_monochrome:
+      case heif_uncompressed_component_type_red:
+      case heif_uncompressed_component_type_green:
+      case heif_uncompressed_component_type_blue:
+      case heif_uncompressed_component_type_filter_array:
         alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth);
         break;
-      case component_type_Cb:
-      case component_type_Cr:
+      case heif_uncompressed_component_type_Cb:
+      case heif_uncompressed_component_type_Cr:
         chroma_bits = std::max(chroma_bits, (int) component.component_bit_depth);
         break;
         // TODO: there are other things we'll need to handle eventually, like palette.
diff --git a/libheif/codecs/uncompressed/unc_decoder.cc b/libheif/codecs/uncompressed/unc_decoder.cc
index 5b287da2..78659e6b 100644
--- a/libheif/codecs/uncompressed/unc_decoder.cc
+++ b/libheif/codecs/uncompressed/unc_decoder.cc
@@ -455,7 +455,7 @@ Result<std::shared_ptr<HeifPixelImage> > unc_decoder::decode_full_image(
   auto img = *createImgResult;

   if (properties.cpat) {
-    img->set_bayer_pattern(properties.cpat->get_bayer_pattern());
+    img->set_bayer_pattern(properties.cpat->get_pattern());
   }

   auto decoderResult = unc_decoder_factory::get_unc_decoder(width, height, cmpd, uncC);
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index 060877fa..f5ccc0b5 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -35,22 +35,22 @@
 heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel)
 {
   switch (channel) {
-    case heif_channel_Y: return heif_uncompressed_component_type::component_type_Y;
-    case heif_channel_Cb: return heif_uncompressed_component_type::component_type_Cb;
-    case heif_channel_Cr: return heif_uncompressed_component_type::component_type_Cr;
-    case heif_channel_R: return heif_uncompressed_component_type::component_type_red;
-    case heif_channel_G: return heif_uncompressed_component_type::component_type_green;
-    case heif_channel_B: return heif_uncompressed_component_type::component_type_blue;
-    case heif_channel_Alpha: return heif_uncompressed_component_type::component_type_alpha;
+    case heif_channel_Y: return heif_uncompressed_component_type_Y;
+    case heif_channel_Cb: return heif_uncompressed_component_type_Cb;
+    case heif_channel_Cr: return heif_uncompressed_component_type_Cr;
+    case heif_channel_R: return heif_uncompressed_component_type_red;
+    case heif_channel_G: return heif_uncompressed_component_type_green;
+    case heif_channel_B: return heif_uncompressed_component_type_blue;
+    case heif_channel_Alpha: return heif_uncompressed_component_type_alpha;
     case heif_channel_interleaved: assert(false);
       break;
-    case heif_channel_filter_array: return heif_uncompressed_component_type::component_type_filter_array;
-    case heif_channel_depth: return heif_uncompressed_component_type::component_type_depth;
-    case heif_channel_disparity: return heif_uncompressed_component_type::component_type_disparity;
-    case heif_channel_unknown: return heif_uncompressed_component_type::component_type_padded;
+    case heif_channel_filter_array: return heif_uncompressed_component_type_filter_array;
+    case heif_channel_depth: return heif_uncompressed_component_type_depth;
+    case heif_channel_disparity: return heif_uncompressed_component_type_disparity;
+    case heif_channel_unknown: return heif_uncompressed_component_type_padded;
   }

-  return heif_uncompressed_component_type::component_type_padded;
+  return heif_uncompressed_component_type_padded;
 }


@@ -160,6 +160,10 @@ Result<Encoder::CodedImageData> unc_encoder::encode_static(const std::shared_ptr
     codedImageData.properties.push_back(this->get_cmpd());
   }

+  if (m_cpat) {
+    codedImageData.properties.push_back(m_cpat);
+  }
+

   // --- encode image

diff --git a/libheif/codecs/uncompressed/unc_encoder.h b/libheif/codecs/uncompressed/unc_encoder.h
index 099953ef..d9565ebf 100644
--- a/libheif/codecs/uncompressed/unc_encoder.h
+++ b/libheif/codecs/uncompressed/unc_encoder.h
@@ -31,6 +31,7 @@

 class Box_uncC;
 class Box_cmpd;
+class Box_cpat;
 class HeifPixelImage;

 heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel);
@@ -47,6 +48,7 @@ public:

   std::shared_ptr<Box_cmpd> get_cmpd() const { return m_cmpd; }
   std::shared_ptr<Box_uncC> get_uncC() const { return m_uncC; }
+  std::shared_ptr<Box_cpat> get_cpat() const { return m_cpat; }


   virtual uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const = 0;
@@ -62,6 +64,7 @@ public:
 protected:
   std::shared_ptr<Box_cmpd> m_cmpd;
   std::shared_ptr<Box_uncC> m_uncC;
+  std::shared_ptr<Box_cpat> m_cpat;
 };


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

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

 #include "pixelimage.h"
 #include "unc_boxes.h"
@@ -60,7 +62,7 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
     else {
       ch = image->get_component_channel(idx);
       if (ch == heif_channel_Y && !image->has_channel(heif_channel_Cb)) {
-        comp_type = component_type_monochrome;
+        comp_type = heif_uncompressed_component_type_monochrome;
       }
       else {
         comp_type = heif_channel_to_component_type(ch);
@@ -104,6 +106,43 @@ 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();
+
+    // 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) {
+      if (seen.insert(pixel.component_type).second) {
+        unique_types.push_back(pixel.component_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++) {
+      cpat_pattern.pixels[i].component_type = type_to_cmpd_index[bayer.pixels[i].component_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);
+  }
 }


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 02d24679..16be6a0e 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
@@ -58,9 +58,9 @@ 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)
 {
-  m_cmpd->add_component({component_type_red});
-  m_cmpd->add_component({component_type_green});
-  m_cmpd->add_component({component_type_blue});
+  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});

   uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);

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 a93750c3..d103abb4 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
@@ -58,14 +58,14 @@ 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)
 {
-  m_cmpd->add_component({component_type_red});
-  m_cmpd->add_component({component_type_green});
-  m_cmpd->add_component({component_type_blue});
+  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});

   bool save_alpha = image->has_alpha();

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

   m_bytes_per_pixel = save_alpha ? 8 : 6;
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
index 268e220d..6e2fc457 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
@@ -54,14 +54,14 @@ 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)
 {
-  m_cmpd->add_component({component_type_red});
-  m_cmpd->add_component({component_type_green});
-  m_cmpd->add_component({component_type_blue});
+  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});

   bool save_alpha = image->has_alpha();

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

   m_bytes_per_pixel = save_alpha ? 4 : 3;
diff --git a/libheif/codecs/uncompressed/unc_types.h b/libheif/codecs/uncompressed/unc_types.h
index de63f0da..7dfb16c0 100644
--- a/libheif/codecs/uncompressed/unc_types.h
+++ b/libheif/codecs/uncompressed/unc_types.h
@@ -23,107 +23,10 @@
 #define LIBHEIF_UNC_TYPES_H

 #include <cstdint>
-#include <vector>
+#include <libheif/heif_uncompressed.h>

-/**
- * Component type.
- *
- * See ISO/IEC 23001-17 Table 1.
-*/
-enum heif_uncompressed_component_type : uint16_t
-{
-  /**
-   * Monochrome component.
-   */
-  component_type_monochrome = 0,
-
-  /**
-   * Luma component (Y).
-   */
-  component_type_Y = 1,
-
-  /**
-   * Chroma component (Cb / U).
-   */
-  component_type_Cb = 2,
-
-  /**
-   * Chroma component (Cr / V).
-   */
-  component_type_Cr = 3,
-
-  /**
-   * Red component (R).
-   */
-  component_type_red = 4,
-
-  /**
-   * Green component (G).
-   */
-  component_type_green = 5,
-
-  /**
-   * Blue component (B).
-   */
-  component_type_blue = 6,
-
-  /**
-   * Alpha / transparency component (A).
-   */
-  component_type_alpha = 7,
-
-  /**
-   * Depth component (D).
-   */
-  component_type_depth = 8,
-
-  /**
-   * Disparity component (Disp).
-   */
-  component_type_disparity = 9,
-
-  /**
-   * Palette component (P).
-   *
-   * The {@code component_format} value for this component shall be 0.
-   */
-  component_type_palette = 10,
-
-  /**
-   * Filter Array (FA) component such as Bayer, RGBW, etc.
-   */
-  component_type_filter_array = 11,
-
-  /**
-   * Padded component (unused bits/bytes).
-   */
-  component_type_padded = 12,
-
-  /**
-   * Cyan component (C).
-   */
-  component_type_cyan = 13,
-
-  /**
-   * Magenta component (M).
-   */
-  component_type_magenta = 14,
-
-  /**
-   * Yellow component (Y).
-   */
-  component_type_yellow = 15,
-
-  /**
-   * Key (black) component (K).
-   */
-  component_type_key_black = 16,
-
-  /**
-   * Maximum valid component type value.
-   */
-  component_type_max_valid = component_type_key_black
-};
+// Update this when adding new values to heif_uncompressed_component_type.
+#define heif_uncompressed_component_type_max_valid heif_uncompressed_component_type_key_black

 /**
  * HEIF uncompressed component format.
@@ -288,18 +191,4 @@ enum heif_uncompressed_interleave_mode
 };


-struct BayerPattern
-{
-  struct PatternComponent
-  {
-    uint32_t component_index;
-    float component_gain;
-  };
-
-  uint16_t m_pattern_width = 0;
-  uint16_t m_pattern_height = 0;
-  std::vector<PatternComponent> m_components;
-};
-
-
 #endif //LIBHEIF_UNC_TYPES_H
diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index 232b6c20..0cc6c700 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -321,7 +321,10 @@ Result<Encoder::CodedImageData> ImageItem::encode_to_bitstream_and_boxes(const s
   // --- write PIXI property

   std::shared_ptr<Box_pixi> pixi = std::make_shared<Box_pixi>();
-  if (colorspace == heif_colorspace_monochrome) {
+  if (colorspace == heif_colorspace_monochrome && image->has_channel(heif_channel_filter_array)) {
+    // Skip pixi for filter array images — bit depth info is in uncC
+  }
+  else if (colorspace == heif_colorspace_monochrome) {
     pixi->add_channel_bits(image->get_bits_per_pixel(heif_channel_Y));
   }
   else if (colorspace == heif_colorspace_YCbCr) {
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index e482e6b1..89f2a69d 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -157,6 +157,11 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
   }


+  // Add cpat property if Bayer pattern is set
+  if ((*uncEncoder)->get_cpat()) {
+    unci_image->add_property((*uncEncoder)->get_cpat(), true);
+  }
+
   // Add `ispe` property

   auto ispe = std::make_shared<Box_ispe>();
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index 600c87b8..588e68d7 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -96,29 +96,29 @@ uint32_t channel_height(uint32_t h, heif_chroma chroma, heif_channel channel)
 heif_channel map_uncompressed_component_to_channel(uint16_t component_type)
 {
   switch (component_type) {
-    case component_type_monochrome:
-    case component_type_Y:
+    case heif_uncompressed_component_type_monochrome:
+    case heif_uncompressed_component_type_Y:
       return heif_channel_Y;
-    case component_type_Cb:
+    case heif_uncompressed_component_type_Cb:
       return heif_channel_Cb;
-    case component_type_Cr:
+    case heif_uncompressed_component_type_Cr:
       return heif_channel_Cr;
-    case component_type_red:
+    case heif_uncompressed_component_type_red:
       return heif_channel_R;
-    case component_type_green:
+    case heif_uncompressed_component_type_green:
       return heif_channel_G;
-    case component_type_blue:
+    case heif_uncompressed_component_type_blue:
       return heif_channel_B;
-    case component_type_alpha:
+    case heif_uncompressed_component_type_alpha:
       return heif_channel_Alpha;
-    case component_type_filter_array:
+    case heif_uncompressed_component_type_filter_array:
       return heif_channel_filter_array;
-    case component_type_depth:
+    case heif_uncompressed_component_type_depth:
       return heif_channel_depth;
-    case component_type_disparity:
+    case heif_uncompressed_component_type_disparity:
       return heif_channel_disparity;

-    case component_type_padded:
+    case heif_uncompressed_component_type_padded:
     default:
       return heif_channel_unknown;
   }
@@ -767,17 +767,17 @@ uint32_t HeifPixelImage::get_primary_component() const
     }

     switch (m_planes[idx].m_component_type) {
-      case component_type_Y:
-      case component_type_monochrome:
-      case component_type_red:
-      case component_type_green:
-      case component_type_blue:
-      case component_type_cyan:
-      case component_type_magenta:
-      case component_type_yellow:
-      case component_type_key_black:
-      case component_type_filter_array:
-      case component_type_palette:
+      case heif_uncompressed_component_type_Y:
+      case heif_uncompressed_component_type_monochrome:
+      case heif_uncompressed_component_type_red:
+      case heif_uncompressed_component_type_green:
+      case heif_uncompressed_component_type_blue:
+      case heif_uncompressed_component_type_cyan:
+      case heif_uncompressed_component_type_magenta:
+      case heif_uncompressed_component_type_yellow:
+      case heif_uncompressed_component_type_key_black:
+      case heif_uncompressed_component_type_filter_array:
+      case heif_uncompressed_component_type_palette:
         return idx;

       default:
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index 2d5fba0a..77f0c128 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -26,6 +26,7 @@
 #include "error.h"
 #include "nclx.h"
 #include <libheif/heif_experimental.h>
+#include <libheif/heif_uncompressed.h>
 #if HEIF_WITH_OMAF
 #include "omaf_boxes.h"
 #endif
@@ -34,12 +35,18 @@
 #include <vector>
 #include <memory>
 #include <map>
+#include <optional>
 #include <set>
 #include <utility>
 #include <cassert>
 #include <string>

-#include "codecs/uncompressed/unc_types.h"
+struct BayerPattern
+{
+  uint16_t pattern_width = 0;
+  uint16_t pattern_height = 0;
+  std::vector<heif_bayer_pattern_pixel> pixels;
+};

 heif_chroma chroma_from_subsampling(int h, int v);

@@ -182,6 +189,15 @@ public:

   std::string get_gimi_sample_content_id() const { assert(has_gimi_sample_content_id()); return *m_gimi_sample_content_id; }

+
+  // --- bayer pattern
+
+  bool has_bayer_pattern() const { return m_bayer_pattern.has_value(); }
+
+  const BayerPattern& get_bayer_pattern() const { assert(has_bayer_pattern()); return *m_bayer_pattern; }
+
+  virtual void set_bayer_pattern(const BayerPattern& pattern) { m_bayer_pattern = pattern; }
+
 #if HEIF_WITH_OMAF
   bool has_omaf_image_projection() const {
     return (m_omaf_image_projection != heif_omaf_image_projection_flat);
@@ -196,12 +212,6 @@ public:
   }
 #endif

-  void set_bayer_pattern(const BayerPattern& pattern) { m_bayer_pattern = pattern; }
-
-  bool has_bayer_pattern() const { return m_bayer_pattern.has_value(); }
-
-  const BayerPattern& get_bayer_pattern() const { assert(m_bayer_pattern); return *m_bayer_pattern; }
-
 private:
   bool m_premultiplied_alpha = false;
   nclx_profile m_color_profile_nclx = nclx_profile::undefined();
@@ -216,12 +226,12 @@ private:

   std::optional<std::string> m_gimi_sample_content_id;

+  std::optional<BayerPattern> m_bayer_pattern;
+
 #if HEIF_WITH_OMAF
   heif_omaf_image_projection m_omaf_image_projection = heif_omaf_image_projection::heif_omaf_image_projection_flat;
 #endif

-  std::optional<BayerPattern> m_bayer_pattern;
-
 protected:
   std::shared_ptr<Box_clli> get_clli_box() const;

diff --git a/libheif/sequences/track_visual.cc b/libheif/sequences/track_visual.cc
index 9be3d948..ee982a26 100644
--- a/libheif/sequences/track_visual.cc
+++ b/libheif/sequences/track_visual.cc
@@ -158,7 +158,7 @@ bool Track_Visual::has_alpha_channel() const
     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)) {
+          if (cmpd->has_component(heif_uncompressed_component_type_alpha)) {
             return true;
           }
         }