Commit 702f08a1 for libheif

commit 702f08a12dde0b443629e7a0ac531ca95896e537
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Feb 8 16:18:44 2026 +0100

    unci: extract unc_encoder_factory from unc_encoder

diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index 5a357d3b..a0c1d1f3 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -51,24 +51,31 @@ heif_uncompressed_component_format to_unc_component_format(heif_channel_datatype
 }


-Result<const unc_encoder*> unc_encoder::get_unc_encoder(const std::shared_ptr<const HeifPixelImage>& prototype_image,
-                                                        const heif_encoding_options& options)
+unc_encoder::unc_encoder()
 {
-  static unc_encoder_rgb3_rgba enc_rgb3_rgba;
-  static unc_encoder_rgb_hdr_packed_interleave enc_rgb10_12;
-  static unc_encoder_rrggbb enc_rrggbb;
-  static unc_encoder_planar enc_planar;
+  m_cmpd = std::make_shared<Box_cmpd>();
+  m_uncC = std::make_shared<Box_uncC>();
+}
+
+
+Result<std::unique_ptr<const unc_encoder> > unc_encoder_factory::get_unc_encoder(const std::shared_ptr<const HeifPixelImage>& prototype_image,
+                                                                                 const heif_encoding_options& options)
+{
+  static unc_encoder_factory_rgb3_rgba enc_rgb3_rgba;
+  static unc_encoder_factory_rgb_hdr_packed_interleave enc_rgb10_12;
+  static unc_encoder_factory_rrggbb enc_rrggbb;
+  static unc_encoder_factory_planar enc_planar;

-  static const unc_encoder* encoders[] {
+  static const unc_encoder_factory* encoders[]{
     &enc_rgb3_rgba,
     &enc_rgb10_12,
     &enc_rrggbb,
     &enc_planar
   };

-  for (const unc_encoder* enc : encoders) {
+  for (const unc_encoder_factory* enc : encoders) {
     if (enc->can_encode(prototype_image, options)) {
-      return {enc};
+      return {enc->create(prototype_image, options)};
     }
   }

@@ -80,7 +87,6 @@ Result<const unc_encoder*> unc_encoder::get_unc_encoder(const std::shared_ptr<co
 }


-
 heif_uncompressed_component_format to_unc_component_format(const std::shared_ptr<const HeifPixelImage>& image, heif_channel channel)
 {
   heif_channel_datatype datatype = image->get_datatype(channel);
@@ -90,9 +96,9 @@ heif_uncompressed_component_format to_unc_component_format(const std::shared_ptr


 Result<Encoder::CodedImageData> unc_encoder::encode_full_image(const std::shared_ptr<const HeifPixelImage>& src_image,
-                                                         const heif_encoding_options& options)
+                                                               const heif_encoding_options& options)
 {
-  auto uncEncoder = get_unc_encoder(src_image, options);
+  auto uncEncoder = unc_encoder_factory::get_unc_encoder(src_image, options);
   if (uncEncoder.error()) {
     return uncEncoder.error();
   }
@@ -122,21 +128,18 @@ Result<Encoder::CodedImageData> unc_encoder::encode_static(const std::shared_ptr

   // --- generate configuration property boxes

-  std::shared_ptr<Box_uncC> uncC = std::make_shared<Box_uncC>();
-  std::shared_ptr<Box_cmpd> cmpd = std::make_shared<Box_cmpd>();
-
-  this->fill_cmpd_and_uncC(cmpd, uncC, src_image, options);
+  auto uncC = this->get_uncC();

   Encoder::CodedImageData codedImageData;
   codedImageData.properties.push_back(uncC);
   if (!uncC->is_minimized()) {
-    codedImageData.properties.push_back(cmpd);
+    codedImageData.properties.push_back(this->get_cmpd());
   }


   // --- encode image

-  Result<std::vector<uint8_t> > codedBitstreamResult = encode_tile(src_image, options);
+  Result<std::vector<uint8_t> > codedBitstreamResult = this->encode_tile(src_image);
   if (!codedBitstreamResult) {
     return codedBitstreamResult.error();
   }
@@ -145,5 +148,3 @@ Result<Encoder::CodedImageData> unc_encoder::encode_static(const std::shared_ptr

   return codedImageData;
 }
-
-
diff --git a/libheif/codecs/uncompressed/unc_encoder.h b/libheif/codecs/uncompressed/unc_encoder.h
index 11ba02bd..6d500afa 100644
--- a/libheif/codecs/uncompressed/unc_encoder.h
+++ b/libheif/codecs/uncompressed/unc_encoder.h
@@ -34,28 +34,41 @@ class HeifPixelImage;
 class unc_encoder
 {
 public:
-  virtual ~unc_encoder() = default;
-
-  virtual bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
-                          const heif_encoding_options& options) const = 0;
+  unc_encoder();

-  virtual void fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& out_cmpd,
-                                  std::shared_ptr<Box_uncC>& out_uncC,
-                                  const std::shared_ptr<const HeifPixelImage>& image,
-                                  const heif_encoding_options& options) const = 0;
+  virtual ~unc_encoder() = default;

-  [[nodiscard]] virtual std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image,
-                                           const heif_encoding_options& options) const = 0;
+  std::shared_ptr<Box_cmpd> get_cmpd() const { return m_cmpd; }
+  std::shared_ptr<Box_uncC> get_uncC() const { return m_uncC; }


-  static Result<const unc_encoder*> get_unc_encoder(const std::shared_ptr<const HeifPixelImage>& prototype_image,
-                                                    const heif_encoding_options& options);
+  [[nodiscard]] virtual std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const = 0;

   Result<Encoder::CodedImageData> encode_static(const std::shared_ptr<const HeifPixelImage>& src_image,
                                                 const heif_encoding_options& options) const;

   static Result<Encoder::CodedImageData> encode_full_image(const std::shared_ptr<const HeifPixelImage>& src_image,
                                                            const heif_encoding_options& options);
+
+protected:
+  std::shared_ptr<Box_cmpd> m_cmpd;
+  std::shared_ptr<Box_uncC> m_uncC;
+};
+
+
+class unc_encoder_factory
+{
+public:
+  virtual ~unc_encoder_factory() = default;
+
+  static Result<std::unique_ptr<const unc_encoder> > get_unc_encoder(const std::shared_ptr<const HeifPixelImage>& prototype_image,
+                                                                     const heif_encoding_options& options);
+
+  virtual bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
+                          const heif_encoding_options& options) const = 0;
+
+  virtual std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& prototype_image,
+                                                    const heif_encoding_options& options) const = 0;
 };

 #endif //LIBHEIF_UNC_ENCODER_H
diff --git a/libheif/codecs/uncompressed/unc_encoder_planar.cc b/libheif/codecs/uncompressed/unc_encoder_planar.cc
index 31d1daad..5c60dff8 100644
--- a/libheif/codecs/uncompressed/unc_encoder_planar.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_planar.cc
@@ -21,7 +21,7 @@
 #include "unc_boxes.h"


-bool unc_encoder_planar::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
+bool unc_encoder_factory_planar::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
                                     const heif_encoding_options& options) const
 {
   if (image->has_channel(heif_channel_interleaved)) {
@@ -32,6 +32,14 @@ bool unc_encoder_planar::can_encode(const std::shared_ptr<const HeifPixelImage>&
 }


+std::unique_ptr<const unc_encoder>  unc_encoder_factory_planar::create(const std::shared_ptr<const HeifPixelImage>& image,
+                                                        const heif_encoding_options& options) const
+{
+  return std::make_unique<unc_encoder_planar>(image, options);
+}
+
+
+
 heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel)
 {
   switch (channel) {
@@ -42,8 +50,9 @@ heif_uncompressed_component_type heif_channel_to_component_type(heif_channel cha
     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_interleaved: assert(false); break;
-    case heif_channel_filter_array:  return heif_uncompressed_component_type::component_type_filter_array;
+    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;
   }
@@ -95,19 +104,18 @@ std::vector<channel_component> get_channels(const std::shared_ptr<const HeifPixe
   return channels;
 }

-void unc_encoder_planar::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
-                                            std::shared_ptr<Box_uncC>& uncC,
-                                            const std::shared_ptr<const HeifPixelImage>& image,
-                                            const heif_encoding_options& options) const
+
+unc_encoder_planar::unc_encoder_planar(const std::shared_ptr<const HeifPixelImage>& image,
+                                                        const heif_encoding_options& options)
 {
   auto channels = get_channels(image);

   // if we have any component > 8 bits, we enable this
   bool little_endian = false;

-  uint16_t index=0;
+  uint16_t index = 0;
   for (channel_component channelcomponent : channels) {
-    cmpd->add_component({channelcomponent.component_type});
+    m_cmpd->add_component({channelcomponent.component_type});

     uint8_t bpp = image->get_bits_per_pixel(channelcomponent.channel);
     uint8_t component_align_size = static_cast<uint8_t>((bpp + 7) / 8);
@@ -120,27 +128,26 @@ void unc_encoder_planar::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
       little_endian = true; // TODO: depending on the host endianness
     }

-    uncC->add_component({index, bpp, component_format_unsigned, component_align_size});
+    m_uncC->add_component({index, bpp, component_format_unsigned, component_align_size});
     index++;
   }

-  uncC->set_interleave_type(interleave_mode_component);
-  uncC->set_components_little_endian(little_endian);
+  m_uncC->set_interleave_type(interleave_mode_component);
+  m_uncC->set_components_little_endian(little_endian);

   if (image->get_chroma_format() == heif_chroma_420) {
-    uncC->set_sampling_type(2);
+    m_uncC->set_sampling_type(2);
   }
   else if (image->get_chroma_format() == heif_chroma_422) {
-    uncC->set_sampling_type(1);
+    m_uncC->set_sampling_type(1);
   }
   else {
-    uncC->set_sampling_type(0);
+    m_uncC->set_sampling_type(0);
   }
 }


-std::vector<uint8_t> unc_encoder_planar::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image,
-                                                     const heif_encoding_options& options) const
+std::vector<uint8_t> unc_encoder_planar::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
 {
   std::vector<uint8_t> data;

@@ -161,7 +168,7 @@ std::vector<uint8_t> unc_encoder_planar::encode_tile(const std::shared_ptr<const

   // output all component planes

-  uint64_t out_data_start_pos=0;
+  uint64_t out_data_start_pos = 0;

   for (channel_component channelcomponent : channels) {
     int bpp = src_image->get_bits_per_pixel(channelcomponent.channel);
diff --git a/libheif/codecs/uncompressed/unc_encoder_planar.h b/libheif/codecs/uncompressed/unc_encoder_planar.h
index b8ba7ce3..3609b319 100644
--- a/libheif/codecs/uncompressed/unc_encoder_planar.h
+++ b/libheif/codecs/uncompressed/unc_encoder_planar.h
@@ -18,21 +18,24 @@

 #include "unc_encoder.h"

-
 class unc_encoder_planar : public unc_encoder
 {
+public:
+  unc_encoder_planar(const std::shared_ptr<const HeifPixelImage>& image,
+                     const heif_encoding_options& options);
+
+  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+};
+
+
+class unc_encoder_factory_planar : public unc_encoder_factory
+{
 public:
   [[nodiscard]] bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
                                 const heif_encoding_options& options) const override;

-  void fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& out_cmpd,
-                          std::shared_ptr<Box_uncC>& out_uncC,
-                          const std::shared_ptr<const HeifPixelImage>& image,
-                          const heif_encoding_options& options) const override;
-
-  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image,
-                                                 const heif_encoding_options& options) const override;
+  std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& image,
+                                            const heif_encoding_options& options) const override;
 };

-
-#endif //LIBHEIF_UNC_ENCODER_PLANAR_H
\ No newline at end of file
+#endif //LIBHEIF_UNC_ENCODER_PLANAR_H
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
index 72fd08b4..0e681286 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
@@ -27,8 +27,8 @@
 #include "unc_types.h"


-bool unc_encoder_rgb3_rgba::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
-                                       const heif_encoding_options& options) const
+bool unc_encoder_factory_rgb3_rgba::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
+                                               const heif_encoding_options& options) const
 {
   if (image->get_colorspace() != heif_colorspace_RGB) {
     return false;
@@ -44,29 +44,34 @@ bool unc_encoder_rgb3_rgba::can_encode(const std::shared_ptr<const HeifPixelImag
 }


-void unc_encoder_rgb3_rgba::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
-                                               std::shared_ptr<Box_uncC>& uncC,
-                                               const std::shared_ptr<const HeifPixelImage>& image,
-                                               const heif_encoding_options& options) const
+std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb3_rgba::create(const std::shared_ptr<const HeifPixelImage>& image,
+                                                                         const heif_encoding_options& options) const
+{
+  return std::make_unique<unc_encoder_rgb3_rgba>(image, options);
+}
+
+
+unc_encoder_rgb3_rgba::unc_encoder_rgb3_rgba(const std::shared_ptr<const HeifPixelImage>& image,
+                                             const heif_encoding_options& options)
 {
-  cmpd->add_component({component_type_red});
-  cmpd->add_component({component_type_green});
-  cmpd->add_component({component_type_blue});
+  m_cmpd->add_component({component_type_red});
+  m_cmpd->add_component({component_type_green});
+  m_cmpd->add_component({component_type_blue});

   bool save_alpha = image->has_alpha();

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

   uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);

   if (bpp == 8) {
     if (save_alpha) {
-      uncC->set_profile(fourcc("rgba"));
+      m_uncC->set_profile(fourcc("rgba"));
     }
     else {
-      uncC->set_profile(fourcc("rgb3"));
+      m_uncC->set_profile(fourcc("rgb3"));
     }
   }

@@ -75,19 +80,18 @@ void unc_encoder_rgb3_rgba::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
     component_align_size = 1;
   }

-  uncC->set_interleave_type(interleave_mode_pixel);
-  uncC->set_sampling_type(0);
-  uncC->add_component({0, bpp, component_format_unsigned, component_align_size});
-  uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
-  uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
+  m_uncC->set_interleave_type(interleave_mode_pixel);
+  m_uncC->set_sampling_type(0);
+  m_uncC->add_component({0, bpp, component_format_unsigned, component_align_size});
+  m_uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
+  m_uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
   if (save_alpha) {
-    uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
+    m_uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
   }
 }


-std::vector<uint8_t> unc_encoder_rgb3_rgba::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image,
-                                                        const heif_encoding_options& options) const
+std::vector<uint8_t> unc_encoder_rgb3_rgba::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
 {
   std::vector<uint8_t> data;

diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
index 2fd4c325..2c196287 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
@@ -25,18 +25,22 @@

 class unc_encoder_rgb3_rgba : public unc_encoder
 {
+public:
+  unc_encoder_rgb3_rgba(const std::shared_ptr<const HeifPixelImage>& image,
+                        const heif_encoding_options& options);
+
+  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+};
+
+
+class unc_encoder_factory_rgb3_rgba : public unc_encoder_factory
+{
 public:
   [[nodiscard]] bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
                                 const heif_encoding_options& options) const override;

-  void fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& out_cmpd,
-                          std::shared_ptr<Box_uncC>& out_uncC,
-                          const std::shared_ptr<const HeifPixelImage>& image,
-                          const heif_encoding_options& options) const override;
-
-  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image,
-                                                 const heif_encoding_options& options) const override;
+  std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& image,
+                                            const heif_encoding_options& options) const override;
 };

-
 #endif //LIBHEIF_UNC_ENCODER_RGB3_RGBA_H
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
index 1265a061..993bd7eb 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
@@ -20,8 +20,8 @@
 #include "unc_types.h"


-bool unc_encoder_rgb_hdr_packed_interleave::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
-                                      const heif_encoding_options& options) const
+bool unc_encoder_factory_rgb_hdr_packed_interleave::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
+                                                               const heif_encoding_options& options) const
 {
   if (image->get_colorspace() != heif_colorspace_RGB) {
     return false;
@@ -43,33 +43,37 @@ bool unc_encoder_rgb_hdr_packed_interleave::can_encode(const std::shared_ptr<con
 }


-void unc_encoder_rgb_hdr_packed_interleave::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
-                                              std::shared_ptr<Box_uncC>& uncC,
-                                              const std::shared_ptr<const HeifPixelImage>& image,
-                                              const heif_encoding_options& options) const
+std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_hdr_packed_interleave::create(const std::shared_ptr<const HeifPixelImage>& image,
+                                                                                         const heif_encoding_options& options) const
 {
-  cmpd->add_component({component_type_red});
-  cmpd->add_component({component_type_green});
-  cmpd->add_component({component_type_blue});
+  return std::make_unique<unc_encoder_rgb_hdr_packed_interleave>(image, options);
+}
+
+
+unc_encoder_rgb_hdr_packed_interleave::unc_encoder_rgb_hdr_packed_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});

   uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);

   uint8_t nBits = static_cast<uint8_t>(3 * bpp);
   uint8_t bytes_per_pixel = static_cast<uint8_t>((nBits + 7) / 8);

-  uncC->set_interleave_type(interleave_mode_pixel);
-  uncC->set_pixel_size(bytes_per_pixel);
-  uncC->set_sampling_type(0);
-  uncC->set_components_little_endian(true);
+  m_uncC->set_interleave_type(interleave_mode_pixel);
+  m_uncC->set_pixel_size(bytes_per_pixel);
+  m_uncC->set_sampling_type(0);
+  m_uncC->set_components_little_endian(true);

-  uncC->add_component({0, bpp, component_format_unsigned, 0});
-  uncC->add_component({1, bpp, component_format_unsigned, 0});
-  uncC->add_component({2, bpp, component_format_unsigned, 0});
+  m_uncC->add_component({0, bpp, component_format_unsigned, 0});
+  m_uncC->add_component({1, bpp, component_format_unsigned, 0});
+  m_uncC->add_component({2, bpp, component_format_unsigned, 0});
 }


-std::vector<uint8_t> unc_encoder_rgb_hdr_packed_interleave::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image,
-                                                       const heif_encoding_options& options) const
+std::vector<uint8_t> unc_encoder_rgb_hdr_packed_interleave::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
 {
   std::vector<uint8_t> data;

@@ -80,6 +84,7 @@ std::vector<uint8_t> unc_encoder_rgb_hdr_packed_interleave::encode_tile(const st

   size_t src_stride;
   const auto* src_data = reinterpret_cast<const uint16_t*>(src_image->get_plane(heif_channel_interleaved, &src_stride));
+  src_stride /= 2;

   uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * bytes_per_pixel;
   data.resize(out_size);
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
index e4db5c13..3dd69646 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
@@ -20,18 +20,22 @@

 class unc_encoder_rgb_hdr_packed_interleave : public unc_encoder
 {
+public:
+  unc_encoder_rgb_hdr_packed_interleave(const std::shared_ptr<const HeifPixelImage>& image,
+                                        const heif_encoding_options& options);
+
+  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+};
+
+
+class unc_encoder_factory_rgb_hdr_packed_interleave : public unc_encoder_factory
+{
 public:
   [[nodiscard]] bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
                                 const heif_encoding_options& options) const override;

-  void fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& out_cmpd,
-                          std::shared_ptr<Box_uncC>& out_uncC,
-                          const std::shared_ptr<const HeifPixelImage>& image,
-                          const heif_encoding_options& options) const override;
-
-  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image,
-                                                 const heif_encoding_options& options) const override;
+  std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& image,
+                                            const heif_encoding_options& options) const override;
 };

-
 #endif //LIBHEIF_UNC_ENCODER_RGB10_12_H
diff --git a/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc b/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
index a64f6ded..26d3e1aa 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
@@ -27,8 +27,8 @@
 #include "unc_types.h"


-bool unc_encoder_rrggbb::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
-                                    const heif_encoding_options& options) const
+bool unc_encoder_factory_rrggbb::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
+                                            const heif_encoding_options& options) const
 {
   if (image->get_colorspace() != heif_colorspace_RGB) {
     return false;
@@ -48,19 +48,24 @@ bool unc_encoder_rrggbb::can_encode(const std::shared_ptr<const HeifPixelImage>&
 }


-void unc_encoder_rrggbb::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
-                                            std::shared_ptr<Box_uncC>& uncC,
-                                            const std::shared_ptr<const HeifPixelImage>& image,
-                                            const heif_encoding_options& options) const
+std::unique_ptr<const unc_encoder> unc_encoder_factory_rrggbb::create(const std::shared_ptr<const HeifPixelImage>& image,
+                                                                      const heif_encoding_options& options) const
+{
+  return std::make_unique<unc_encoder_rrggbb>(image, options);
+}
+
+
+unc_encoder_rrggbb::unc_encoder_rrggbb(const std::shared_ptr<const HeifPixelImage>& image,
+                                       const heif_encoding_options& options)
 {
-  cmpd->add_component({component_type_red});
-  cmpd->add_component({component_type_green});
-  cmpd->add_component({component_type_blue});
+  m_cmpd->add_component({component_type_red});
+  m_cmpd->add_component({component_type_green});
+  m_cmpd->add_component({component_type_blue});

   bool save_alpha = image->has_alpha();

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

   bool little_endian = (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE ||
@@ -73,21 +78,20 @@ void unc_encoder_rrggbb::fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
     component_align_size = 0;
   }

-  uncC->set_interleave_type(interleave_mode_pixel);
-  uncC->set_sampling_type(0);
-  uncC->set_components_little_endian(little_endian);
+  m_uncC->set_interleave_type(interleave_mode_pixel);
+  m_uncC->set_sampling_type(0);
+  m_uncC->set_components_little_endian(little_endian);

-  uncC->add_component({0, bpp, component_format_unsigned, component_align_size});
-  uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
-  uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
+  m_uncC->add_component({0, bpp, component_format_unsigned, component_align_size});
+  m_uncC->add_component({1, bpp, component_format_unsigned, component_align_size});
+  m_uncC->add_component({2, bpp, component_format_unsigned, component_align_size});
   if (save_alpha) {
-    uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
+    m_uncC->add_component({3, bpp, component_format_unsigned, component_align_size});
   }
 }


-std::vector<uint8_t> unc_encoder_rrggbb::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image,
-                                                     const heif_encoding_options& options) const
+std::vector<uint8_t> unc_encoder_rrggbb::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
 {
   std::vector<uint8_t> data;

diff --git a/libheif/codecs/uncompressed/unc_encoder_rrggbb.h b/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
index 4e0e0ab1..431cb19e 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
@@ -25,17 +25,22 @@

 class unc_encoder_rrggbb : public unc_encoder
 {
+public:
+  unc_encoder_rrggbb(const std::shared_ptr<const HeifPixelImage>& image,
+                     const heif_encoding_options& options);
+
+  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+};
+
+
+class unc_encoder_factory_rrggbb : public unc_encoder_factory
+{
 public:
   [[nodiscard]] bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
                                 const heif_encoding_options& options) const override;

-  void fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& out_cmpd,
-                          std::shared_ptr<Box_uncC>& out_uncC,
-                          const std::shared_ptr<const HeifPixelImage>& image,
-                          const heif_encoding_options& options) const override;
-
-  [[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image,
-                                                 const heif_encoding_options& options) const override;
+  std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& image,
+                                            const heif_encoding_options& options) const override;
 };


diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index 8e09644a..0ab5d0b9 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -92,7 +92,7 @@ Result<Encoder::CodedImageData> ImageItem_uncompressed::encode(const std::shared
                                                                  const heif_encoding_options& options,
                                                                  heif_image_input_class input_class)
 {
-  Result<const unc_encoder*> uncEncoder = unc_encoder::get_unc_encoder(src_image, options);
+  Result<std::unique_ptr<const unc_encoder>> uncEncoder = unc_encoder_factory::get_unc_encoder(src_image, options);
   if (!uncEncoder) {
     return {uncEncoder.error()};
   }
@@ -118,7 +118,7 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
   }


-  Result<const unc_encoder*> uncEncoder = unc_encoder::get_unc_encoder(prototype, *encoding_options);
+  Result<std::unique_ptr<const unc_encoder>> uncEncoder = unc_encoder_factory::get_unc_encoder(prototype, *encoding_options);
   if (!uncEncoder) {
     return {uncEncoder.error()};
   }
@@ -131,7 +131,7 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
   heif_item_id unci_id = ctx->get_heif_file()->add_new_image(fourcc("unci"));
   auto unci_image = std::make_shared<ImageItem_uncompressed>(ctx, unci_id);
   unci_image->set_resolution(parameters->image_width, parameters->image_height);
-  unci_image->m_unc_encoder = *uncEncoder;
+  unci_image->m_unc_encoder = std::move(*uncEncoder);
   unci_image->m_encoding_options = *encoding_options;

   ctx->insert_image_item(unci_id, unci_image);
@@ -142,19 +142,10 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci

   // --- generate configuration property boxes

-  Result<const unc_encoder*> encoderResult = unc_encoder::get_unc_encoder(prototype, *encoding_options);
-  if (encoderResult.error()) {
-    return encoderResult.error();
-  }
-
-  std::shared_ptr<Box_uncC> uncC = std::make_shared<Box_uncC>();
-  std::shared_ptr<Box_cmpd> cmpd = std::make_shared<Box_cmpd>();
-
-  (*encoderResult)->fill_cmpd_and_uncC(cmpd, uncC, prototype, *encoding_options);
-
+  auto uncC = unci_image->m_unc_encoder->get_uncC();
   unci_image->add_property(uncC, true);
   if (!uncC->is_minimized()) {
-    unci_image->add_property(cmpd, true);
+    unci_image->add_property(unci_image->m_unc_encoder->get_cmpd(), true);
   }


@@ -240,7 +231,7 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c
     // TODO: drop alpha
   }

-  Result<std::vector<uint8_t>> codedBitstreamResult = m_unc_encoder->encode_tile(image, m_encoding_options);
+  Result<std::vector<uint8_t>> codedBitstreamResult = m_unc_encoder->encode_tile(image);
   if (!codedBitstreamResult) {
     return codedBitstreamResult.error();
   }
diff --git a/libheif/image-items/unc_image.h b/libheif/image-items/unc_image.h
index 50c286fb..889f7255 100644
--- a/libheif/image-items/unc_image.h
+++ b/libheif/image-items/unc_image.h
@@ -32,6 +32,7 @@
 #include <vector>
 #include <memory>
 #include <set>
+#include "codecs/uncompressed/unc_encoder.h"

 class unc_encoder;
 class HeifContext;
@@ -98,7 +99,7 @@ private:
   std::shared_ptr<class Decoder_uncompressed> m_decoder;
   std::shared_ptr<class Encoder_uncompressed> m_encoder;

-  const unc_encoder* m_unc_encoder = nullptr;
+  std::unique_ptr<const unc_encoder> m_unc_encoder;
   heif_encoding_options m_encoding_options;

   /*