diff -pruN 1.19.8-1/CMakeLists.txt 1.20.1-1/CMakeLists.txt
--- 1.19.8-1/CMakeLists.txt	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/CMakeLists.txt	2025-07-02 13:05:31.000000000 +0000
@@ -1,6 +1,6 @@
 cmake_minimum_required (VERSION 3.16.3) # Oldest Ubuntu LTS (20.04 currently)
 
-project(libheif LANGUAGES C CXX VERSION 1.19.8)
+project(libheif LANGUAGES C CXX VERSION 1.20.1)
 
 # compatibility_version is never allowed to be decreased for any specific SONAME.
 # Libtool in the libheif-1.15.1 release had set it to 17.0.0, so we have to use this for the v1.x.y versions.
diff -pruN 1.19.8-1/README.md 1.20.1-1/README.md
--- 1.19.8-1/README.md	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/README.md	2025-07-02 13:05:31.000000000 +0000
@@ -8,7 +8,7 @@ There is partial support for ISO/IEC 230
 HEIF and AVIF are new image file formats employing HEVC (H.265) or AV1 image coding, respectively, for the
 best compression ratios currently possible.
 
-libheif makes use of [libde265](https://github.com/strukturag/libde265) for HEIF image decoding and x265 for encoding.
+libheif makes use of [libde265](https://github.com/strukturag/libde265) for HEIC image decoding and x265 for encoding.
 For AVIF, libaom, dav1d, svt-av1, or rav1e are used as codecs.
 
 ## Supported features
diff -pruN 1.19.8-1/debian/changelog 1.20.1-1/debian/changelog
--- 1.19.8-1/debian/changelog	2025-04-29 08:32:58.000000000 +0000
+++ 1.20.1-1/debian/changelog	2025-07-28 09:41:29.000000000 +0000
@@ -1,3 +1,14 @@
+libheif (1.20.1-1) experimental; urgency=medium
+
+  * New upstream version 1.20.1
+  * New upstream version 1.20.1
+  * Add patch to fix undefined symbol in svtenc plugin.
+  * Update symbols for new upstream version.
+  * Add "heif-view" binary package.
+  * Add patch to load (encoding) plugins earlier.
+
+ -- Joachim Bauch <fancycode@debian.org>  Mon, 28 Jul 2025 11:41:29 +0200
+
 libheif (1.19.8-1) unstable; urgency=medium
 
   * New upstream version 1.19.8
diff -pruN 1.19.8-1/debian/control 1.20.1-1/debian/control
--- 1.19.8-1/debian/control	2025-04-29 08:32:16.000000000 +0000
+++ 1.20.1-1/debian/control	2025-07-28 07:41:29.000000000 +0000
@@ -19,6 +19,7 @@ Build-Depends:
 # librav1e-dev is built by src:rust-rav1e which is not available on all platforms.
 # Make sure to use the same list in the "libheif-plugin-rav1e" package below.
  librav1e-dev [amd64 arm64 armel armhf mips64el mipsel powerpc ppc64 ppc64el riscv64 s390x],
+ libsdl2-dev,
  libsharpyuv-dev,
  libsvtav1enc-dev,
  libx265-dev,
@@ -474,3 +475,24 @@ Description: HEIF and AVIF file format d
  .
  A gdk-pixbuf loader module for applications such as "gpicview" and "pcmanfm"
  is provided by this package.
+
+Package: heif-view
+Section: graphics
+Architecture: any
+Multi-Arch: foreign
+Depends:
+ libheif1 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends}
+Description: HEIF and AVIF file format decoder and encoder - heif sequence viewer
+ libheif is an ISO/IEC 23008-12:2017 HEIF and AVIF (AV1 Image File Format) file
+ format decoder and encoder. There is partial support for ISO/IEC 23008-12:2022
+ (2nd Edition) capabilities.
+ .
+ HEIF and AVIF are new image file formats employing HEVC (H.265) or AV1 image
+ coding, respectively, for the best compression ratios currently possible.
+ .
+ libheif supports various codecs provided by plugins for image decoding and
+ encoding.
+ .
+ A viewer for heif sequence images is provided by this package.
diff -pruN 1.19.8-1/debian/heif-view.install 1.20.1-1/debian/heif-view.install
--- 1.19.8-1/debian/heif-view.install	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/debian/heif-view.install	2025-07-28 07:33:23.000000000 +0000
@@ -0,0 +1 @@
+usr/bin/heif-view
diff -pruN 1.19.8-1/debian/libheif1.symbols 1.20.1-1/debian/libheif1.symbols
--- 1.19.8-1/debian/libheif1.symbols	2025-01-07 12:11:40.000000000 +0000
+++ 1.20.1-1/debian/libheif1.symbols	2025-07-28 07:01:02.000000000 +0000
@@ -10,10 +10,14 @@ libheif.so.1 libheif1 #MINVER#
  heif_camera_extrinsic_matrix_release@Base 1.18.1
  heif_check_filetype@Base 1.4.0
  heif_check_jpeg_filetype@Base 1.14.0
+ heif_color_conversion_options_ext_alloc@Base 1.20.0
+ heif_color_conversion_options_ext_copy@Base 1.20.0
+ heif_color_conversion_options_ext_free@Base 1.20.0
  heif_color_conversion_options_set_defaults@Base 1.19.1
  heif_context_add_XMP_metadata2@Base 1.14.0
  heif_context_add_XMP_metadata@Base 1.3.2
  heif_context_add_compatible_brand@Base 1.18.1
+ heif_context_add_empty_unci_image@Base 1.20.0
  heif_context_add_exif_metadata@Base 1.3.2
  heif_context_add_generic_metadata@Base 1.6.0
  heif_context_add_generic_uri_metadata@Base 1.18.1
@@ -26,6 +30,8 @@ libheif.so.1 libheif1 #MINVER#
  heif_context_add_overlay_image@Base 1.19.1
  heif_context_add_precompressed_mime_item@Base 1.18.1
  heif_context_add_uri_item@Base 1.18.1
+ heif_context_add_uri_metadata_sequence_track@Base 1.20.0
+ heif_context_add_visual_sequence_track@Base 1.20.0
  heif_context_alloc@Base 1.0.0
  heif_context_assign_thumbnail@Base 1.3.2
  heif_context_debug_dump_boxes_to_file@Base 1.0.0
@@ -47,21 +53,30 @@ libheif.so.1 libheif1 #MINVER#
  heif_context_get_primary_image_handle@Base 1.0.0
  heif_context_get_region_item@Base 1.16.2
  heif_context_get_security_limits@Base 1.19.1
+ heif_context_get_sequence_duration@Base 1.20.0
+ heif_context_get_sequence_timescale@Base 1.20.0
+ heif_context_get_track@Base 1.20.0
+ heif_context_get_track_ids@Base 1.20.0
+ heif_context_has_sequence@Base 1.20.0
  heif_context_is_top_level_image_ID@Base 1.0.0
+ heif_context_number_of_sequence_tracks@Base 1.20.0
  heif_context_read_from_file@Base 1.0.0
  heif_context_read_from_memory@Base 1.0.0
  heif_context_read_from_memory_without_copy@Base 1.3.2
  heif_context_read_from_reader@Base 1.3.2
+ heif_context_set_major_brand@Base 1.20.0
  heif_context_set_max_decoding_threads@Base 1.13.0
  heif_context_set_maximum_image_size_limit@Base 1.6.0
  heif_context_set_primary_image@Base 1.3.2
  heif_context_set_security_limits@Base 1.19.1
+ heif_context_set_sequence_timescale@Base 1.20.0
  heif_context_write@Base 1.1.0
  heif_context_write_to_file@Base 1.1.0
  heif_decode_image@Base 1.0.0
  heif_decoder_descriptor_get_id_name@Base 1.15.1
  heif_decoder_descriptor_get_name@Base 1.15.1
  heif_decoding_options_alloc@Base 1.0.0
+ heif_decoding_options_copy@Base 1.20.0
  heif_decoding_options_free@Base 1.0.0
  heif_deinit@Base 1.13.0
  heif_depth_representation_info_free@Base 1.0.0
@@ -96,6 +111,7 @@ libheif.so.1 libheif1 #MINVER#
  heif_encoder_set_parameter_integer@Base 1.1.0
  heif_encoder_set_parameter_string@Base 1.1.0
  heif_encoding_options_alloc@Base 1.3.2
+ heif_encoding_options_copy@Base 1.20.0
  heif_encoding_options_free@Base 1.3.2
  heif_entity_groups_release@Base 1.19.1
  heif_error_success@Base 1.17.0
@@ -119,10 +135,12 @@ libheif.so.1 libheif1 #MINVER#
  heif_have_encoder_for_format@Base 1.3.2
  heif_image_add_decoding_warning@Base 1.13.0
  heif_image_add_plane@Base 1.0.0
+ heif_image_add_plane_safe@Base 1.20.0
  heif_image_create@Base 1.0.0
  heif_image_crop@Base 1.9.1
  heif_image_extend_padding_to_size@Base 1.15.1
  heif_image_extend_to_size_fill_with_zero@Base 1.19.1
+ heif_image_extract_area@Base 1.20.0
  heif_image_get_bits_per_pixel@Base 1.0.0
  heif_image_get_bits_per_pixel_range@Base 1.5.0
  heif_image_get_chroma_format@Base 1.0.0
@@ -130,16 +148,21 @@ libheif.so.1 libheif1 #MINVER#
  heif_image_get_colorspace@Base 1.0.0
  heif_image_get_content_light_level@Base 1.15.1
  heif_image_get_decoding_warnings@Base 1.13.0
+ heif_image_get_duration@Base 1.20.0
+ heif_image_get_gimi_sample_content_id@Base 1.20.0
  heif_image_get_height@Base 1.0.0
  heif_image_get_mastering_display_colour_volume@Base 1.15.1
  heif_image_get_nclx_color_profile@Base 1.5.0
  heif_image_get_pixel_aspect_ratio@Base 1.15.1
+ heif_image_get_plane2@Base 1.20.0
  heif_image_get_plane@Base 1.0.0
+ heif_image_get_plane_readonly2@Base 1.20.0
  heif_image_get_plane_readonly@Base 1.0.0
  heif_image_get_primary_height@Base 1.9.1
  heif_image_get_primary_width@Base 1.9.1
  heif_image_get_raw_color_profile@Base 1.5.0
  heif_image_get_raw_color_profile_size@Base 1.5.0
+ heif_image_get_tai_timestamp@Base 1.20.0
  heif_image_get_width@Base 1.0.0
  heif_image_handle_add_region_item@Base 1.16.2
  heif_image_handle_decode_image_tile@Base 1.19.1
@@ -199,11 +222,14 @@ libheif.so.1 libheif1 #MINVER#
  heif_image_release@Base 1.0.0
  heif_image_scale_image@Base 1.0.0
  heif_image_set_content_light_level@Base 1.15.1
+ heif_image_set_duration@Base 1.20.0
+ heif_image_set_gimi_sample_content_id@Base 1.20.0
  heif_image_set_mastering_display_colour_volume@Base 1.15.1
  heif_image_set_nclx_color_profile@Base 1.4.0
  heif_image_set_pixel_aspect_ratio@Base 1.15.1
  heif_image_set_premultiplied_alpha@Base 1.12.0
  heif_image_set_raw_color_profile@Base 1.4.0
+ heif_image_set_tai_timestamp@Base 1.20.0
  heif_init@Base 1.13.0
  heif_item_add_property_user_description@Base 1.16.2
  heif_item_add_raw_property@Base 1.18.1
@@ -215,6 +241,8 @@ libheif.so.1 libheif1 #MINVER#
  heif_item_get_properties_of_type@Base 1.16.2
  heif_item_get_property_raw_data@Base 1.18.1
  heif_item_get_property_raw_size@Base 1.18.1
+ heif_item_get_property_tai_clock_info@Base 1.20.0
+ heif_item_get_property_tai_timestamp@Base 1.20.0
  heif_item_get_property_transform_crop_borders@Base 1.16.2
  heif_item_get_property_transform_mirror@Base 1.16.2
  heif_item_get_property_transform_rotation_ccw@Base 1.16.2
@@ -225,6 +253,8 @@ libheif.so.1 libheif1 #MINVER#
  heif_item_get_uri_item_uri_type@Base 1.19.1
  heif_item_is_item_hidden@Base 1.19.1
  heif_item_set_item_name@Base 1.19.1
+ heif_item_set_property_tai_clock_info@Base 1.20.0
+ heif_item_set_property_tai_timestamp@Base 1.20.0
  heif_list_compatible_brands@Base 1.11.0
  heif_load_plugin@Base 1.14.0
  heif_load_plugins@Base 1.14.0
@@ -236,6 +266,18 @@ libheif.so.1 libheif1 #MINVER#
  heif_nclx_color_profile_set_matrix_coefficients@Base 1.13.0
  heif_nclx_color_profile_set_transfer_characteristics@Base 1.13.0
  heif_property_user_description_release@Base 1.16.2
+ heif_raw_sequence_sample_alloc@Base 1.20.0
+ heif_raw_sequence_sample_get_data@Base 1.20.0
+ heif_raw_sequence_sample_get_data_size@Base 1.20.0
+ heif_raw_sequence_sample_get_duration@Base 1.20.0
+ heif_raw_sequence_sample_get_gimi_sample_content_id@Base 1.20.0
+ heif_raw_sequence_sample_get_tai_timestamp@Base 1.20.0
+ heif_raw_sequence_sample_has_tai_timestamp@Base 1.20.0
+ heif_raw_sequence_sample_release@Base 1.20.0
+ heif_raw_sequence_sample_set_data@Base 1.20.0
+ heif_raw_sequence_sample_set_duration@Base 1.20.0
+ heif_raw_sequence_sample_set_gimi_sample_content_id@Base 1.20.0
+ heif_raw_sequence_sample_set_tai_timestamp@Base 1.20.0
  heif_read_main_brand@Base 1.11.0
  heif_read_minor_version_brand@Base 1.19.1
  heif_region_get_ellipse@Base 1.16.2
@@ -275,4 +317,44 @@ libheif.so.1 libheif1 #MINVER#
  heif_register_encoder_plugin@Base 1.1.0
  heif_release_item_data@Base 1.18.1
  heif_release_item_references@Base 1.18.1
+ heif_sequence_encoding_options_alloc@Base 1.20.0
+ heif_sequence_encoding_options_release@Base 1.20.0
+ heif_string_release@Base 1.20.0
+ heif_tai_clock_info_alloc@Base 1.20.0
+ heif_tai_clock_info_copy@Base 1.20.0
+ heif_tai_clock_info_release@Base 1.20.0
+ heif_tai_timestamp_packet_alloc@Base 1.20.0
+ heif_tai_timestamp_packet_copy@Base 1.20.0
+ heif_tai_timestamp_packet_release@Base 1.20.0
+ heif_track_add_raw_sequence_sample@Base 1.20.0
+ heif_track_add_reference_to_track@Base 1.20.0
+ heif_track_decode_next_image@Base 1.20.0
+ heif_track_encode_sequence_image@Base 1.20.0
+ heif_track_find_referring_tracks@Base 1.20.0
+ heif_track_get_gimi_track_content_id@Base 1.20.0
+ heif_track_get_id@Base 1.20.0
+ heif_track_get_image_resolution@Base 1.20.0
+ heif_track_get_next_raw_sequence_sample@Base 1.20.0
+ heif_track_get_number_of_sample_aux_infos@Base 1.20.0
+ heif_track_get_number_of_track_reference_of_type@Base 1.20.0
+ heif_track_get_number_of_track_reference_types@Base 1.20.0
+ heif_track_get_references_from_track@Base 1.20.0
+ heif_track_get_sample_aux_info_types@Base 1.20.0
+ heif_track_get_sample_entry_type_of_first_cluster@Base 1.20.0
+ heif_track_get_tai_clock_info_of_first_cluster@Base 1.20.0
+ heif_track_get_timescale@Base 1.20.0
+ heif_track_get_track_handler_type@Base 1.20.0
+ heif_track_get_track_reference_types@Base 1.20.0
+ heif_track_get_urim_sample_entry_uri_of_first_cluster@Base 1.20.0
+ heif_track_options_alloc@Base 1.20.0
+ heif_track_options_enable_sample_gimi_content_ids@Base 1.20.0
+ heif_track_options_enable_sample_tai_timestamps@Base 1.20.0
+ heif_track_options_release@Base 1.20.0
+ heif_track_options_set_gimi_track_id@Base 1.20.0
+ heif_track_options_set_interleaved_sample_aux_infos@Base 1.20.0
+ heif_track_options_set_timescale@Base 1.20.0
+ heif_track_release@Base 1.20.0
+ heif_unci_image_parameters_alloc@Base 1.20.0
+ heif_unci_image_parameters_copy@Base 1.20.0
+ heif_unci_image_parameters_release@Base 1.20.0
  heif_unload_plugin@Base 1.14.0
diff -pruN 1.19.8-1/debian/patches/load-encoder-plugins.patch 1.20.1-1/debian/patches/load-encoder-plugins.patch
--- 1.19.8-1/debian/patches/load-encoder-plugins.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/debian/patches/load-encoder-plugins.patch	2025-07-28 09:30:18.000000000 +0000
@@ -0,0 +1,32 @@
+From 1bf5e6509030a048cc044def155e06703324e1f6 Mon Sep 17 00:00:00 2001
+From: Joachim Bauch <bauch@struktur.de>
+Date: Mon, 28 Jul 2025 11:25:57 +0200
+Subject: [PATCH] Load plugins in "get_filtered_encoder_descriptors".
+Forwarded: https://github.com/strukturag/libheif/pull/1563
+
+---
+ libheif/plugin_registry.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/libheif/plugin_registry.cc b/libheif/plugin_registry.cc
+index 3c3b59e980..863cfabe0d 100644
+--- a/libheif/plugin_registry.cc
++++ b/libheif/plugin_registry.cc
+@@ -273,8 +273,6 @@ void register_encoder(const heif_encoder_plugin* encoder_plugin)
+ 
+ const struct heif_encoder_plugin* get_encoder(enum heif_compression_format type)
+ {
+-  load_plugins_if_not_initialized_yet();
+-
+   auto filtered_encoder_descriptors = get_filtered_encoder_descriptors(type, nullptr);
+   if (filtered_encoder_descriptors.size() > 0) {
+     return filtered_encoder_descriptors[0]->plugin;
+@@ -289,6 +287,8 @@ std::vector<const struct heif_encoder_descriptor*>
+ get_filtered_encoder_descriptors(enum heif_compression_format format,
+                                  const char* name)
+ {
++  load_plugins_if_not_initialized_yet();
++
+   std::vector<const struct heif_encoder_descriptor*> filtered_descriptors;
+ 
+   for (const auto& descr : s_encoder_descriptors) {
diff -pruN 1.19.8-1/debian/patches/series 1.20.1-1/debian/patches/series
--- 1.19.8-1/debian/patches/series	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/debian/patches/series	2025-07-28 09:30:42.000000000 +0000
@@ -0,0 +1,2 @@
+svtenc-undefined-symbol.patch
+load-encoder-plugins.patch
diff -pruN 1.19.8-1/debian/patches/svtenc-undefined-symbol.patch 1.20.1-1/debian/patches/svtenc-undefined-symbol.patch
--- 1.19.8-1/debian/patches/svtenc-undefined-symbol.patch	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/debian/patches/svtenc-undefined-symbol.patch	2025-07-28 07:27:14.000000000 +0000
@@ -0,0 +1,23 @@
+From 9674f755631f2deec7bfcaf23947fe6e225a9f63 Mon Sep 17 00:00:00 2001
+From: Joachim Bauch <bauch@struktur.de>
+Date: Mon, 28 Jul 2025 09:21:22 +0200
+Subject: [PATCH] Fix undefined symbol "Error::Ok" in svtenc plugin.
+Forwarded: https://github.com/strukturag/libheif/pull/1562
+
+---
+ libheif/plugins/CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/libheif/plugins/CMakeLists.txt b/libheif/plugins/CMakeLists.txt
+index f7379c09da..643f413a1c 100644
+--- a/libheif/plugins/CMakeLists.txt
++++ b/libheif/plugins/CMakeLists.txt
+@@ -66,7 +66,7 @@ set(AOM_ENCODER_extra_plugin_sources ../error.cc ../common_utils.cc ../common_ut
+ plugin_compilation(aomenc AOM AOM_ENCODER_FOUND AOM_ENCODER AOM_ENCODER)
+ 
+ set(SvtEnc_sources encoder_svt.cc encoder_svt.h)
+-set(SvtEnc_extra_plugin_sources ../common_utils.cc ../common_utils.h)
++set(SvtEnc_extra_plugin_sources ../error.cc ../common_utils.cc ../common_utils.h)
+ plugin_compilation(svtenc SvtEnc SvtEnc_FOUND SvtEnc SvtEnc)
+ 
+ set(RAV1E_sources encoder_rav1e.cc encoder_rav1e.h)
diff -pruN 1.19.8-1/examples/CMakeLists.txt 1.20.1-1/examples/CMakeLists.txt
--- 1.19.8-1/examples/CMakeLists.txt	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/CMakeLists.txt	2025-07-02 13:05:31.000000000 +0000
@@ -64,6 +64,22 @@ if (PNG_FOUND)
     install(FILES heif-thumbnailer.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
 endif()
 
+
+find_package(SDL2 NO_MODULE)
+
+if (SDL2_FOUND)
+    add_executable(heif-view ${getopt_sources}
+            heif_view.cc
+            sdl.cc
+            sdl.hh
+            common.cc
+            common.h)
+    target_link_libraries(heif-view PRIVATE heif ${SDL2_LIBRARIES})
+    target_include_directories(heif-view PRIVATE ${libheif_SOURCE_DIR} ${SDL2_INCLUDE_DIRS})
+    install(TARGETS heif-view RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif ()
+
+
 add_executable(heif-test ${getopt_sources}
         heif_test.cc
         common.cc
diff -pruN 1.19.8-1/examples/common.cc 1.20.1-1/examples/common.cc
--- 1.19.8-1/examples/common.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/common.cc	2025-07-02 13:05:31.000000000 +0000
@@ -27,22 +27,123 @@
 #include "common.h"
 #include "libheif/heif.h"
 #include <iostream>
+#include <fstream>
+#include <array>
+#include <vector>
+#include <algorithm>
 
+namespace heif_examples {
+  void show_version()
+  {
+    std::cout << LIBHEIF_VERSION << '\n'
+              << "libheif: " << heif_get_version() << '\n';
+    {
+      auto paths = heif_get_plugin_directories();
+      for (int i = 0; paths[i]; i++) {
+        std::cout << "plugin path: " << paths[i] << '\n';
+      }
+
+      if (paths[0] == nullptr) {
+        std::cout << "plugin path: plugins are disabled\n";
+      }
+
+      heif_free_plugin_directories(paths);
+    }
+  }
+
+
+#define MAX_DECODERS 20
+
+  void list_decoders(heif_compression_format format)
+  {
+    const heif_decoder_descriptor* decoders[MAX_DECODERS];
+    int n = heif_get_decoder_descriptors(format, decoders, MAX_DECODERS);
+
+    for (int i = 0; i < n; i++) {
+      const char* id = heif_decoder_descriptor_get_id_name(decoders[i]);
+      if (id == nullptr) {
+        id = "---";
+      }
+
+      std::cout << "- " << id << " = " << heif_decoder_descriptor_get_name(decoders[i]) << "\n";
+    }
+  }
+
+
+  void list_all_decoders()
+  {
+    std::cout << "AVC decoders:\n";
+    list_decoders(heif_compression_AVC);
+
+    std::cout << "AVIF decoders:\n";
+    list_decoders(heif_compression_AV1);
 
-void show_version()
-{
-  std::cout << LIBHEIF_VERSION << '\n'
-            << "libheif: " << heif_get_version() << '\n';
+    std::cout << "HEIC decoders:\n";
+    list_decoders(heif_compression_HEVC);
+
+    std::cout << "JPEG decoders:\n";
+    list_decoders(heif_compression_JPEG);
+
+    std::cout << "JPEG 2000 decoders:\n";
+    list_decoders(heif_compression_JPEG2000);
+
+    std::cout << "JPEG 2000 (HT) decoders:\n";
+    list_decoders(heif_compression_HTJ2K);
+
+    std::cout << "uncompressed:\n";
+    list_decoders(heif_compression_uncompressed);
+
+    std::cout << "VVIC decoders:\n";
+    list_decoders(heif_compression_VVC);
+  }
+
+
+  std::string fourcc_to_string(uint32_t fourcc)
   {
-    auto paths = heif_get_plugin_directories();
-    for (int i=0;paths[i];i++) {
-      std::cout << "plugin path: " << paths[i] << '\n';
+    char s[5];
+    s[0] = static_cast<char>((fourcc >> 24) & 0xFF);
+    s[1] = static_cast<char>((fourcc >> 16) & 0xFF);
+    s[2] = static_cast<char>((fourcc >> 8) & 0xFF);
+    s[3] = static_cast<char>((fourcc) & 0xFF);
+    s[4] = 0;
+
+    return s;
+  }
+
+
+  int check_for_valid_input_HEIF_file(const std::string& input_filename)
+  {
+    std::ifstream istr(input_filename.c_str(), std::ios_base::binary);
+    if (istr.fail()) {
+      fprintf(stderr, "Input file does not exist.\n");
+      return 10;
     }
 
-    if (paths[0] == nullptr) {
-      std::cout << "plugin path: plugins are disabled\n";
+    std::array<uint8_t, 4> length{};
+    istr.read((char*) length.data(), length.size());
+    uint32_t box_size = (length[0] << 24) + (length[1] << 16) + (length[2] << 8) + (length[3]);
+    if ((box_size < 16) || (box_size > 512)) {
+      fprintf(stderr, "Input file does not appear to start with a valid box length.");
+      if ((box_size & 0xFFFFFFF0) == 0xFFD8FFE0) {
+        fprintf(stderr, " Possibly could be a JPEG file instead.\n");
+      }
+      else {
+        fprintf(stderr, "\n");
+      }
+      return 1;
     }
 
-    heif_free_plugin_directories(paths);
+    std::vector<uint8_t> ftyp_bytes(box_size);
+    std::copy(length.begin(), length.end(), ftyp_bytes.begin());
+    istr.read((char*) ftyp_bytes.data() + 4, ftyp_bytes.size() - 4);
+
+    heif_error filetype_check = heif_has_compatible_filetype(ftyp_bytes.data(), (int) ftyp_bytes.size());
+    if (filetype_check.code != heif_error_Ok) {
+      fprintf(stderr, "Input file is not a supported format. %s\n", filetype_check.message);
+      return 1;
+    }
+
+    return 0;
   }
+
 }
diff -pruN 1.19.8-1/examples/common.h 1.20.1-1/examples/common.h
--- 1.19.8-1/examples/common.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/common.h	2025-07-02 13:05:31.000000000 +0000
@@ -27,6 +27,21 @@
 #ifndef LIBHEIF_COMMON_H
 #define LIBHEIF_COMMON_H
 
-void show_version();
+#include <libheif/heif.h>
+#include <string>
+
+namespace heif_examples {
+// Note: the same function is also exists in common_utils.h, but is not in the public API.
+  std::string fourcc_to_string(uint32_t fourcc);
+
+  void show_version();
+
+  void list_all_decoders();
+
+  void list_decoders(heif_compression_format format);
+
+// returns 0 on success, or program exit code in case of warning/error
+  int check_for_valid_input_HEIF_file(const std::string& input_filename);
+}
 
 #endif //LIBHEIF_COMMON_H
diff -pruN 1.19.8-1/examples/heif_dec.cc 1.20.1-1/examples/heif_dec.cc
--- 1.19.8-1/examples/heif_dec.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/heif_dec.cc	2025-07-02 13:05:31.000000000 +0000
@@ -29,6 +29,8 @@
 #include <getopt.h>
 #include <filesystem>
 #include "libheif/heif_items.h"
+#include "libheif/heif_experimental.h"
+#include "libheif/heif_sequences.h"
 
 #if defined(HAVE_UNISTD_H)
 
@@ -47,6 +49,7 @@
 #include <cctype>
 
 #include <libheif/heif.h>
+#include <libheif/heif_tai_timestamps.h>
 
 #include "heifio/encoder.h"
 
@@ -71,6 +74,7 @@
 
 #define UNUSED(x) (void)x
 
+
 static void show_help(const char* argv0)
 {
   std::filesystem::path p(argv0);
@@ -102,8 +106,11 @@ static void show_help(const char* argv0)
                "      --list-decoders            list all available decoders (built-in and plugins)\n"
                "      --tiles                    output all image tiles as separate images\n"
                "      --quiet                    do not output status messages to console\n"
+               "  -S, --sequence                 decode image sequence instead of still image\n"
                "  -C, --chroma-upsampling ALGO   Force chroma upsampling algorithm (nn = nearest-neighbor / bilinear)\n"
                "      --png-compression-level #  Set to integer between 0 (fastest) and 9 (best). Use -1 for default.\n"
+               "      --transparency-composition-mode MODE  Controls how transparent images are rendered when the output format\n"
+               "                                            support transparency. MODE must be one of: white, black, checkerboard.\n"
                "      --disable-limits           disable all security limits (do not use in production environment)\n";
 }
 
@@ -134,12 +141,14 @@ int option_list_decoders = 0;
 int option_png_compression_level = -1; // use zlib default
 int option_output_tiles = 0;
 int option_disable_limits = 0;
+int option_sequence = 0;
 std::string output_filename;
 
 std::string chroma_upsampling;
+std::string transparency_composition_mode = "checkerboard";
 
 #define OPTION_PNG_COMPRESSION_LEVEL 1000
-
+#define OPTION_TRANSPARENCY_COMPOSITION_MODE 1001
 
 static struct option long_options[] = {
     {(char* const) "quality",          required_argument, 0,                        'q'},
@@ -154,9 +163,11 @@ static struct option long_options[] = {
     {(char* const) "no-colons",        no_argument,       &option_no_colons,        1},
     {(char* const) "list-decoders",    no_argument,       &option_list_decoders,    1},
     {(char* const) "tiles",            no_argument,       &option_output_tiles,     1},
+    {(char* const) "sequence",            no_argument,       &option_sequence,     1},
     {(char* const) "help",             no_argument,       0,                        'h'},
     {(char* const) "chroma-upsampling", required_argument, 0,                     'C'},
     {(char* const) "png-compression-level", required_argument, 0,  OPTION_PNG_COMPRESSION_LEVEL},
+    {(char* const) "transparency-composition-mode", required_argument, 0,  OPTION_TRANSPARENCY_COMPOSITION_MODE},
     {(char* const) "version",          no_argument,       0,                        'v'},
     {(char* const) "disable-limits", no_argument, &option_disable_limits, 1},
     {nullptr, no_argument, nullptr, 0}
@@ -165,50 +176,6 @@ static struct option long_options[] = {
 
 #define MAX_DECODERS 20
 
-void list_decoders(heif_compression_format format)
-{
-  const heif_decoder_descriptor* decoders[MAX_DECODERS];
-  int n = heif_get_decoder_descriptors(format, decoders, MAX_DECODERS);
-
-  for (int i=0;i<n;i++) {
-    const char* id = heif_decoder_descriptor_get_id_name(decoders[i]);
-    if (id==nullptr) {
-      id = "---";
-    }
-
-    std::cout << "- " << id << " = " << heif_decoder_descriptor_get_name(decoders[i]) << "\n";
-  }
-}
-
-
-void list_all_decoders()
-{
-  std::cout << "AVC decoders:\n";
-  list_decoders(heif_compression_AVC);
-
-  std::cout << "AVIF decoders:\n";
-  list_decoders(heif_compression_AV1);
-
-  std::cout << "HEIC decoders:\n";
-  list_decoders(heif_compression_HEVC);
-
-  std::cout << "JPEG decoders:\n";
-  list_decoders(heif_compression_JPEG);
-
-  std::cout << "JPEG 2000 decoders:\n";
-  list_decoders(heif_compression_JPEG2000);
-
-  std::cout << "JPEG 2000 (HT) decoders:\n";
-  list_decoders(heif_compression_HTJ2K);
-
-  std::cout << "uncompressed:\n";
-  list_decoders(heif_compression_uncompressed);
-
-  std::cout << "VVIC decoders:\n";
-  list_decoders(heif_compression_VVC);
-}
-
-
 bool is_integer_string(const char* s)
 {
   if (strlen(s)==0) {
@@ -615,7 +582,7 @@ int main(int argc, char** argv)
   //while ((opt = getopt(argc, argv, "q:s")) != -1) {
   while (true) {
     int option_index = 0;
-    int c = getopt_long(argc, argv, "hq:sd:C:vo:", long_options, &option_index);
+    int c = getopt_long(argc, argv, "hq:sd:C:vo:S", long_options, &option_index);
     if (c == -1) {
       break;
     }
@@ -659,17 +626,31 @@ int main(int argc, char** argv)
           exit(5);
         }
         break;
+      case OPTION_TRANSPARENCY_COMPOSITION_MODE:
+        if (strcmp(optarg, "white") == 0 ||
+            strcmp(optarg, "black") == 0 ||
+            strcmp(optarg, "checkerboard") == 0) {
+            transparency_composition_mode = optarg;
+          }
+        else {
+          fprintf(stderr, "Unknown transparency composition mode. Must be one of: white, black, checkerboard.\n");
+          exit(5);
+        }
+        break;
       case 'v':
-        show_version();
+        heif_examples::show_version();
         return 0;
       case 'o':
         output_filename = optarg;
         break;
+      case 'S':
+        option_sequence = 1;
+        break;
     }
   }
 
   if (option_list_decoders) {
-    list_all_decoders();
+    heif_examples::list_all_decoders();
     return 0;
   }
 
@@ -766,36 +747,8 @@ int main(int argc, char** argv)
 
   // --- check whether input is a supported HEIF file
 
-  // TODO: when we are reading from named pipes, we probably should not consume any bytes
-  // just for file-type checking.
-  // TODO: check, whether reading from named pipes works at all.
-
-  std::ifstream istr(input_filename.c_str(), std::ios_base::binary);
-  if (istr.fail()) {
-    fprintf(stderr, "Input file does not exist.\n");
-    return 10;
-  }
-  std::array<uint8_t,4> length{};
-  istr.read((char*) length.data(), length.size());
-  uint32_t box_size = (length[0] << 24) + (length[1] << 16) + (length[2] << 8) + (length[3]);
-  if ((box_size < 16) || (box_size > 512)) {
-    fprintf(stderr, "Input file does not appear to start with a valid box length.");
-    if ((box_size & 0xFFFFFFF0) == 0xFFD8FFE0) {
-      fprintf(stderr, " Possibly could be a JPEG file instead.\n");
-    } else {
-      fprintf(stderr, "\n");
-    }
-    return 1;
-  }
-
-  std::vector<uint8_t> ftyp_bytes(box_size);
-  std::copy(length.begin(), length.end(), ftyp_bytes.begin());
-  istr.read((char*) ftyp_bytes.data() + 4, ftyp_bytes.size() - 4);
-
-  heif_error filetype_check = heif_has_compatible_filetype(ftyp_bytes.data(), (int)ftyp_bytes.size());
-  if (filetype_check.code != heif_error_Ok) {
-    fprintf(stderr, "Input file is not a supported format. %s\n", filetype_check.message);
-    return 1;
+  if (int ret = heif_examples::check_for_valid_input_HEIF_file(input_filename)) {
+    return ret;
   }
 
   // --- read the HEIF file
@@ -819,6 +772,140 @@ int main(int argc, char** argv)
   }
 
 
+  if (option_sequence) {
+    if (!heif_context_has_sequence(ctx)) {
+      std::cerr << "File contains no image sequence\n";
+      return 1;
+    }
+
+    int nTracks = heif_context_number_of_sequence_tracks(ctx);
+    std::cout << "number of tracks: " << nTracks << "\n";
+
+    std::vector<uint32_t> track_ids(nTracks);
+    heif_context_get_track_ids(ctx, track_ids.data());
+
+    for (uint32_t id : track_ids) {
+      heif_track* track = heif_context_get_track(ctx, id);
+      std::cout << "#" << id << " : " << heif_examples::fourcc_to_string(heif_track_get_track_handler_type(track));
+
+      if (heif_track_get_track_handler_type(track) == heif_track_type_image_sequence ||
+          heif_track_get_track_handler_type(track) == heif_track_type_video) {
+        uint16_t w,h;
+        heif_track_get_image_resolution(track, &w, &h);
+        std::cout << " " << w << "x" << h;
+      }
+      else {
+        uint32_t sample_entry_type = heif_track_get_sample_entry_type_of_first_cluster(track);
+        std::cout << "\n  sample entry type: " << heif_examples::fourcc_to_string(sample_entry_type);
+        if (sample_entry_type == heif_fourcc('u', 'r', 'i', 'm')) {
+          const char* uri;
+          err = heif_track_get_urim_sample_entry_uri_of_first_cluster(track, &uri);
+          if (err.code) {
+            std::cerr << "Cannot read 'urim'-track URI: " << err.message << "\n";
+          }
+          std::cout << "\n  URI: " << uri;
+          heif_string_release(uri);
+        }
+
+        // get metadata track samples
+
+        for (;;) {
+          struct heif_raw_sequence_sample* sample;
+          err = heif_track_get_next_raw_sequence_sample(track, &sample);
+          if (err.code != 0) {
+            break;
+          }
+
+          size_t dataSize;
+          const uint8_t* data = heif_raw_sequence_sample_get_data(sample, &dataSize);
+
+          std::cout << "\n  raw sample: ";
+          for (uint32_t i = 0; i < dataSize; i++) {
+            std::cout << " " << std::hex << (int)data[i];
+          }
+
+          heif_raw_sequence_sample_release(sample);
+        }
+      }
+
+      std::cout << "\n";
+    }
+
+    std::unique_ptr<heif_decoding_options, void(*)(heif_decoding_options*)> decode_options(heif_decoding_options_alloc(), heif_decoding_options_free);
+    encoder->UpdateDecodingOptions(nullptr, decode_options.get());
+
+    struct heif_track* track = heif_context_get_track(ctx, 0);
+
+    const char* track_contentId = heif_track_get_gimi_track_content_id(track);
+    if (track_contentId) {
+      std::cout << "track content ID: " << track_contentId << "\n";
+      heif_string_release(track_contentId);
+    }
+
+    const heif_tai_clock_info* taic = heif_track_get_tai_clock_info_of_first_cluster(track);
+    if (taic) {
+      std::cout << "taic: " << taic->time_uncertainty << " / " << taic->clock_resolution << " / "
+                << taic->clock_drift_rate << " / " << int(taic->clock_type) << "\n";
+    }
+
+    for (int i=0; ;i++) {
+      heif_image* out_image = nullptr;
+      int bit_depth = 8; // TODO
+      err = heif_track_decode_next_image(track, &out_image,
+                                         encoder->colorspace(false),
+                                         encoder->chroma(false, bit_depth),
+                                         decode_options.get());
+      if (err.code == heif_error_End_of_sequence) {
+        break;
+      }
+      else if (err.code) {
+        std::cerr << err.message << "\n";
+        return 1;
+      }
+
+      std::cout << "sample duration " << heif_image_get_duration(out_image) << "\n";
+
+      const char* contentID = heif_image_get_gimi_sample_content_id(out_image);
+      if (contentID) {
+        std::cout << "content ID " << contentID << "\n";
+        heif_string_release(contentID);
+      }
+
+      heif_tai_timestamp_packet* timestamp;
+      err = heif_image_get_tai_timestamp(out_image, &timestamp);
+      if (err.code) {
+        std::cerr << err.message << "\n";
+        return 10;
+      }
+      else if (timestamp) {
+        std::cout << "TAI timestamp: " << timestamp->tai_timestamp << "\n";
+        heif_tai_timestamp_packet_release(timestamp);
+      }
+
+      std::ostringstream s;
+      s << output_filename_stem;
+      s << "-" << i+1;
+      s << "." << output_filename_suffix;
+      std::string numbered_filename = s.str();
+
+      bool written = encoder->Encode(nullptr, out_image, numbered_filename);
+      if (!written) {
+        fprintf(stderr, "could not write image\n");
+      }
+      else {
+        if (!option_quiet) {
+          std::cout << "Written to " << numbered_filename << "\n";
+        }
+      }
+      heif_image_release(out_image);
+    }
+
+    heif_track_release(track);
+
+    return 0;
+  }
+
+
   int num_images = heif_context_get_number_of_top_level_images(ctx);
   if (num_images == 0) {
     fprintf(stderr, "File doesn't contain any images\n");
@@ -885,12 +972,39 @@ int main(int argc, char** argv)
 
     int ret;
 
+    auto* color_conversion_options_ext = heif_color_conversion_options_ext_alloc();
+    decode_options->color_conversion_options_ext = color_conversion_options_ext;
+
+
+    // If output file format does not support transparency, render in the selected composition mode.
+
+    if (!encoder->supports_alpha()) {
+      if (transparency_composition_mode == "white") {
+        color_conversion_options_ext->alpha_composition_mode = heif_alpha_composition_mode_solid_color;
+        color_conversion_options_ext->background_red = 0xFFFFU;
+        color_conversion_options_ext->background_green = 0xFFFFU;
+        color_conversion_options_ext->background_blue = 0xFFFFU;
+      }
+      else if (transparency_composition_mode == "black") {
+        color_conversion_options_ext->alpha_composition_mode = heif_alpha_composition_mode_solid_color;
+        color_conversion_options_ext->background_red = 0;
+        color_conversion_options_ext->background_green = 0;
+        color_conversion_options_ext->background_blue = 0;
+      }
+      else if (transparency_composition_mode == "checkerboard") {
+        color_conversion_options_ext->alpha_composition_mode = heif_alpha_composition_mode_checkerboard;
+      }
+    }
+
     if (option_output_tiles) {
       ret = decode_image_tiles(handle, numbered_output_filename_stem, output_filename_suffix, decode_options.get(), encoder);
     }
     else {
       ret = decode_single_image(handle, numbered_output_filename_stem, output_filename_suffix, decode_options.get(), encoder);
     }
+
+    heif_color_conversion_options_ext_free(color_conversion_options_ext);
+
     if (ret) {
       heif_image_handle_release(handle);
       return ret;
diff -pruN 1.19.8-1/examples/heif_enc.cc 1.20.1-1/examples/heif_enc.cc
--- 1.19.8-1/examples/heif_enc.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/heif_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -54,6 +54,10 @@
 #include "common.h"
 #include "libheif/api_structs.h"
 #include "libheif/heif_experimental.h"
+#include "libheif/heif_sequences.h"
+#include "libheif/heif_uncompressed.h"
+
+// --- command line parameters
 
 int master_alpha = 1;
 int thumb_alpha = 1;
@@ -77,6 +81,27 @@ uint16_t nclx_transfer_characteristic =
 uint16_t nclx_matrix_coefficients = 6;
 int nclx_full_range = true;
 
+// default to 30 fps
+uint32_t sequence_timebase = 30;
+uint32_t sequence_durations = 1;
+std::string vmt_metadata_file;
+
+int quality = 50;
+bool lossless = false;
+std::string output_filename;
+int logging_level = 0;
+bool option_show_parameters = false;
+int thumbnail_bbox_size = 0;
+int output_bit_depth = 10;
+bool force_enc_av1f = false;
+bool force_enc_vvc = false;
+bool force_enc_uncompressed = false;
+bool force_enc_jpeg = false;
+bool force_enc_jpeg2000 = false;
+bool force_enc_htj2k = false;
+bool use_tiling = false;
+bool encode_sequence = false;
+
 std::string property_pitm_description;
 
 // for benchmarking
@@ -107,6 +132,10 @@ const int OPTION_TILED_IMAGE_HEIGHT = 10
 const int OPTION_TILING_METHOD = 1013;
 const int OPTION_UNCI_COMPRESSION = 1014;
 const int OPTION_CUT_TILES = 1015;
+const int OPTION_SEQUENCES_TIMEBASE = 1016;
+const int OPTION_SEQUENCES_DURATIONS = 1017;
+const int OPTION_SEQUENCES_FPS = 1018;
+const int OPTION_VMT_METADATA_FILE = 1019;
 
 
 static struct option long_options[] = {
@@ -144,16 +173,21 @@ static struct option long_options[] = {
     {(char* const) "enable-metadata-compression", no_argument,       &metadata_compression, 1},
     {(char* const) "pitm-description",            required_argument, 0,                     OPTION_PITM_DESCRIPTION},
     {(char* const) "chroma-downsampling",         required_argument, 0, 'C'},
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
     {(char* const) "cut-tiles",                   required_argument, nullptr, OPTION_CUT_TILES},
-#endif
     {(char* const) "tiled-input",                 no_argument, 0, 'T'},
     {(char* const) "tiled-image-width",           required_argument, nullptr, OPTION_TILED_IMAGE_WIDTH},
     {(char* const) "tiled-image-height",          required_argument, nullptr, OPTION_TILED_IMAGE_HEIGHT},
     {(char* const) "tiled-input-x-y",             no_argument,       &tiled_input_x_y, 1},
     {(char* const) "tiling-method",               required_argument, nullptr, OPTION_TILING_METHOD},
     {(char* const) "add-pyramid-group",           no_argument,       &add_pyramid_group, 1},
-    {0, 0,                                                           0,  0},
+    {(char* const) "sequence",                    no_argument, 0, 'S'},
+    {(char* const) "timebase",                    required_argument,       nullptr, OPTION_SEQUENCES_TIMEBASE},
+    {(char* const) "duration",                    required_argument,       nullptr, OPTION_SEQUENCES_DURATIONS},
+    {(char* const) "fps",                         required_argument,       nullptr, OPTION_SEQUENCES_FPS},
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+    {(char* const) "vmt-metadata",                required_argument,       nullptr, OPTION_VMT_METADATA_FILE},
+#endif
+    {0, 0,                                                           0,  0}
 };
 
 
@@ -216,9 +250,9 @@ void show_help(const char* argv0)
             << "                                  (sharp-yuv makes edges look sharper when using YUV420 with bilinear chroma upsampling)\n"
             << "  --benchmark               measure encoding time, PSNR, and output file size\n"
             << "  --pitm-description TEXT   (experimental) set user description for primary image\n"
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-            << "  --cut-tiles #             cuts the input image into tiles of the given width\n"
-#endif
+            << "\n"
+            << "tiling:\n"
+            << "  --cut-tiles #             cuts the input image into square tiles of the given width\n"
             << "  -T,--tiled-input          input is a set of tile images (only provide one filename with two tile position numbers).\n"
             << "                            For example, 'tile-01-05.jpg' would be a valid input filename.\n"
             << "                            You only have to provide the filename of one tile as input, heif-enc will scan the directory\n"
@@ -227,10 +261,28 @@ void show_help(const char* argv0)
             << "  --tiled-image-height #    override image height of tiled image\n"
             << "  --tiled-input-x-y         usually, the first number in the input tile filename should be the y position.\n"
             << "                            With this option, this can be swapped so that the first number is x, the second number y.\n"
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES || WITH_UNCOMPRESSED_CODEC
+            << "  --tiling-method METHOD    choose one of these methods: grid"
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+               ", tili"
+#endif
+#if WITH_UNCOMPRESSED_CODEC
+               ", unci"
+#endif
+               ". The default is 'grid'.\n"
+#endif
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-            << "  --tiling-method METHOD    choose one of these methods: grid, tili, unci. The default is 'grid'.\n"
             << "  --add-pyramid-group       when several images are given, put them into a multi-resolution pyramid group.\n"
 #endif
+            << "\n"
+            << "sequences:\n"
+            << "  -S, --sequence            encode input images as sequence (input filenames with a number will pull in all files with this pattern).\n"
+            << "      --timebase #          set clock ticks/second for sequence\n"
+            << "      --duration #          set frame duration (default: 1)\n"
+            << "      --fps #               set timebase and duration based on fps\n"
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+            << "      --vmt-metadata FILE   encode metadata track from VMT file\n"
+#endif
             ;
 }
 
@@ -766,7 +818,7 @@ std::shared_ptr<input_tiles_generator> d
   return generator;
 }
 
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+
 class input_tiles_generator_cut_image : public input_tiles_generator
 {
 public:
@@ -808,7 +860,6 @@ private:
   uint32_t mWidth, mHeight;
   int mTileSize;
 };
-#endif
 
 
 // TODO: we have to attach the input image Exif and XMP to the tiled image
@@ -850,6 +901,8 @@ heif_image_handle* encode_tiled(heif_con
       return nullptr;
     }
   }
+#endif
+#if WITH_UNCOMPRESSED_CODEC
   else if (tiling_method == "unci") {
     heif_unci_image_parameters params{};
     params.version = 1;
@@ -861,7 +914,7 @@ heif_image_handle* encode_tiled(heif_con
 
     InputImage prototype_image = tile_generator->get_image(0,0, output_bit_depth);
 
-    heif_error error = heif_context_add_unci_image(ctx, &params, options, prototype_image.image.get(), &tiled_image);
+    heif_error error = heif_context_add_empty_unci_image(ctx, &params, options, prototype_image.image.get(), &tiled_image);
     if (error.code != 0) {
       std::cerr << "Could not generate unci image: " << error.message << "\n";
       return nullptr;
@@ -929,32 +982,21 @@ public:
 };
 
 
+int do_encode_images(heif_context*, heif_encoder*, heif_encoding_options* options, const std::vector<std::string>& args);
+int do_encode_sequence(heif_context*, heif_encoder*, heif_encoding_options* options, std::vector<std::string> args);
+
+
 int main(int argc, char** argv)
 {
   // This takes care of initializing libheif and also deinitializing it at the end to free all resources.
   LibHeifInitializer initializer;
 
-  int quality = 50;
-  bool lossless = false;
-  std::string output_filename;
-  int logging_level = 0;
-  bool option_show_parameters = false;
-  int thumbnail_bbox_size = 0;
-  int output_bit_depth = 10;
-  bool force_enc_av1f = false;
-  bool force_enc_vvc = false;
-  bool force_enc_uncompressed = false;
-  bool force_enc_jpeg = false;
-  bool force_enc_jpeg2000 = false;
-  bool force_enc_htj2k = false;
-  bool use_tiling = false;
-
   std::vector<std::string> raw_params;
 
 
   while (true) {
     int option_index = 0;
-    int c = getopt_long(argc, argv, "hq:Lo:vPp:t:b:Ae:C:T"
+    int c = getopt_long(argc, argv, "hq:Lo:vPp:t:b:Ae:C:TS"
 #if WITH_UNCOMPRESSED_CODEC
         "U"
 #endif
@@ -967,7 +1009,7 @@ int main(int argc, char** argv)
         show_help(argv[0]);
         return 0;
       case 'v':
-        show_version();
+        heif_examples::show_version();
         return 0;
       case 'q':
         quality = atoi(optarg);
@@ -1054,8 +1096,11 @@ int main(int argc, char** argv)
       case OPTION_TILING_METHOD:
         tiling_method = optarg;
         if (tiling_method != "grid"
+#if WITH_UNCOMPRESSED_CODEC
+            && tiling_method != "unci"
+#endif
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-            && tiling_method != "tili" && tiling_method != "unci"
+            && tiling_method != "tili"
 #endif
           ) {
           std::cerr << "Invalid tiling method '" << tiling_method << "'\n";
@@ -1107,6 +1152,29 @@ int main(int argc, char** argv)
       case 'T':
         use_tiling = true;
         break;
+      case 'S':
+        encode_sequence = true;
+        break;
+      case OPTION_SEQUENCES_TIMEBASE:
+        sequence_timebase = atoi(optarg);
+        break;
+      case OPTION_SEQUENCES_DURATIONS:
+        sequence_durations = atoi(optarg);
+        break;
+      case OPTION_SEQUENCES_FPS:
+        if (strcmp(optarg,"29.97")==0) {
+          sequence_durations = 1001;
+          sequence_timebase = 30000;
+        }
+        else {
+          double fps = std::atof(optarg);
+          sequence_timebase = 90000;
+          sequence_durations = (uint32_t)(90000 / fps + 0.5);
+        }
+        break;
+      case OPTION_VMT_METADATA_FILE:
+        vmt_metadata_file = optarg;
+        break;
     }
   }
 
@@ -1118,6 +1186,22 @@ int main(int argc, char** argv)
   if ((force_enc_av1f ? 1 : 0) + (force_enc_vvc ? 1 : 0) + (force_enc_uncompressed ? 1 : 0) + (force_enc_jpeg ? 1 : 0) +
       (force_enc_jpeg2000 ? 1 : 0) > 1) {
     std::cerr << "Choose at most one output compression format.\n";
+    return 5;
+  }
+
+  if (encode_sequence && (use_tiling || cut_tiles)) {
+    std::cerr << "Image sequences cannot be used together with tiling.\n";
+    return 5;
+  }
+
+  if (sequence_timebase <= 0) {
+    std::cerr << "Sequence clock tick rate cannot be zero.\n";
+    return 5;
+  }
+
+  if (sequence_durations <= 0) {
+    std::cerr << "Sequence frame durations cannot be zero.\n";
+    return 5;
   }
 
   if (logging_level > 0) {
@@ -1243,33 +1327,95 @@ int main(int argc, char** argv)
     }
   }
 
-  struct heif_error error;
+  std::vector<std::string> args;
+  for (; optind < argc; optind++) {
+    args.emplace_back(argv[optind]);
+  }
 
-  std::shared_ptr<heif_image> primary_image;
 
-  bool is_primary_image = true;
+  if (!lossless) {
+    heif_encoder_set_lossy_quality(encoder, quality);
+  }
 
-  std::vector<heif_item_id> encoded_image_ids;
+  heif_encoder_set_logging_level(encoder, logging_level);
 
-  for (; optind < argc; optind++) {
-    std::string input_filename = argv[optind];
+  set_params(encoder, raw_params);
+  struct heif_encoding_options* options = heif_encoding_options_alloc();
+  options->save_two_colr_boxes_when_ICC_and_nclx_available = (uint8_t) two_colr_boxes;
 
-    if (output_filename.empty()) {
-      std::string filename_without_suffix;
-      std::string::size_type dot_position = input_filename.find_last_of('.');
-      if (dot_position != std::string::npos) {
-        filename_without_suffix = input_filename.substr(0, dot_position);
-      }
-      else {
-        filename_without_suffix = input_filename;
-      }
+  if (chroma_downsampling == "average") {
+    options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
+    options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
+  }
+  else if (chroma_downsampling == "sharp-yuv") {
+    options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_sharp_yuv;
+    options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
+  }
+  else if (chroma_downsampling == "nearest-neighbor") {
+    options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_nearest_neighbor;
+    options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
+  }
 
-      std::string suffix = suffix_for_compression_format(compressionFormat);
-      output_filename = filename_without_suffix + '.' + suffix;
+
+  // --- if no output filename was given, synthesize one from the first input image filename
+
+  if (output_filename.empty()) {
+    const std::string& first_input_filename = args[0];
+
+    std::string filename_without_suffix;
+    std::string::size_type dot_position = first_input_filename.find_last_of('.');
+    if (dot_position != std::string::npos) {
+      filename_without_suffix = first_input_filename.substr(0, dot_position);
+    }
+    else {
+      filename_without_suffix = first_input_filename;
     }
 
+    std::string suffix = suffix_for_compression_format(compressionFormat);
+    output_filename = filename_without_suffix + '.' + suffix;
+  }
+
+
+  int ret;
+
+  if (!encode_sequence) {
+    ret = do_encode_images(context.get(), encoder, options, args);
+  }
+  else {
+    ret = do_encode_sequence(context.get(), encoder, options, args);
+  }
+
+  if (ret != 0) {
+    heif_encoding_options_free(options);
+    heif_encoder_release(encoder);
+    return ret;
+  }
+
+
+  // --- write HEIF file
+
+  heif_error error = heif_context_write_to_file(context.get(), output_filename.c_str());
+  if (error.code) {
+    std::cerr << error.message << "\n";
+    return 5;
+  }
+
+  heif_encoding_options_free(options);
+  heif_encoder_release(encoder);
+
+  return 0;
+}
+
 
-    // ==============================================================================
+int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding_options* options, const std::vector<std::string>& args)
+{
+  std::shared_ptr<heif_image> primary_image;
+
+  bool is_primary_image = true;
+
+  std::vector<heif_item_id> encoded_image_ids;
+
+  for (std::string input_filename : args) {
 
     InputImage input_image = load_image(input_filename, output_bit_depth);
 
@@ -1299,7 +1445,6 @@ int main(int argc, char** argv)
         return 5;
       }
     }
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
     else if (cut_tiles != 0) {
       auto cutting_tile_generator = std::make_shared<input_tiles_generator_cut_image>(input_filename.c_str(),
                                                                          cut_tiles, output_bit_depth);
@@ -1313,7 +1458,6 @@ int main(int argc, char** argv)
       tiling.image_height = cutting_tile_generator->get_image_height();
       tiling.number_of_extra_dimensions = 0;
     }
-#endif
 
     if (!primary_image) {
       primary_image = image;
@@ -1332,32 +1476,10 @@ int main(int argc, char** argv)
       return 5;
     }
 
-    if (!lossless) {
-      heif_encoder_set_lossy_quality(encoder, quality);
-    }
-
-    heif_encoder_set_logging_level(encoder, logging_level);
-
-    set_params(encoder, raw_params);
-    struct heif_encoding_options* options = heif_encoding_options_alloc();
     options->save_alpha_channel = (uint8_t) master_alpha;
-    options->save_two_colr_boxes_when_ICC_and_nclx_available = (uint8_t) two_colr_boxes;
     options->output_nclx_profile = nclx;
     options->image_orientation = input_image.orientation;
 
-    if (chroma_downsampling == "average") {
-      options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
-      options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
-    }
-    else if (chroma_downsampling == "sharp-yuv") {
-      options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_sharp_yuv;
-      options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
-    }
-    else if (chroma_downsampling == "nearest-neighbor") {
-      options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_nearest_neighbor;
-      options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
-    }
-
     if (premultiplied_alpha) {
       heif_image_set_premultiplied_alpha(image.get(), premultiplied_alpha);
     }
@@ -1365,18 +1487,16 @@ int main(int argc, char** argv)
     struct heif_image_handle* handle;
 
     if (use_tiling || cut_tiles > 0) {
-      handle = encode_tiled(context.get(), encoder, options, output_bit_depth, tile_generator, tiling);
+      handle = encode_tiled(context, encoder, options, output_bit_depth, tile_generator, tiling);
     }
     else {
-      error = heif_context_encode_image(context.get(),
+      error = heif_context_encode_image(context,
                                         image.get(),
                                         encoder,
                                         options,
                                         &handle);
       if (error.code != 0) {
-        heif_encoding_options_free(options);
         heif_nclx_color_profile_free(nclx);
-        heif_encoder_release(encoder);
         std::cerr << "Could not encode HEIF/AVIF file: " << error.message << "\n";
         return 1;
       }
@@ -1388,7 +1508,7 @@ int main(int argc, char** argv)
     }
 
     if (is_primary_image) {
-      heif_context_set_primary_image(context.get(), handle);
+      heif_context_set_primary_image(context, handle);
     }
 
     encoded_image_ids.push_back(heif_image_handle_get_item_id(handle));
@@ -1398,12 +1518,10 @@ int main(int argc, char** argv)
       // Note: we do not modify the EXIF Orientation here because we want it to match the HEIF transforms.
       // TODO: is this a good choice? Or should we set it to 1 (normal) so that other, faulty software will not transform it once more?
 
-      error = heif_context_add_exif_metadata(context.get(), handle,
+      error = heif_context_add_exif_metadata(context, handle,
                                              input_image.exif.data(), (int) input_image.exif.size());
       if (error.code != 0) {
-        heif_encoding_options_free(options);
         heif_nclx_color_profile_free(nclx);
-        heif_encoder_release(encoder);
         std::cerr << "Could not write EXIF metadata: " << error.message << "\n";
         return 1;
       }
@@ -1411,13 +1529,11 @@ int main(int argc, char** argv)
 
     // write XMP to HEIC
     if (!input_image.xmp.empty()) {
-      error = heif_context_add_XMP_metadata2(context.get(), handle,
+      error = heif_context_add_XMP_metadata2(context, handle,
                                              input_image.xmp.data(), (int) input_image.xmp.size(),
                                              metadata_compression ? heif_metadata_compression_deflate : heif_metadata_compression_off);
       if (error.code != 0) {
-        heif_encoding_options_free(options);
         heif_nclx_color_profile_free(nclx);
-        heif_encoder_release(encoder);
         std::cerr << "Could not write XMP metadata: " << error.message << "\n";
         return 1;
       }
@@ -1430,7 +1546,7 @@ int main(int argc, char** argv)
 
       options->save_alpha_channel = master_alpha && thumb_alpha;
 
-      error = heif_context_encode_thumbnail(context.get(),
+      error = heif_context_encode_thumbnail(context,
                                             image.get(),
                                             handle,
                                             encoder,
@@ -1438,9 +1554,7 @@ int main(int argc, char** argv)
                                             thumbnail_bbox_size,
                                             &thumbnail_handle);
       if (error.code) {
-        heif_encoding_options_free(options);
         heif_nclx_color_profile_free(nclx);
-        heif_encoder_release(encoder);
         std::cerr << "Could not generate thumbnail: " << error.message << "\n";
         return 5;
       }
@@ -1457,17 +1571,14 @@ int main(int argc, char** argv)
 #endif
 
     heif_image_handle_release(handle);
-    heif_encoding_options_free(options);
     heif_nclx_color_profile_free(nclx);
 
     is_primary_image = false;
   }
 
-  heif_encoder_release(encoder);
-
   if (!property_pitm_description.empty()) {
     heif_image_handle* primary_image_handle;
-    struct heif_error err = heif_context_get_primary_image_handle(context.get(), &primary_image_handle);
+    struct heif_error err = heif_context_get_primary_image_handle(context, &primary_image_handle);
     if (err.code) {
       std::cerr << "No primary image set, cannot set user description\n";
       return 5;
@@ -1480,7 +1591,7 @@ int main(int argc, char** argv)
     udes.name = nullptr;
     udes.tags = nullptr;
     udes.description = property_pitm_description.c_str();
-    err = heif_item_add_property_user_description(context.get(), pitm_id, &udes, nullptr);
+    err = heif_item_add_property_user_description(context, pitm_id, &udes, nullptr);
     if (err.code) {
       std::cerr << "Cannot set user description\n";
       return 5;
@@ -1491,7 +1602,7 @@ int main(int argc, char** argv)
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
   if (add_pyramid_group && encoded_image_ids.size() > 1) {
-    error = heif_context_add_pyramid_entity_group(context.get(), encoded_image_ids.data(), encoded_image_ids.size(), nullptr);
+    heif_error error = heif_context_add_pyramid_entity_group(context, encoded_image_ids.data(), encoded_image_ids.size(), nullptr);
     if (error.code) {
       std::cerr << "Cannot set multi-resolution pyramid: " << error.message << "\n";
       return 5;
@@ -1499,12 +1610,6 @@ int main(int argc, char** argv)
   }
 #endif
 
-  error = heif_context_write_to_file(context.get(), output_filename.c_str());
-  if (error.code) {
-    std::cerr << error.message << "\n";
-    return 5;
-  }
-
   if (run_benchmark) {
     double psnr = compute_psnr(primary_image.get(), output_filename);
     std::cout << "PSNR: " << std::setprecision(2) << std::fixed << psnr << " ";
@@ -1522,3 +1627,238 @@ int main(int argc, char** argv)
 
   return 0;
 }
+
+
+
+
+std::vector<std::string> deflate_input_filenames(const std::string& filename_example)
+{
+  std::regex pattern(R"((.*\D)?(\d+)(\..+)$)");
+  std::smatch match;
+
+  if (!std::regex_match(filename_example, match, pattern)) {
+    return {filename_example};
+  }
+
+  std::string prefix = match[1];
+
+  auto p = std::filesystem::absolute(std::filesystem::path(prefix));
+  std::filesystem::path directory = p.parent_path();
+  std::string filename_prefix = p.filename().string(); // TODO: we could also use u8string(), but it is not well supported in C++20
+  std::string number = match[2];
+  std::string suffix = match[3];
+
+
+  std::string patternString = filename_prefix + "(\\d+)" + suffix + "$";
+  pattern = patternString;
+
+  uint32_t digits = std::numeric_limits<uint32_t>::max();
+  uint32_t start = std::numeric_limits<uint32_t>::max();
+  uint32_t end = 0;
+
+  for (const auto& dirEntry : std::filesystem::directory_iterator(directory))
+  {
+    if (dirEntry.is_regular_file()) {
+      std::string s{dirEntry.path().filename().string()};
+
+      if (std::regex_match(s, match, pattern)) {
+        digits = std::min(digits, (uint32_t)match[1].length());
+
+        uint32_t number = std::stoi(match[1]);
+        start = std::min(start, number);
+        end = std::max(end, number);
+      }
+    }
+  }
+
+
+  std::vector<std::string> files;
+
+  for (uint32_t i=start;i<=end;i++)
+  {
+    std::stringstream sstr;
+
+    sstr << prefix << std::setw(digits) << std::setfill('0') << i << suffix;
+
+    std::filesystem::path p = directory / sstr.str();
+    files.emplace_back(p.string());
+  }
+
+  return files;
+}
+
+
+int encode_vmt_metadata_track(heif_context* context, heif_track* visual_track)
+{
+  // --- add metadata track
+
+  heif_track* track = nullptr;
+
+  heif_track_options* track_options = heif_track_options_alloc();
+  heif_track_options_set_timescale(track_options, 1000);
+
+  heif_context_add_uri_metadata_sequence_track(context, "vmt:metadata", track_options, &track);
+  heif_raw_sequence_sample* sample = heif_raw_sequence_sample_alloc();
+
+
+  std::ifstream istr(vmt_metadata_file.c_str());
+
+  std::regex pattern(R"((\d\d):(\d\d):(\d\d).(\d\d\d) -->$)");
+
+  static std::string prev_metadata;
+  static uint32_t prev_ts = 0;
+
+  std::string line;
+  while (std::getline(istr, line))
+  {
+    std::smatch match;
+
+    if (!std::regex_match(line, match, pattern)) {
+      continue;
+    }
+
+    std::string hh = match[1];
+    std::string mm = match[2];
+    std::string ss = match[3];
+    std::string mil = match[4];
+
+    uint32_t ts = (std::stoi(hh) * 3600 * 1000 +
+                   std::stoi(mm) * 60 * 1000 +
+                   std::stoi(ss) * 1000 +
+                   std::stoi(mil));
+
+    std::string concat;
+
+    while (std::getline(istr, line)) {
+      if (line.empty()) {
+        break;
+      }
+
+      concat += line + '\n';
+    }
+
+    if (prev_ts > 0) {
+      heif_raw_sequence_sample_set_data(sample, (const uint8_t*)prev_metadata.c_str(), prev_metadata.length()+1);
+      heif_raw_sequence_sample_set_duration(sample, ts - prev_ts);
+      heif_track_add_raw_sequence_sample(track, sample);
+    }
+
+    prev_ts = ts;
+    prev_metadata = concat;
+  }
+
+  // --- flush last metadata packet
+
+  heif_raw_sequence_sample_set_data(sample, (const uint8_t*)prev_metadata.c_str(), prev_metadata.length()+1);
+  heif_raw_sequence_sample_set_duration(sample, 1);
+  heif_track_add_raw_sequence_sample(track, sample);
+
+  // --- add track reference
+
+  heif_track_add_reference_to_track(track, heif_track_reference_type_description, visual_track);
+
+  // --- release all objects
+
+  heif_raw_sequence_sample_release(sample);
+  heif_track_options_release(track_options);
+  heif_track_release(track);
+
+  return 0;
+}
+
+
+int do_encode_sequence(heif_context* context, heif_encoder* encoder, heif_encoding_options* options, std::vector<std::string> args)
+{
+  if (args.size() == 1) {
+    args = deflate_input_filenames(args[0]);
+  }
+
+  size_t nImages = args.size();
+  size_t currImage = 0;
+
+  uint16_t image_width=0, image_height=0;
+
+  bool first_image = true;
+
+  heif_track* track = nullptr;
+
+  for (std::string input_filename : args) {
+    currImage++;
+    std::cout << "\rencoding sequence image " << currImage << "/" << nImages;
+    std::cout.flush();
+
+    InputImage input_image = load_image(input_filename, output_bit_depth);
+
+    std::shared_ptr<heif_image> image = input_image.image;
+
+    int w = heif_image_get_primary_width(image.get());
+    int h = heif_image_get_primary_height(image.get());
+
+    if (w > 0xFFFF || h > 0xFFFF) {
+      std::cerr << "maximum image size of 65535x65535 exceeded\n";
+      return 5;
+    }
+
+    if (first_image) {
+      heif_track_options* track_options = heif_track_options_alloc();
+
+      heif_track_options_set_timescale(track_options, sequence_timebase);
+
+      heif_context_set_sequence_timescale(context, sequence_timebase);
+
+      image_width = static_cast<uint16_t>(w);
+      image_height = static_cast<uint16_t>(h);
+
+      heif_context_add_visual_sequence_track(context,
+                                             image_width, image_height,
+                                             heif_track_type_video,
+                                             track_options,
+                                             nullptr,
+                                             &track);
+
+      heif_track_options_release(track_options);
+
+      first_image = false;
+    }
+
+    if (image_width != static_cast<uint16_t>(w) ||
+        image_height != static_cast<uint16_t>(h)) {
+      std::cerr << "image '" << input_filename << "' has size " << w << "x" << h
+                << " which is different from the first image size " << image_width << "x" << image_height << "\n";
+      return 5;
+    }
+
+    heif_color_profile_nclx* nclx;
+    heif_error error = create_output_nclx_profile_and_configure_encoder(encoder, &nclx, image, lossless);
+    if (error.code) {
+      std::cerr << error.message << "\n";
+      return 5;
+    }
+
+    options->save_alpha_channel = false; // TODO: sequences with alpha ?
+    options->image_orientation = heif_orientation_normal; // input_image.orientation;  TODO: sequence rotation
+
+    heif_image_set_duration(image.get(), sequence_durations);
+
+    error = heif_track_encode_sequence_image(track, image.get(), encoder, nullptr);
+    if (error.code) {
+      std::cerr << "Cannot encode sequence image: " << error.message << "\n";
+      return 5;
+    }
+
+    heif_nclx_color_profile_free(nclx);
+  }
+
+  std::cout << "\n";
+
+  if (!vmt_metadata_file.empty()) {
+    int ret = encode_vmt_metadata_track(context, track);
+    if (ret) {
+      return ret;
+    }
+  }
+
+  heif_track_release(track);
+
+  return 0;
+}
diff -pruN 1.19.8-1/examples/heif_info.cc 1.20.1-1/examples/heif_info.cc
--- 1.19.8-1/examples/heif_info.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/heif_info.cc	2025-07-02 13:05:31.000000000 +0000
@@ -40,6 +40,8 @@
 #include <libheif/heif.h>
 #include <libheif/heif_regions.h>
 #include <libheif/heif_properties.h>
+#include <libheif/heif_experimental.h>
+#include "libheif/heif_sequences.h"
 
 #include <fstream>
 #include <iostream>
@@ -79,17 +81,6 @@ static struct option long_options[] = {
     {0, 0,                                    0, 0}
 };
 
-// Note: the same function is also exists in common_utils.h, but is not in the public API.
-static const char* fourcc_to_string(uint32_t fourcc)
-{
-  static char fcc[5];
-  fcc[0] = (char) ((fourcc >> 24) & 0xFF);
-  fcc[1] = (char) ((fourcc >> 16) & 0xFF);
-  fcc[2] = (char) ((fourcc >> 8) & 0xFF);
-  fcc[3] = (char) ((fourcc >> 0) & 0xFF);
-  fcc[4] = 0;
-  return fcc;
-}
 
 void show_help(const char* argv0)
 {
@@ -156,7 +147,7 @@ int main(int argc, char** argv)
         output_filename = optarg;
         break;
       case 'v':
-        show_version();
+        heif_examples::show_version();
         return 0;
     }
   }
@@ -375,7 +366,7 @@ int main(int argc, char** argv)
     // --- color profile
 
     uint32_t profileType = heif_image_handle_get_color_profile_type(handle);
-    printf("  color profile: %s\n", profileType ? fourcc_to_string(profileType) : "no");
+    printf("  color profile: %s\n", profileType ? heif_examples::fourcc_to_string(profileType).c_str() : "no");
 
 
     // --- depth information
@@ -758,5 +749,110 @@ int main(int argc, char** argv)
   heif_image_handle_release(handle);
 #endif
 
+  // ==============================================================================
+
+  heif_context* context = ctx.get();
+
+  uint32_t nTracks = heif_context_number_of_sequence_tracks(context);
+
+  if (nTracks > 0) {
+    std::cout << "\n";
+
+    uint64_t timescale = heif_context_get_sequence_timescale(context);
+    std::cout << "sequence time scale: " << timescale << " Hz\n";
+
+    uint64_t duration = heif_context_get_sequence_duration(context);
+    std::cout << "sequence duration: " << ((double)duration)/(double)timescale << " seconds\n";
+
+              //    TrackOptions
+
+    std::vector<uint32_t> track_ids(nTracks);
+
+    heif_context_get_track_ids(context, track_ids.data());
+
+    for (uint32_t id : track_ids) {
+      heif_track* track = heif_context_get_track(context, id);
+
+      heif_track_type handler = heif_track_get_track_handler_type(track);
+      std::cout << "track " << id << "\n";
+      std::cout << "  handler: '" << heif_examples::fourcc_to_string(handler) << "' = ";
+
+      switch (handler) {
+        case heif_track_type_image_sequence:
+          std::cout << "image sequence\n";
+          break;
+        case heif_track_type_video:
+          std::cout << "video\n";
+          break;
+        case heif_track_type_metadata:
+          std::cout << "metadata\n";
+          break;
+        default:
+          std::cout << "unknown\n";
+          break;
+      }
+
+      if (handler == heif_track_type_video ||
+          handler == heif_track_type_image_sequence) {
+        uint16_t w, h;
+        heif_track_get_image_resolution(track, &w, &h);
+        std::cout << "  resolution: " << w << "x" << h << "\n";
+      }
+
+      uint32_t sampleEntryType = heif_track_get_sample_entry_type_of_first_cluster(track);
+      std::cout << "  sample entry type: " << heif_examples::fourcc_to_string(sampleEntryType) << "\n";
+
+      if (sampleEntryType == heif_fourcc('u', 'r', 'i', 'm')) {
+        const char* uri;
+        err = heif_track_get_urim_sample_entry_uri_of_first_cluster(track, &uri);
+        if (err.code) {
+          std::cerr << "error reading urim-track uri: " << err.message << "\n";
+        }
+        std::cout << "  uri: " << uri << "\n";
+        heif_string_release(uri);
+      }
+
+      std::cout << "  sample auxiliary information: ";
+      int nSampleAuxTypes = heif_track_get_number_of_sample_aux_infos(track);
+
+      std::vector<heif_sample_aux_info_type> aux_types(nSampleAuxTypes);
+      heif_track_get_sample_aux_info_types(track, aux_types.data());
+
+      for (size_t i=0;i<aux_types.size();i++) {
+        if (i) { std::cout << ", "; }
+        std::cout << heif_examples::fourcc_to_string(aux_types[i].type);
+      }
+
+      if (nSampleAuxTypes==0) {
+        std::cout << "---";
+      }
+      std::cout << "\n";
+
+
+      size_t nRefTypes = heif_track_get_number_of_track_reference_types(track);
+
+      if (nRefTypes > 0) {
+        std::cout << "  references:\n";
+        std::vector<uint32_t> refTypes(nRefTypes);
+        heif_track_get_track_reference_types(track, refTypes.data());
+
+        for (uint32_t refType : refTypes) {
+          std::cout << "    " << heif_examples::fourcc_to_string(refType) << ": ";
+
+          size_t n = heif_track_get_number_of_track_reference_of_type(track, refType);
+          std::vector<uint32_t> track_ids(n);
+          heif_track_get_references_from_track(track, refType, track_ids.data());
+          for (size_t i=0;i<n;i++) {
+            if (i>0) std::cout << ", ";
+            std::cout << "track#" << track_ids[i];
+          }
+          std::cout << "\n";
+        }
+      }
+
+      heif_track_release(track);
+    }
+  }
+
   return 0;
 }
diff -pruN 1.19.8-1/examples/heif_test.cc 1.20.1-1/examples/heif_test.cc
--- 1.19.8-1/examples/heif_test.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/heif_test.cc	2025-07-02 13:05:31.000000000 +0000
@@ -105,7 +105,7 @@ int main(int argc, char** argv)
         show_help(argv[0]);
         return 0;
       case 'v':
-        show_version();
+        heif_examples::show_version();
         return 0;
     }
   }
@@ -149,7 +149,7 @@ int main(int argc, char** argv)
           int height = img.get_height(channel);
           int bytes = (img.get_bits_per_pixel(channel) + 7) / 8;
 
-          int stride;
+          size_t stride;
           const uint8_t* p = img.get_plane(channel, &stride);
           for (int y = 0; y < height; y++) {
             fwrite(p + y * stride, width, bytes, stdout);
diff -pruN 1.19.8-1/examples/heif_thumbnailer.cc 1.20.1-1/examples/heif_thumbnailer.cc
--- 1.19.8-1/examples/heif_thumbnailer.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/examples/heif_thumbnailer.cc	2025-07-02 13:05:31.000000000 +0000
@@ -78,7 +78,7 @@ int main(int argc, char** argv)
         thumbnail_from_primary_image_only = true;
         break;
       case 'v':
-        show_version();
+        heif_examples::show_version();
         return 0;
       case 'h':
       default:
diff -pruN 1.19.8-1/examples/heif_view.cc 1.20.1-1/examples/heif_view.cc
--- 1.19.8-1/examples/heif_view.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/examples/heif_view.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,457 @@
+/*
+  libheif example application.
+
+  MIT License
+
+  Copyright (c) 2025 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 <cstring>
+#include <getopt.h>
+#include "libheif/heif_experimental.h"
+#include "libheif/heif_sequences.h"
+#include <libheif/heif_tai_timestamps.h>
+
+#if defined(HAVE_UNISTD_H)
+
+#include <unistd.h>
+
+#endif
+
+#include <fstream>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <cassert>
+#include <algorithm>
+#include <vector>
+#include <array>
+#include <cctype>
+#include <memory>
+
+#include <libheif/heif.h>
+
+#include "common.h"
+#include "sdl.hh"
+
+#if defined(_MSC_VER)
+#include "getopt.h"
+#endif
+
+#if !SDL_VERSION_ATLEAST(2, 0, 18)
+#define SDL_GetTicks64 SDL_GetTicks
+#endif
+
+#define UNUSED(x) (void)x
+
+
+static void show_help(const char* argv0)
+{
+  std::cerr << " " << argv0 << "  libheif version: " << heif_get_version() << "\n"
+            << "---------------------------------------\n"
+               "Usage: " << argv0 << " [options]  <input-file>\n"
+            << "\n"
+               "Options:\n"
+               "  -h, --help                     show help\n"
+               "  -v, --version                  show version\n"
+               "      --list-decoders            list all available decoders (built-in and plugins)\n"
+               "  -d, --decoder ID               use a specific decoder (see --list-decoders)\n"
+               "      --speedup FACTOR           increase playback speed by FACTOR\n"
+               "      --show-sai                 show sample auxiliary information\n"
+               "      --show-frame-duration      show each frame duration in milliseconds\n"
+               "      --show-track-metadata      show metadata attached to the track (e.g. TAI config)\n"
+               "      --show-metadata-text       show data in metadata track as text\n"
+               "      --show-metadata-hex        show data in metadata track as hex bytes\n"
+               "      --show-all                 show all extra information\n";
+}
+
+
+class ContextReleaser {
+public:
+  ContextReleaser(struct heif_context* ctx) : ctx_(ctx) {}
+
+  ~ContextReleaser()
+  {
+    heif_context_free(ctx_);
+  }
+
+private:
+  struct heif_context* ctx_;
+};
+
+
+int option_list_decoders = 0;
+double option_speedup = 1.0;
+bool option_show_sai = false;
+bool option_show_frame_duration = false;
+bool option_show_track_metadata = false;
+enum {
+  metadata_output_none,
+  metadata_output_text,
+  metadata_output_hex
+} option_metadata_output = metadata_output_none;
+
+const int OPTION_SPEEDUP = 1000;
+const int OPTION_SHOW_SAI = 1001;
+const int OPTION_SHOW_FRAME_DURATION = 1002;
+const int OPTION_SHOW_TRACK_METADATA = 1003;
+const int OPTION_SHOW_ALL = 1004;
+const int OPTION_SHOW_METADATA_TEXT = 1005;
+const int OPTION_SHOW_METADATA_HEX = 1006;
+
+static struct option long_options[] = {
+    {(char* const) "decoder",             required_argument, 0,                     'd'},
+    {(char* const) "list-decoders",       no_argument,       &option_list_decoders, 1},
+    {(char* const) "help",                no_argument,       0,                     'h'},
+    {(char* const) "version",             no_argument,       0,                     'v'},
+    {(char* const) "speedup",             required_argument, 0,                     OPTION_SPEEDUP},
+    {(char* const) "show-sai",            no_argument,       0,                     OPTION_SHOW_SAI},
+    {(char* const) "show-frame-duration", no_argument,       0,                     OPTION_SHOW_FRAME_DURATION},
+    {(char* const) "show-track-metadata", no_argument,       0,                     OPTION_SHOW_TRACK_METADATA},
+    {(char* const) "show-metadata-text",  no_argument,       0,                     OPTION_SHOW_METADATA_TEXT},
+    {(char* const) "show-metadata-hex",   no_argument,       0,                     OPTION_SHOW_METADATA_HEX},
+    {(char* const) "show-all",            no_argument,       0,                     OPTION_SHOW_ALL},
+    {nullptr,                             no_argument,       nullptr,               0}
+};
+
+
+void output_hex(const uint8_t* data, size_t size)
+{
+  std::cout << std::hex << std::setfill('0');
+
+  for (size_t i=0;i<size;i++) {
+    if (i%16==0) {
+      std::cout << std::setw(4) << i << " : ";
+    }
+
+    std::cout << std::setw(2) << (uint16_t)data[i];
+    if (i%16==7)
+      std::cout << "  ";
+    else if (i%16==15)
+      std::cout << '\n';
+    else
+      std::cout << ' ';
+  }
+
+  if (size%16 != 15) {
+    std::cout << '\n';
+  }
+
+  std::cout << std::dec << std::setfill(' ');
+}
+
+
+class LibHeifInitializer {
+public:
+  LibHeifInitializer() { heif_init(nullptr); }
+
+  ~LibHeifInitializer() { heif_deinit(); }
+};
+
+
+int main(int argc, char** argv)
+{
+  // This takes care of initializing libheif and also deinitializing it at the end to free all resources.
+  LibHeifInitializer initializer;
+
+  const char* decoder_id = nullptr;
+  bool show_frame_number = false;
+
+  while (true) {
+    int option_index = 0;
+    int c = getopt_long(argc, argv, "hvd:", long_options, &option_index);
+    if (c == -1) {
+      break;
+    }
+
+    switch (c) {
+      case 'd':
+        decoder_id = optarg;
+        break;
+      case 'h':
+        show_help(argv[0]);
+        return 0;
+      case 'v':
+        heif_examples::show_version();
+        return 0;
+      case OPTION_SPEEDUP:
+        option_speedup = atof(optarg);
+        if (option_speedup <= 0) {
+          std::cerr << "Speedup must be positive.\n";
+          return 5;
+        }
+        break;
+      case OPTION_SHOW_SAI:
+        option_show_sai = true;
+        show_frame_number = true;
+        break;
+      case OPTION_SHOW_FRAME_DURATION:
+        option_show_frame_duration = true;
+        show_frame_number = true;
+        break;
+      case OPTION_SHOW_TRACK_METADATA:
+        option_show_track_metadata = true;
+        show_frame_number = true;
+        break;
+      case OPTION_SHOW_ALL:
+        option_show_sai = true;
+        option_show_frame_duration = true;
+        option_show_track_metadata = true;
+        show_frame_number = true;
+        break;
+      case OPTION_SHOW_METADATA_TEXT:
+        option_metadata_output = metadata_output_text;
+        break;
+      case OPTION_SHOW_METADATA_HEX:
+        option_metadata_output = metadata_output_hex;
+        break;
+    }
+  }
+
+  if (option_list_decoders) {
+    heif_examples::list_all_decoders();
+    return 0;
+  }
+
+  if (optind + 1 != argc) {
+    // Need exactly one input filename as additional argument.
+    show_help(argv[0]);
+    return 5;
+  }
+
+  std::string input_filename(argv[optind++]);
+
+
+  // --- check whether input is a supported HEIF file
+
+  if (int ret = heif_examples::check_for_valid_input_HEIF_file(input_filename)) {
+    return ret;
+  }
+
+  // --- open the HEIF file
+
+  struct heif_context* ctx = heif_context_alloc();
+  if (!ctx) {
+    fprintf(stderr, "Could not create context object\n");
+    return 1;
+  }
+
+  ContextReleaser cr(ctx);
+  struct heif_error err;
+  err = heif_context_read_from_file(ctx, input_filename.c_str(), nullptr);
+  if (err.code != 0) {
+    std::cerr << "Could not read HEIF/AVIF file: " << err.message << "\n";
+    return 1;
+  }
+
+
+  // --- error if file contains no image sequence
+
+  if (!heif_context_has_sequence(ctx)) {
+    std::cerr << "File contains no image sequence\n";
+    return 1;
+  }
+
+
+  // --- get visual track
+
+  struct heif_track* track = heif_context_get_track(ctx, 0);
+
+  uint16_t w, h;
+  heif_track_get_image_resolution(track, &w, &h);
+
+
+  // --- show track properties
+
+  if (option_show_track_metadata) {
+    const char* track_contentId = heif_track_get_gimi_track_content_id(track);
+    if (track_contentId) {
+      std::cout << "track content ID: " << track_contentId << "\n";
+      heif_string_release(track_contentId);
+    }
+
+    const heif_tai_clock_info* taic = heif_track_get_tai_clock_info_of_first_cluster(track);
+    if (taic) {
+      std::cout << "track taic: " << taic->time_uncertainty << " / " << taic->clock_resolution << " / "
+                << taic->clock_drift_rate << " / " << int(taic->clock_type) << "\n";
+    }
+  }
+
+
+  // --- find metadata track
+
+  heif_track* metadata_track = nullptr;
+  if (option_metadata_output != metadata_output_none) {
+    uint32_t metadata_track_id;
+    size_t nMetadataTracks = heif_track_find_referring_tracks(track, heif_track_reference_type_description, &metadata_track_id, 1);
+
+    if (nMetadataTracks == 1) {
+      metadata_track = heif_context_get_track(ctx, metadata_track_id);
+    }
+  }
+
+
+  // --- open output window
+
+  SDL_YUV_Display sdlWindow;
+  bool success = sdlWindow.init(w, h, SDL_YUV_Display::SDL_CHROMA_420, "heif-view");
+  if (!success) {
+    std::cerr << "Cannot open output window\n";
+    return 10;
+  }
+
+  std::unique_ptr<heif_decoding_options, void (*)(heif_decoding_options*)> decode_options(heif_decoding_options_alloc(), heif_decoding_options_free);
+  decode_options->convert_hdr_to_8bit = true;
+  decode_options->decoder_id = decoder_id;
+
+
+  // --- decoding loop
+
+  for (int frameNr = 1;; frameNr++) {
+    heif_image* out_image = nullptr;
+
+    // --- decode next sequence image
+
+    err = heif_track_decode_next_image(track, &out_image,
+                                       heif_colorspace_YCbCr, // TODO: find best format
+                                       heif_chroma_420,
+                                       decode_options.get());
+    if (err.code == heif_error_End_of_sequence) {
+      break;
+    }
+    else if (err.code) {
+      std::cerr << err.message << "\n";
+      return 1;
+    }
+
+    // --- wait for image presentation time
+
+    uint32_t duration = heif_image_get_duration(out_image);
+    uint64_t timescale = heif_track_get_timescale(track);
+    uint64_t duration_ms = duration * 1000 / timescale;
+
+    if (option_show_frame_duration) {
+      std::cout << "sample duration " << heif_image_get_duration(out_image) << " = " << duration_ms << " ms\n";
+    }
+
+    static const uint64_t start_time = SDL_GetTicks64();
+    static uint64_t next_frame_pts = 0;
+
+    uint64_t now_time = SDL_GetTicks64();
+    uint64_t elapsed_time = (now_time - start_time);
+    if (elapsed_time < next_frame_pts) {
+      SDL_Delay((Uint32)(next_frame_pts - elapsed_time));
+    }
+
+    next_frame_pts += static_cast<uint64_t>(static_cast<double>(duration_ms) / option_speedup);
+
+
+    // --- display image
+
+    size_t stride_Y, stride_Cb, stride_Cr;
+    const uint8_t* p_Y = heif_image_get_plane_readonly2(out_image, heif_channel_Y, &stride_Y);
+    const uint8_t* p_Cb = heif_image_get_plane_readonly2(out_image, heif_channel_Cb, &stride_Cb);
+    const uint8_t* p_Cr = heif_image_get_plane_readonly2(out_image, heif_channel_Cr, &stride_Cr);
+
+    sdlWindow.display(p_Y, p_Cb, p_Cr, static_cast<int>(stride_Y), static_cast<int>(stride_Cb));
+
+    if (show_frame_number) {
+      std::cout << "--- frame " << frameNr << "\n";
+    }
+
+    if (option_show_sai) {
+      const char* contentID = heif_image_get_gimi_sample_content_id(out_image);
+      if (contentID) {
+        std::cout << "GIMI content id: " << contentID << "\n";
+        heif_string_release(contentID);
+      }
+
+      heif_tai_timestamp_packet* timestamp;
+      err = heif_image_get_tai_timestamp(out_image, &timestamp);
+      if (err.code) {
+        std::cerr << err.message << "\n";
+        return 10;
+      }
+      else if (timestamp) {
+        std::cout << "TAI timestamp: " << timestamp->tai_timestamp << "\n";
+        heif_tai_timestamp_packet_release(timestamp);
+      }
+    }
+
+    // --- get metadata sample
+
+    static heif_raw_sequence_sample* metadata_sample = nullptr;
+    static uint64_t metadata_sample_display_time = 0;
+
+    if (metadata_track && metadata_sample == nullptr) {
+      err = heif_track_get_next_raw_sequence_sample(metadata_track, &metadata_sample);
+      if (err.code != heif_error_Ok && err.code != heif_error_End_of_sequence) {
+        std::cerr << err.message << "\n";
+        return 10;
+      }
+    }
+
+    // --- show metadata
+
+    while (metadata_sample && static_cast<uint64_t>((double)metadata_sample_display_time / option_speedup) <= elapsed_time) {
+      size_t size;
+      const uint8_t* data = heif_raw_sequence_sample_get_data(metadata_sample, &size);
+
+      std::cout << "timestamp: " << ((double)metadata_sample_display_time)/1000.0f << "sec\n";
+
+      if (option_metadata_output == metadata_output_text) {
+        std::cout << ((const char*) data) << "\n";
+      }
+      else if (option_metadata_output == metadata_output_hex) {
+        output_hex(data, size);
+        std::cout << '\n';
+      }
+
+      uint64_t metadata_timescale = heif_track_get_timescale(metadata_track);
+      uint32_t metadata_duration = heif_raw_sequence_sample_get_duration(metadata_sample);
+      metadata_sample_display_time += metadata_duration * 1000 / metadata_timescale;
+
+      heif_raw_sequence_sample_release(metadata_sample);
+      metadata_sample = nullptr;
+
+      // get next metadata sample
+
+      err = heif_track_get_next_raw_sequence_sample(metadata_track, &metadata_sample);
+      if (err.code != heif_error_Ok && err.code != heif_error_End_of_sequence) {
+        std::cerr << err.message << "\n";
+        return 10;
+      }
+    }
+
+    heif_image_release(out_image);
+
+    if (sdlWindow.doQuit()) {
+      break;
+    }
+  }
+
+  sdlWindow.close();
+
+  heif_track_release(track);
+  heif_track_release(metadata_track);
+
+  return 0;
+}
diff -pruN 1.19.8-1/examples/sdl.cc 1.20.1-1/examples/sdl.cc
--- 1.19.8-1/examples/sdl.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/examples/sdl.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,298 @@
+/*
+  This file is part of dec265, an example application using libde265.
+
+  MIT License
+
+  Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
+
+  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 "sdl.hh"
+#include <assert.h>
+
+
+bool SDL_YUV_Display::init(int frame_width, int frame_height, enum SDL_Chroma chroma,
+                           const char* window_title)
+{
+  // reduce image size to a multiple of 8 (apparently required by YUV overlay)
+
+  frame_width  &= ~7;
+  frame_height &= ~7;
+
+  mChroma = chroma;
+
+  if (SDL_Init(SDL_INIT_VIDEO) < 0 ) {
+    printf("SDL_Init() failed: %s\n", SDL_GetError( ) );
+    SDL_Quit();
+    return false;
+  }
+
+  // set window title
+  mWindow = SDL_CreateWindow(window_title,
+    SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+    frame_width, frame_height, 0);
+  if (!mWindow) {
+    printf("SDL: Couldn't set video mode to %dx%d: %s\n",
+           frame_width, frame_height, SDL_GetError());
+    SDL_Quit();
+    return false;
+  }
+
+  Uint32 flags = 0;  // Empty flags prioritize SDL_RENDERER_ACCELERATED.
+  mRenderer = SDL_CreateRenderer(mWindow, -1, flags);
+  if (!mRenderer) {
+    printf("SDL: Couldn't create renderer: %s\n", SDL_GetError());
+    SDL_Quit();
+    return false;
+  }
+
+  Uint32 pixelFormat = 0;
+  switch (mChroma) {
+  case SDL_CHROMA_MONO: pixelFormat = SDL_PIXELFORMAT_YV12; break;
+  case SDL_CHROMA_420:  pixelFormat = SDL_PIXELFORMAT_YV12; break;
+  case SDL_CHROMA_422:  pixelFormat = SDL_PIXELFORMAT_YV12; break;
+  case SDL_CHROMA_444:  pixelFormat = SDL_PIXELFORMAT_YV12; break;
+  //case SDL_CHROMA_444:  pixelFormat = SDL_PIXELFORMAT_YV12; break;
+  default:
+    printf("Unsupported chroma: %d\n", mChroma);
+    SDL_Quit();
+    return false;
+  }
+
+  mTexture = SDL_CreateTexture(mRenderer, pixelFormat,
+    SDL_TEXTUREACCESS_STREAMING, frame_width, frame_height);
+  if (!mTexture ) {
+    printf("SDL: Couldn't create SDL texture: %s\n", SDL_GetError());
+    SDL_Quit();
+    return false;
+  }
+
+  rect.x = 0;
+  rect.y = 0;
+  rect.w = frame_width;
+  rect.h = frame_height;
+
+  mWindowOpen=true;
+
+  return true;
+}
+
+void SDL_YUV_Display::display(const unsigned char *Y,
+                              const unsigned char *U,
+                              const unsigned char *V,
+                              int stride, int chroma_stride)
+{
+  if (!mWindowOpen) return;
+  if (SDL_LockTexture(mTexture, nullptr,
+    reinterpret_cast<void**>(&mPixels), &mStride) < 0) return;
+
+  if (mChroma == SDL_CHROMA_420) {
+    display420(Y,U,V,stride,chroma_stride);
+  }
+  else if (mChroma == SDL_CHROMA_422) {
+    display422(Y,U,V,stride,chroma_stride);
+  }
+  else if (mChroma == SDL_CHROMA_444) {
+    display444as420(Y,U,V,stride,chroma_stride);
+    //display444as422(Y,U,V,stride,chroma_stride);
+  }
+  else if (mChroma == SDL_CHROMA_MONO) {
+    display400(Y,stride);
+  }
+
+  SDL_UnlockTexture(mTexture);
+
+  SDL_RenderCopy(mRenderer, mTexture, nullptr, nullptr);
+  SDL_RenderPresent(mRenderer);
+}
+
+
+void SDL_YUV_Display::display420(const unsigned char *Y,
+                                 const unsigned char *U,
+                                 const unsigned char *V,
+                                 int stride, int chroma_stride)
+{
+  if (stride == mStride && chroma_stride == mStride/2) {
+
+    // fast copy
+
+    memcpy(mPixels, Y, rect.w * rect.h);
+    memcpy(&mPixels[rect.w * rect.h], V, rect.w * rect.h / 4);
+    memcpy(&mPixels[(rect.w * rect.h) + (rect.w * rect.h / 4)], U, rect.w * rect.h / 4);
+  }
+  else {
+    // copy line by line, because sizes are different
+    uint8_t *dest = mPixels;
+
+    for (int y=0;y<rect.h;y++,dest+=mStride)
+      {
+        memcpy(dest, Y+stride*y, rect.w);
+      }
+
+    for (int y=0;y<rect.h/2;y++,dest+=mStride/2)
+      {
+        memcpy(dest, V+chroma_stride*y, rect.w/2);
+      }
+
+    for (int y=0;y<rect.h/2;y++,dest+=mStride/2)
+      {
+        memcpy(dest, U+chroma_stride*y, rect.w/2);
+      }
+  }
+}
+
+
+void SDL_YUV_Display::display400(const unsigned char *Y, int stride)
+{
+  uint8_t *dest = mPixels;
+  if (stride == mStride) {
+
+    // fast copy
+
+    memcpy(mPixels, Y, rect.w * rect.h);
+    dest += mStride * rect.h;
+  }
+  else {
+    // copy line by line, because sizes are different
+
+    for (int y=0;y<rect.h;y++,dest+=mStride)
+      {
+        memcpy(dest, Y+stride*y, rect.w);
+      }
+  }
+
+  // clear chroma planes
+
+  memset(dest, 0x80, mStride * rect.h / 2);
+}
+
+
+void SDL_YUV_Display::display422(const unsigned char* Y,
+                                 const unsigned char* U,
+                                 const unsigned char* V,
+                                 int stride, int chroma_stride)
+{
+  for (int y = 0; y < rect.h; y++) {
+    unsigned char* dstY = mPixels + y * mStride;
+    const unsigned char* Yp = Y + y * stride;
+
+    memcpy(dstY, Yp, rect.w);
+  }
+
+  for (int y = 0; y < rect.h; y += 2) {
+    unsigned char* dstV = mPixels + (y / 2) * mStride / 2 + rect.w * rect.h;
+    unsigned char* dstU = mPixels + (y / 2) * mStride / 2 + rect.w * rect.h + rect.w * rect.h / 4;
+
+    const unsigned char* Up = U + y * chroma_stride;
+    const unsigned char* Vp = V + y * chroma_stride;
+
+    memcpy(dstU, Up, rect.w / 2);
+    memcpy(dstV, Vp, rect.w / 2);
+  }
+}
+
+
+/* This converts down 4:4:4 input to 4:2:2 for display, as SDL does not support
+   any 4:4:4 pixel format.
+ */
+void SDL_YUV_Display::display444as422(const unsigned char *Y,
+                                      const unsigned char *U,
+                                      const unsigned char *V,
+                                      int stride, int chroma_stride)
+{
+  for (int y=0;y<rect.h;y++)
+    {
+      unsigned char* p = mPixels + y*mStride *2;
+
+      const unsigned char* Yp = Y + y*stride;
+      const unsigned char* Up = U + y*chroma_stride;
+      const unsigned char* Vp = V + y*chroma_stride;
+
+      for (int x=0;x<rect.w;x+=2) {
+        *p++ = Yp[x];
+        *p++ = Up[x];
+        *p++ = Yp[x+1];
+        *p++ = Vp[x];
+      }
+    }
+}
+
+
+void SDL_YUV_Display::display444as420(const unsigned char *Y,
+                                      const unsigned char *U,
+                                      const unsigned char *V,
+                                      int stride, int chroma_stride)
+{
+  for (int y=0;y<rect.h;y++)
+    {
+      unsigned char* p = mPixels + y*mStride;
+      memcpy(p, Y+y*stride, rect.w);
+    }
+
+  uint8_t *startV = mPixels + (rect.h*mStride);
+  uint8_t *startU = startV + (rect.h*mStride/2);
+  for (int y=0;y<rect.h;y+=2)
+    {
+      uint8_t* u = startU + y/2*mStride/2;
+      uint8_t* v = startV + y/2*mStride/2;
+
+      for (int x=0;x<rect.w;x+=2) {
+        u[x / 2] = static_cast<uint8_t>((U[(y + 0) * chroma_stride + x] + U[(y + 0) * chroma_stride + x + 1] +
+                                         U[(y + 1) * chroma_stride + x] + U[(y + 1) * chroma_stride + x + 1]) / 4);
+        v[x / 2] = static_cast<uint8_t>((V[(y + 0) * chroma_stride + x] + V[(y + 0) * chroma_stride + x + 1] +
+                                         V[(y + 1) * chroma_stride + x] + V[(y + 1) * chroma_stride + x + 1]) / 4);
+
+        //u[x/2] = U[y*chroma_stride + x];
+        //v[x/2] = V[y*chroma_stride + x];
+      }
+    }
+}
+
+
+bool SDL_YUV_Display::doQuit() const
+{
+  SDL_Event event;
+  while (SDL_PollEvent(&event)) {
+    if (event.type == SDL_QUIT) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void SDL_YUV_Display::close()
+{
+  if (mTexture) {
+    SDL_DestroyTexture(mTexture);
+    mTexture = nullptr;
+  }
+  if (mRenderer) {
+    SDL_DestroyRenderer(mRenderer);
+    mRenderer = nullptr;
+  }
+  if (mWindow) {
+    SDL_DestroyWindow(mWindow);
+    mWindow = nullptr;
+  }
+  SDL_Quit();
+
+  mWindowOpen=false;
+}
diff -pruN 1.19.8-1/examples/sdl.hh 1.20.1-1/examples/sdl.hh
--- 1.19.8-1/examples/sdl.hh	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/examples/sdl.hh	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,79 @@
+/*
+  This file is part of dec265, an example application using libde265.
+
+  MIT License
+
+  Copyright (c) 2013-2014 struktur AG, Dirk Farin <farin@struktur.de>
+
+  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 <SDL.h>
+
+
+class SDL_YUV_Display
+{
+public:
+
+  enum SDL_Chroma {
+    SDL_CHROMA_MONO=400,
+    SDL_CHROMA_420 =420,
+    SDL_CHROMA_422 =422,
+    SDL_CHROMA_444 =444
+  };
+
+  bool init(int frame_width, int frame_height, enum SDL_Chroma chroma, const char* window_title);
+  void display(const unsigned char *Y, const unsigned char *U, const unsigned char *V,
+               int stride, int chroma_stride);
+  void close();
+
+  bool doQuit() const;
+
+  bool isOpen() const { return mWindowOpen; }
+
+private:
+  SDL_Window *mWindow = nullptr;
+  SDL_Renderer *mRenderer = nullptr;
+  SDL_Texture *mTexture = nullptr;
+  SDL_Rect     rect;
+  bool         mWindowOpen;
+  uint8_t *mPixels = nullptr;
+  int mStride = 0;
+
+  SDL_Chroma mChroma;
+
+  void display400(const unsigned char *Y,
+                  int stride);
+  void display420(const unsigned char *Y,
+                  const unsigned char *U,
+                  const unsigned char *V,
+                  int stride, int chroma_stride);
+  void display422(const unsigned char *Y,
+                  const unsigned char *U,
+                  const unsigned char *V,
+                  int stride, int chroma_stride);
+  void display444as422(const unsigned char *Y,
+                       const unsigned char *U,
+                       const unsigned char *V,
+                       int stride, int chroma_stride);
+  void display444as420(const unsigned char *Y,
+                       const unsigned char *U,
+                       const unsigned char *V,
+                       int stride, int chroma_stride);
+};
diff -pruN 1.19.8-1/fuzzing/color_conversion_fuzzer.cc 1.20.1-1/fuzzing/color_conversion_fuzzer.cc
--- 1.19.8-1/fuzzing/color_conversion_fuzzer.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/fuzzing/color_conversion_fuzzer.cc	2025-07-02 13:05:31.000000000 +0000
@@ -250,15 +250,19 @@ extern "C" int LLVMFuzzerTestOneInput(co
   int output_bpp = 0; // Same as input.
   heif_encoding_options* options = heif_encoding_options_alloc();
 
+  heif_color_conversion_options_ext* options_ext = heif_color_conversion_options_ext_alloc();
+
   auto out_image_result = convert_colorspace(in_image,
                                              static_cast<heif_colorspace>(out_colorspace),
                                              static_cast<heif_chroma>(out_chroma),
                                              nullptr,
                                              output_bpp,
                                              options->color_conversion_options,
+                                             options_ext,
                                              heif_get_disabled_security_limits());
 
   heif_encoding_options_free(options);
+  heif_color_conversion_options_ext_free(options_ext);
 
   if (out_image_result.error) {
     // Conversion is not supported.
diff -pruN 1.19.8-1/fuzzing/file_fuzzer.cc 1.20.1-1/fuzzing/file_fuzzer.cc
--- 1.19.8-1/fuzzing/file_fuzzer.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/fuzzing/file_fuzzer.cc	2025-07-02 13:05:31.000000000 +0000
@@ -37,6 +37,7 @@ static void TestDecodeImage(struct heif_
   (void) primary;
   int width = heif_image_handle_get_width(handle);
   int height = heif_image_handle_get_height(handle);
+  (void)width; (void)height;
   assert(width >= 0);
   assert(height >= 0);
   int metadata_count = heif_image_handle_get_number_of_metadata_blocks(handle, nullptr);
@@ -47,6 +48,7 @@ static void TestDecodeImage(struct heif_
   int metadata_ids_count = heif_image_handle_get_list_of_metadata_block_IDs(handle, nullptr, metadata_ids,
                                                                             metadata_count);
   assert(metadata_count == metadata_ids_count);
+  (void)metadata_ids_count;
   for (int i = 0; i < metadata_count; i++) {
     heif_image_handle_get_metadata_type(handle, metadata_ids[i]);
     heif_image_handle_get_metadata_content_type(handle, metadata_ids[i]);
@@ -101,6 +103,10 @@ extern "C" int LLVMFuzzerTestOneInput(co
 
   ctx = heif_context_alloc();
   assert(ctx);
+
+  auto* limits = heif_context_get_security_limits(ctx);
+  limits->max_memory_block_size = 128 * 1024 * 1024; // 128 MB
+
   err = heif_context_read_from_memory(ctx, data, size, nullptr);
   if (err.code != heif_error_Ok) {
     // Not a valid HEIF file passed (which is most likely while fuzzing).
diff -pruN 1.19.8-1/go/heif/heif.go 1.20.1-1/go/heif/heif.go
--- 1.19.8-1/go/heif/heif.go	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/go/heif/heif.go	2025-07-02 13:05:31.000000000 +0000
@@ -182,6 +182,8 @@ const (
 	// Error during encoding or when writing to the output
 	ErrorEncoding = C.heif_error_Encoding_error
 
+	ErrorEndOfSequence = C.heif_error_End_of_sequence
+
 	// Application has asked for a color profile type that does not exist
 	ErrorColorProfileDoesNotExist = C.heif_error_Color_profile_does_not_exist
 
@@ -211,6 +213,8 @@ const (
 
 	SuberrorNoMetaBox = C.heif_suberror_No_meta_box
 
+	SuberrorNoMoovBox = C.heif_suberror_No_moov_box
+
 	SuberrorNoHdlrBox = C.heif_suberror_No_hdlr_box
 
 	SuberrorNoHvcCBox = C.heif_suberror_No_hvcC_box
diff -pruN 1.19.8-1/heifio/decoder_jpeg.cc 1.20.1-1/heifio/decoder_jpeg.cc
--- 1.19.8-1/heifio/decoder_jpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/decoder_jpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -303,8 +303,8 @@ heif_error loadJPEG(const char *filename
     err = heif_image_add_plane(image, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8);
     if (err.code) { goto cleanup; }
 
-    int y_stride;
-    uint8_t* py = heif_image_get_plane(image, heif_channel_Y, &y_stride);
+    size_t y_stride;
+    uint8_t* py = heif_image_get_plane2(image, heif_channel_Y, &y_stride);
 
 
     // read the image
@@ -385,11 +385,11 @@ heif_error loadJPEG(const char *filename
     err = heif_image_add_plane(image, heif_channel_Cr, cw, ch, 8);
     if (err.code) { goto cleanup; }
 
-    int stride[3];
+    size_t stride[3];
     uint8_t* p[3];
-    p[0] = heif_image_get_plane(image, heif_channel_Y, &stride[0]);
-    p[1] = heif_image_get_plane(image, heif_channel_Cb, &stride[1]);
-    p[2] = heif_image_get_plane(image, heif_channel_Cr, &stride[2]);
+    p[0] = heif_image_get_plane2(image, heif_channel_Y, &stride[0]);
+    p[1] = heif_image_get_plane2(image, heif_channel_Cb, &stride[1]);
+    p[2] = heif_image_get_plane2(image, heif_channel_Cr, &stride[2]);
 
     // read the image
 
diff -pruN 1.19.8-1/heifio/decoder_png.cc 1.20.1-1/heifio/decoder_png.cc
--- 1.19.8-1/heifio/decoder_png.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/decoder_png.cc	2025-07-02 13:05:31.000000000 +0000
@@ -255,15 +255,15 @@ heif_error loadPNG(const char* filename,
 
     heif_image_add_plane(image, heif_channel_Y, (int) width, (int) height, 8);
 
-    int y_stride;
-    int a_stride;
-    uint8_t* py = heif_image_get_plane(image, heif_channel_Y, &y_stride);
+    size_t y_stride;
+    size_t a_stride;
+    uint8_t* py = heif_image_get_plane2(image, heif_channel_Y, &y_stride);
     uint8_t* pa = nullptr;
 
     if (has_alpha) {
       heif_image_add_plane(image, heif_channel_Alpha, (int) width, (int) height, 8);
 
-      pa = heif_image_get_plane(image, heif_channel_Alpha, &a_stride);
+      pa = heif_image_get_plane2(image, heif_channel_Alpha, &a_stride);
     }
 
 
@@ -294,15 +294,15 @@ heif_error loadPNG(const char* filename,
 
     heif_image_add_plane(image, heif_channel_Y, (int) width, (int) height, output_bit_depth);
 
-    int y_stride;
-    int a_stride = 0;
-    uint16_t* py = (uint16_t*) heif_image_get_plane(image, heif_channel_Y, &y_stride);
+    size_t y_stride;
+    size_t a_stride = 0;
+    uint16_t* py = (uint16_t*) heif_image_get_plane2(image, heif_channel_Y, &y_stride);
     uint16_t* pa = nullptr;
 
     if (has_alpha) {
       heif_image_add_plane(image, heif_channel_Alpha, (int) width, (int) height, output_bit_depth);
 
-      pa = (uint16_t*) heif_image_get_plane(image, heif_channel_Alpha, &a_stride);
+      pa = (uint16_t*) heif_image_get_plane2(image, heif_channel_Alpha, &a_stride);
     }
 
     y_stride /= 2;
@@ -343,11 +343,11 @@ heif_error loadPNG(const char* filename,
     heif_image_add_plane(image, heif_channel_Y, (int) width, (int) height, 8);
     heif_image_add_plane(image, heif_channel_Alpha, (int) width, (int) height, 8);
 
-    int stride;
-    uint8_t* p = heif_image_get_plane(image, heif_channel_Y, &stride);
+    size_t stride;
+    uint8_t* p = heif_image_get_plane2(image, heif_channel_Y, &stride);
 
-    int strideA;
-    uint8_t* pA = heif_image_get_plane(image, heif_channel_Alpha, &strideA);
+    size_t strideA;
+    uint8_t* pA = heif_image_get_plane2(image, heif_channel_Alpha, &strideA);
 
     for (uint32_t y = 0; y < height; y++) {
       for (uint32_t x = 0; x < width; x++) {
@@ -366,8 +366,8 @@ heif_error loadPNG(const char* filename,
     heif_image_add_plane(image, heif_channel_interleaved, (int) width, (int) height,
                          has_alpha ? 32 : 24);
 
-    int stride;
-    uint8_t* p = heif_image_get_plane(image, heif_channel_interleaved, &stride);
+    size_t stride;
+    uint8_t* p = heif_image_get_plane2(image, heif_channel_interleaved, &stride);
 
     for (uint32_t y = 0; y < height; y++) {
       if (has_alpha) {
@@ -401,8 +401,8 @@ heif_error loadPNG(const char* filename,
 
     heif_image_add_plane(image, heif_channel_interleaved, (int) width, (int) height, output_bit_depth);
 
-    int stride;
-    uint8_t* p_out = (uint8_t*) heif_image_get_plane(image, heif_channel_interleaved, &stride);
+    size_t stride;
+    uint8_t* p_out = (uint8_t*) heif_image_get_plane2(image, heif_channel_interleaved, &stride);
 
     if (output_bit_depth==8) {
       // convert HDR to SDR
diff -pruN 1.19.8-1/heifio/decoder_tiff.cc 1.20.1-1/heifio/decoder_tiff.cc
--- 1.19.8-1/heifio/decoder_tiff.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/decoder_tiff.cc	2025-07-02 13:05:31.000000000 +0000
@@ -283,8 +283,8 @@ heif_error readMono(TIFF *tif, heif_imag
   }
   heif_image_add_plane(*image, heif_channel_Y, (int)width, (int)height, 8);
 
-  int y_stride;
-  uint8_t *py = heif_image_get_plane(*image, heif_channel_Y, &y_stride);
+  size_t y_stride;
+  uint8_t *py = heif_image_get_plane2(*image, heif_channel_Y, &y_stride);
   for (uint32_t row = 0; row < height; row++)
   {
     TIFFReadScanline(tif, py, row, 0);
@@ -313,8 +313,8 @@ heif_error readPixelInterleaveRGB(TIFF *
   heif_channel channel = heif_channel_interleaved;
   heif_image_add_plane(*image, channel, (int)width, (int)height, samplesPerPixel * 8);
 
-  int y_stride;
-  uint8_t *py = heif_image_get_plane(*image, channel, &y_stride);
+  size_t y_stride;
+  uint8_t *py = heif_image_get_plane2(*image, channel, &y_stride);
 
   tdata_t buf = _TIFFmalloc(TIFFScanlineSize(tif));
   for (uint32_t row = 0; row < height; row++)
@@ -354,8 +354,8 @@ heif_error readBandInterleaveRGB(TIFF *t
   heif_channel channel = heif_channel_interleaved;
   heif_image_add_plane(*image, channel, (int)width, (int)height, samplesPerPixel * 8);
 
-  int y_stride;
-  uint8_t *py = heif_image_get_plane(*image, channel, &y_stride);
+  size_t y_stride;
+  uint8_t *py = heif_image_get_plane2(*image, channel, &y_stride);
 
   uint8_t *buf = static_cast<uint8_t *>(_TIFFmalloc(TIFFScanlineSize(tif)));
   for (uint16_t i = 0; i < samplesPerPixel; i++)
diff -pruN 1.19.8-1/heifio/decoder_y4m.cc 1.20.1-1/heifio/decoder_y4m.cc
--- 1.19.8-1/heifio/decoder_y4m.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/decoder_y4m.cc	2025-07-02 13:05:31.000000000 +0000
@@ -125,10 +125,10 @@ heif_error loadY4M(const char *filename,
   heif_image_add_plane(image, heif_channel_Cb, (w + 1) / 2, (h + 1) / 2, 8);
   heif_image_add_plane(image, heif_channel_Cr, (w + 1) / 2, (h + 1) / 2, 8);
 
-  int y_stride, cb_stride, cr_stride;
-  uint8_t* py = heif_image_get_plane(image, heif_channel_Y, &y_stride);
-  uint8_t* pcb = heif_image_get_plane(image, heif_channel_Cb, &cb_stride);
-  uint8_t* pcr = heif_image_get_plane(image, heif_channel_Cr, &cr_stride);
+  size_t y_stride, cb_stride, cr_stride;
+  uint8_t* py = heif_image_get_plane2(image, heif_channel_Y, &y_stride);
+  uint8_t* pcb = heif_image_get_plane2(image, heif_channel_Cb, &cb_stride);
+  uint8_t* pcr = heif_image_get_plane2(image, heif_channel_Cr, &cr_stride);
 
   for (int y = 0; y < h; y++) {
     istr.read((char*) (py + y * y_stride), w);
diff -pruN 1.19.8-1/heifio/encoder.h 1.20.1-1/heifio/encoder.h
--- 1.19.8-1/heifio/encoder.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder.h	2025-07-02 13:05:31.000000000 +0000
@@ -43,6 +43,8 @@ public:
 
   virtual heif_chroma chroma(bool has_alpha, int bit_depth) const = 0;
 
+  virtual bool supports_alpha() const = 0;
+
   virtual void UpdateDecodingOptions(const struct heif_image_handle* handle,
                                      struct heif_decoding_options* options) const
   {
diff -pruN 1.19.8-1/heifio/encoder_jpeg.cc 1.20.1-1/heifio/encoder_jpeg.cc
--- 1.19.8-1/heifio/encoder_jpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_jpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -175,116 +175,121 @@ bool JpegEncoder::Encode(const struct he
 
   // --- Write EXIF
 
-  size_t exifsize = 0;
-  uint8_t* exifdata = GetExifMetaData(handle, &exifsize);
-  if (exifdata) {
-    if (exifsize > 4) {
-      static const uint8_t kExifMarker = JPEG_APP0 + 1;
-
-      uint32_t skip = (exifdata[0]<<24) | (exifdata[1]<<16) | (exifdata[2]<<8) | exifdata[3];
-      if (skip > (exifsize - 4)) {
-        fprintf(stderr, "Invalid EXIF data (offset too large)\n");
-        free(exifdata);
-        jpeg_destroy_compress(&cinfo);
-        fclose(fp);
-        return false;
-      }
-      skip += 4;
-
-      uint8_t* ptr = exifdata + skip;
-      size_t size = exifsize - skip;
-
-      if (size > std::numeric_limits<uint32_t>::max()) {
-        fprintf(stderr, "EXIF larger than 4GB is not supported");
-        free(exifdata);
-        jpeg_destroy_compress(&cinfo);
-        fclose(fp);
-        return false;
-      }
-
-      auto size32 = static_cast<uint32_t>(size);
-
-      // libheif by default normalizes the image orientation, so that we have to set the EXIF Orientation to "Horizontal (normal)"
-      modify_exif_orientation_tag_if_it_exists(ptr, size32, 1);
-      overwrite_exif_image_size_if_it_exists(ptr, size32, cinfo.image_width, cinfo.image_height);
-
-      // We have to limit the size for the memcpy, otherwise GCC warns that we exceed the maximum size.
-      if (size>0x1000000) {
-        size = 0x1000000;
-      }
-
-      std::vector<uint8_t> jpegExifMarkerData(6+size);
-      memcpy(jpegExifMarkerData.data()+6, ptr, size);
-      jpegExifMarkerData[0]='E';
-      jpegExifMarkerData[1]='x';
-      jpegExifMarkerData[2]='i';
-      jpegExifMarkerData[3]='f';
-      jpegExifMarkerData[4]=0;
-      jpegExifMarkerData[5]=0;
-
-      ptr = jpegExifMarkerData.data();
-      size = jpegExifMarkerData.size();
+  if (handle) {
+    size_t exifsize = 0;
+    uint8_t* exifdata = GetExifMetaData(handle, &exifsize);
+    if (exifdata) {
+      if (exifsize > 4) {
+        static const uint8_t kExifMarker = JPEG_APP0 + 1;
+
+        uint32_t skip = (exifdata[0] << 24) | (exifdata[1] << 16) | (exifdata[2] << 8) | exifdata[3];
+        if (skip > (exifsize - 4)) {
+          fprintf(stderr, "Invalid EXIF data (offset too large)\n");
+          free(exifdata);
+          jpeg_destroy_compress(&cinfo);
+          fclose(fp);
+          return false;
+        }
+        skip += 4;
+
+        uint8_t* ptr = exifdata + skip;
+        size_t size = exifsize - skip;
+
+        if (size > std::numeric_limits<uint32_t>::max()) {
+          fprintf(stderr, "EXIF larger than 4GB is not supported");
+          free(exifdata);
+          jpeg_destroy_compress(&cinfo);
+          fclose(fp);
+          return false;
+        }
+
+        auto size32 = static_cast<uint32_t>(size);
+
+        // libheif by default normalizes the image orientation, so that we have to set the EXIF Orientation to "Horizontal (normal)"
+        modify_exif_orientation_tag_if_it_exists(ptr, size32, 1);
+        overwrite_exif_image_size_if_it_exists(ptr, size32, cinfo.image_width, cinfo.image_height);
+
+        // We have to limit the size for the memcpy, otherwise GCC warns that we exceed the maximum size.
+        if (size > 0x1000000) {
+          size = 0x1000000;
+        }
+
+        std::vector<uint8_t> jpegExifMarkerData(6 + size);
+        memcpy(jpegExifMarkerData.data() + 6, ptr, size);
+        jpegExifMarkerData[0] = 'E';
+        jpegExifMarkerData[1] = 'x';
+        jpegExifMarkerData[2] = 'i';
+        jpegExifMarkerData[3] = 'f';
+        jpegExifMarkerData[4] = 0;
+        jpegExifMarkerData[5] = 0;
+
+        ptr = jpegExifMarkerData.data();
+        size = jpegExifMarkerData.size();
+
+        while (size > MAX_BYTES_IN_MARKER) {
+          jpeg_write_marker(&cinfo, kExifMarker, ptr,
+                            static_cast<unsigned int>(MAX_BYTES_IN_MARKER));
+
+          ptr += MAX_BYTES_IN_MARKER;
+          size -= MAX_BYTES_IN_MARKER;
+        }
 
-      while (size > MAX_BYTES_IN_MARKER) {
         jpeg_write_marker(&cinfo, kExifMarker, ptr,
-                          static_cast<unsigned int>(MAX_BYTES_IN_MARKER));
-
-        ptr += MAX_BYTES_IN_MARKER;
-        size -= MAX_BYTES_IN_MARKER;
+                          static_cast<unsigned int>(size));
       }
 
-      jpeg_write_marker(&cinfo, kExifMarker, ptr,
-                        static_cast<unsigned int>(size));
+      free(exifdata);
     }
-
-    free(exifdata);
   }
 
   // --- Write XMP
 
   // spec: https://raw.githubusercontent.com/adobe/xmp-docs/master/XMPSpecifications/XMPSpecificationPart3.pdf
 
-  auto xmp = get_xmp_metadata(handle);
-  if (xmp.size() > 65502) {
-    fprintf(stderr, "XMP data too large, ExtendedXMP is not supported yet.\n");
-  }
-  else if (!xmp.empty()) {
-    std::vector<uint8_t> xmpWithId;
-    xmpWithId.resize(xmp.size() + strlen(JPEG_XMP_MARKER_ID)+1);
-    strcpy((char*)xmpWithId.data(), JPEG_XMP_MARKER_ID);
-    memcpy(xmpWithId.data() + strlen(JPEG_XMP_MARKER_ID) + 1, xmp.data(), xmp.size());
+  if (handle) {
+    auto xmp = get_xmp_metadata(handle);
+    if (xmp.size() > 65502) {
+      fprintf(stderr, "XMP data too large, ExtendedXMP is not supported yet.\n");
+    }
+    else if (!xmp.empty()) {
+      std::vector<uint8_t> xmpWithId;
+      xmpWithId.resize(xmp.size() + strlen(JPEG_XMP_MARKER_ID) + 1);
+      strcpy((char*) xmpWithId.data(), JPEG_XMP_MARKER_ID);
+      memcpy(xmpWithId.data() + strlen(JPEG_XMP_MARKER_ID) + 1, xmp.data(), xmp.size());
 
-    jpeg_write_marker(&cinfo, JPEG_XMP_MARKER, xmpWithId.data(), static_cast<unsigned int>(xmpWithId.size()));
+      jpeg_write_marker(&cinfo, JPEG_XMP_MARKER, xmpWithId.data(), static_cast<unsigned int>(xmpWithId.size()));
+    }
   }
 
   // --- Write ICC
 
-  size_t profile_size = heif_image_handle_get_raw_color_profile_size(handle);
-  if (profile_size > 0) {
-    uint8_t* profile_data = static_cast<uint8_t*>(malloc(profile_size));
-    heif_image_handle_get_raw_color_profile(handle, profile_data);
-    jpeg_write_icc_profile(&cinfo, profile_data, (unsigned int) profile_size);
-    free(profile_data);
-  }
+  if (handle) {
+    size_t profile_size = heif_image_handle_get_raw_color_profile_size(handle);
+    if (profile_size > 0) {
+      uint8_t* profile_data = static_cast<uint8_t*>(malloc(profile_size));
+      heif_image_handle_get_raw_color_profile(handle, profile_data);
+      jpeg_write_icc_profile(&cinfo, profile_data, (unsigned int) profile_size);
+      free(profile_data);
+    }
 
 
-  if (heif_image_get_bits_per_pixel(image, heif_channel_Y) != 8) {
-    fprintf(stderr, "JPEG writer cannot handle image with >8 bpp.\n");
-    jpeg_destroy_compress(&cinfo);
-    fclose(fp);
-    return false;
+    if (heif_image_get_bits_per_pixel(image, heif_channel_Y) != 8) {
+      fprintf(stderr, "JPEG writer cannot handle image with >8 bpp.\n");
+      jpeg_destroy_compress(&cinfo);
+      fclose(fp);
+      return false;
+    }
   }
 
-
-  int stride_y;
-  const uint8_t* row_y = heif_image_get_plane_readonly(image, heif_channel_Y,
-                                                       &stride_y);
-  int stride_u;
-  const uint8_t* row_u = heif_image_get_plane_readonly(image, heif_channel_Cb,
-                                                       &stride_u);
-  int stride_v;
-  const uint8_t* row_v = heif_image_get_plane_readonly(image, heif_channel_Cr,
-                                                       &stride_v);
+  size_t stride_y;
+  const uint8_t* row_y = heif_image_get_plane_readonly2(image, heif_channel_Y,
+                                                        &stride_y);
+  size_t stride_u;
+  const uint8_t* row_u = heif_image_get_plane_readonly2(image, heif_channel_Cb,
+                                                        &stride_u);
+  size_t stride_v;
+  const uint8_t* row_v = heif_image_get_plane_readonly2(image, heif_channel_Cr,
+                                                        &stride_v);
 
   JSAMPARRAY buffer = cinfo.mem->alloc_sarray(
       reinterpret_cast<j_common_ptr>(&cinfo), JPOOL_IMAGE,
diff -pruN 1.19.8-1/heifio/encoder_jpeg.h 1.20.1-1/heifio/encoder_jpeg.h
--- 1.19.8-1/heifio/encoder_jpeg.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_jpeg.h	2025-07-02 13:05:31.000000000 +0000
@@ -50,6 +50,8 @@ public:
     return heif_chroma_420;
   }
 
+  bool supports_alpha() const override { return false; }
+
   void UpdateDecodingOptions(const struct heif_image_handle* handle,
                              struct heif_decoding_options* options) const override;
 
diff -pruN 1.19.8-1/heifio/encoder_png.cc 1.20.1-1/heifio/encoder_png.cc
--- 1.19.8-1/heifio/encoder_png.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_png.cc	2025-07-02 13:05:31.000000000 +0000
@@ -93,71 +93,76 @@ bool PngEncoder::Encode(const struct hei
 
   // --- write ICC profile
 
-  size_t profile_size = heif_image_handle_get_raw_color_profile_size(handle);
-  if (profile_size > 0) {
-    uint8_t* profile_data = static_cast<uint8_t*>(malloc(profile_size));
-    heif_image_handle_get_raw_color_profile(handle, profile_data);
-    char profile_name[] = "unknown";
-    png_set_iCCP(png_ptr, info_ptr, profile_name, PNG_COMPRESSION_TYPE_BASE,
+  if (handle) {
+    size_t profile_size = heif_image_handle_get_raw_color_profile_size(handle);
+    if (profile_size > 0) {
+      uint8_t* profile_data = static_cast<uint8_t*>(malloc(profile_size));
+      heif_image_handle_get_raw_color_profile(handle, profile_data);
+      char profile_name[] = "unknown";
+      png_set_iCCP(png_ptr, info_ptr, profile_name, PNG_COMPRESSION_TYPE_BASE,
 #if PNG_LIBPNG_VER < 10500
-        (png_charp)profile_data,
+              (png_charp)profile_data,
 #else
-                 (png_const_bytep) profile_data,
+                   (png_const_bytep) profile_data,
 #endif
-                 (png_uint_32) profile_size);
-    free(profile_data);
+                   (png_uint_32) profile_size);
+      free(profile_data);
+    }
   }
 
-
   // --- write EXIF metadata
 
 #ifdef PNG_eXIf_SUPPORTED
-  size_t exifsize = 0;
-  uint8_t* exifdata = GetExifMetaData(handle, &exifsize);
-  if (exifdata) {
-    if (exifsize > 4) {
-      uint32_t skip = (exifdata[0]<<24) | (exifdata[1]<<16) | (exifdata[2]<<8) | exifdata[3];
-      if (skip < (exifsize - 4)) {
-        skip += 4;
-        uint8_t* ptr = exifdata + skip;
-        size_t size = exifsize - skip;
-
-        // libheif by default normalizes the image orientation, so that we have to set the EXIF Orientation to "Horizontal (normal)"
-        modify_exif_orientation_tag_if_it_exists(ptr, (int)size, 1);
-        overwrite_exif_image_size_if_it_exists(ptr, (int)size, width, height);
+  if (handle) {
+    size_t exifsize = 0;
+    uint8_t* exifdata = GetExifMetaData(handle, &exifsize);
+    if (exifdata) {
+      if (exifsize > 4) {
+        uint32_t skip = (exifdata[0] << 24) | (exifdata[1] << 16) | (exifdata[2] << 8) | exifdata[3];
+        if (skip < (exifsize - 4)) {
+          skip += 4;
+          uint8_t* ptr = exifdata + skip;
+          size_t size = exifsize - skip;
+
+          // libheif by default normalizes the image orientation, so that we have to set the EXIF Orientation to "Horizontal (normal)"
+          modify_exif_orientation_tag_if_it_exists(ptr, (int) size, 1);
+          overwrite_exif_image_size_if_it_exists(ptr, (int) size, width, height);
 
-        png_set_eXIf_1(png_ptr, info_ptr, (png_uint_32)size, ptr);
+          png_set_eXIf_1(png_ptr, info_ptr, (png_uint_32) size, ptr);
+        }
       }
-    }
 
-    free(exifdata);
+      free(exifdata);
+    }
   }
 #endif
 
   // --- write XMP metadata
 
 #ifdef PNG_iTXt_SUPPORTED
-  // spec: https://raw.githubusercontent.com/adobe/xmp-docs/master/XMPSpecifications/XMPSpecificationPart3.pdf
-  std::vector<uint8_t> xmp = get_xmp_metadata(handle);
-  if (!xmp.empty()) {
-    // make sure that XMP string is always null terminated.
-    if (xmp.back() != 0) {
-      xmp.push_back(0);
-    }
+  if (handle) {
+    // spec: https://raw.githubusercontent.com/adobe/xmp-docs/master/XMPSpecifications/XMPSpecificationPart3.pdf
+    std::vector<uint8_t> xmp = get_xmp_metadata(handle);
+    if (!xmp.empty()) {
+      // make sure that XMP string is always null terminated.
+      if (xmp.back() != 0) {
+        xmp.push_back(0);
+      }
 
-    // compute XMP string length
-    size_t text_length = 0;
-    while (xmp[text_length] != 0) {
-      text_length++;
-    }
+      // compute XMP string length
+      size_t text_length = 0;
+      while (xmp[text_length] != 0) {
+        text_length++;
+      }
 
-    png_text xmp_text{}; // important to zero-initialize the structure so that the remaining fields are NULL !
-    xmp_text.compression = PNG_ITXT_COMPRESSION_NONE;
-    xmp_text.key = (char*) "XML:com.adobe.xmp";
-    xmp_text.text = (char*) xmp.data();
-    xmp_text.text_length = 0; // should be 0 for ITXT according the libpng documentation
-    xmp_text.itxt_length = text_length;
-    png_set_text(png_ptr, info_ptr, &xmp_text, 1);
+      png_text xmp_text{}; // important to zero-initialize the structure so that the remaining fields are NULL !
+      xmp_text.compression = PNG_ITXT_COMPRESSION_NONE;
+      xmp_text.key = (char*) "XML:com.adobe.xmp";
+      xmp_text.text = (char*) xmp.data();
+      xmp_text.text_length = 0; // should be 0 for ITXT according the libpng documentation
+      xmp_text.itxt_length = text_length;
+      png_set_text(png_ptr, info_ptr, &xmp_text, 1);
+    }
   }
 #endif
 
@@ -165,9 +170,9 @@ bool PngEncoder::Encode(const struct hei
 
   uint8_t** row_pointers = new uint8_t* [height];
 
-  int stride_rgb;
-  const uint8_t* row_rgb = heif_image_get_plane_readonly(image,
-                                                         heif_channel_interleaved, &stride_rgb);
+  size_t stride_rgb;
+  const uint8_t* row_rgb = heif_image_get_plane_readonly2(image,
+                                                          heif_channel_interleaved, &stride_rgb);
 
   for (int y = 0; y < height; ++y) {
     row_pointers[y] = const_cast<uint8_t*>(&row_rgb[y * stride_rgb]);
@@ -179,7 +184,7 @@ bool PngEncoder::Encode(const struct hei
     int shift = 16 - input_bpp;
     if (shift > 0) {
       for (int y = 0; y < height; ++y) {
-        for (int x = 0; x < stride_rgb; x += 2) {
+        for (size_t x = 0; x < stride_rgb; x += 2) {
           uint8_t* p = (&row_pointers[y][x]);
           int v = (p[0] << 8) | p[1];
           v = (v << shift) | (v >> (16 - shift));
diff -pruN 1.19.8-1/heifio/encoder_png.h 1.20.1-1/heifio/encoder_png.h
--- 1.19.8-1/heifio/encoder_png.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_png.h	2025-07-02 13:05:31.000000000 +0000
@@ -64,6 +64,8 @@ public:
     }
   }
 
+  bool supports_alpha() const override { return true; }
+
   bool Encode(const struct heif_image_handle* handle,
               const struct heif_image* image, const std::string& filename) override;
 
diff -pruN 1.19.8-1/heifio/encoder_tiff.cc 1.20.1-1/heifio/encoder_tiff.cc
--- 1.19.8-1/heifio/encoder_tiff.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_tiff.cc	2025-07-02 13:05:31.000000000 +0000
@@ -63,9 +63,9 @@ bool TiffEncoder::Encode(const struct he
     TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
     TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
 
-    int stride_rgb;
-    const uint8_t *row_rgb = heif_image_get_plane_readonly(image,
-                                                           heif_channel_interleaved, &stride_rgb);
+    size_t stride_rgb;
+    const uint8_t *row_rgb = heif_image_get_plane_readonly2(image,
+                                                            heif_channel_interleaved, &stride_rgb);
 
     for (int i = 0; i < height; i++)
     {
diff -pruN 1.19.8-1/heifio/encoder_tiff.h 1.20.1-1/heifio/encoder_tiff.h
--- 1.19.8-1/heifio/encoder_tiff.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_tiff.h	2025-07-02 13:05:31.000000000 +0000
@@ -57,6 +57,8 @@ public:
     }
   }
 
+  bool supports_alpha() const override { return true; }
+
   bool Encode(const struct heif_image_handle* handle,
               const struct heif_image* image, const std::string& filename) override;
 };
diff -pruN 1.19.8-1/heifio/encoder_y4m.cc 1.20.1-1/heifio/encoder_y4m.cc
--- 1.19.8-1/heifio/encoder_y4m.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_y4m.cc	2025-07-02 13:05:31.000000000 +0000
@@ -50,10 +50,10 @@ bool Y4MEncoder::Encode(const struct hei
     return false;
   }
 
-  int y_stride, cb_stride, cr_stride;
-  const uint8_t* yp = heif_image_get_plane_readonly(image, heif_channel_Y, &y_stride);
-  const uint8_t* cbp = heif_image_get_plane_readonly(image, heif_channel_Cb, &cb_stride);
-  const uint8_t* crp = heif_image_get_plane_readonly(image, heif_channel_Cr, &cr_stride);
+  size_t y_stride, cb_stride, cr_stride;
+  const uint8_t* yp = heif_image_get_plane_readonly2(image, heif_channel_Y, &y_stride);
+  const uint8_t* cbp = heif_image_get_plane_readonly2(image, heif_channel_Cb, &cb_stride);
+  const uint8_t* crp = heif_image_get_plane_readonly2(image, heif_channel_Cr, &cr_stride);
 
   assert(y_stride > 0);
   assert(cb_stride > 0);
diff -pruN 1.19.8-1/heifio/encoder_y4m.h 1.20.1-1/heifio/encoder_y4m.h
--- 1.19.8-1/heifio/encoder_y4m.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/heifio/encoder_y4m.h	2025-07-02 13:05:31.000000000 +0000
@@ -45,6 +45,8 @@ public:
     return heif_chroma_420;
   }
 
+  bool supports_alpha() const override { return false; }
+
   void UpdateDecodingOptions(const struct heif_image_handle* handle,
                              struct heif_decoding_options* options) const override;
 
diff -pruN 1.19.8-1/libheif/CMakeLists.txt 1.20.1-1/libheif/CMakeLists.txt
--- 1.19.8-1/libheif/CMakeLists.txt	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/CMakeLists.txt	2025-07-02 13:05:31.000000000 +0000
@@ -4,11 +4,28 @@ configure_file(api/libheif/heif_version.
 
 set(libheif_headers
         api/libheif/heif.h
-        api/libheif/heif_cxx.h
+        api/libheif/heif_library.h
+        api/libheif/heif_image.h
+        api/libheif/heif_color.h
+        api/libheif/heif_error.h
         api/libheif/heif_plugin.h
         api/libheif/heif_properties.h
         api/libheif/heif_regions.h
         api/libheif/heif_items.h
+        api/libheif/heif_sequences.h
+        api/libheif/heif_tai_timestamps.h
+        api/libheif/heif_brands.h
+        api/libheif/heif_metadata.h
+        api/libheif/heif_aux_images.h
+        api/libheif/heif_entity_groups.h
+        api/libheif/heif_security.h
+        api/libheif/heif_encoding.h
+        api/libheif/heif_decoding.h
+        api/libheif/heif_image_handle.h
+        api/libheif/heif_context.h
+        api/libheif/heif_tiling.h
+        api/libheif/heif_uncompressed.h
+        api/libheif/heif_cxx.h
         ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h)
 
 set(libheif_sources
@@ -43,22 +60,46 @@ set(libheif_sources
         common_utils.h
         region.cc
         region.h
+        brands.cc
+        brands.h
         api/libheif/api_structs.h
         api/libheif/heif.cc
+        api/libheif/heif_library.cc
+        api/libheif/heif_image.cc
+        api/libheif/heif_color.cc
         api/libheif/heif_regions.cc
         api/libheif/heif_plugin.cc
         api/libheif/heif_properties.cc
         api/libheif/heif_items.cc
+        api/libheif/heif_sequences.cc
+        api/libheif/heif_tai_timestamps.cc
+        api/libheif/heif_brands.cc
+        api/libheif/heif_metadata.cc
+        api/libheif/heif_aux_images.cc
+        api/libheif/heif_entity_groups.cc
+        api/libheif/heif_security.cc
+        api/libheif/heif_encoding.cc
+        api/libheif/heif_decoding.cc
+        api/libheif/heif_image_handle.cc
+        api/libheif/heif_context.cc
+        api/libheif/heif_tiling.cc
+        api/libheif/heif_uncompressed.cc
         codecs/decoder.h
         codecs/decoder.cc
+        codecs/encoder.h
+        codecs/encoder.cc
         image-items/hevc.cc
         image-items/hevc.h
         codecs/hevc_boxes.cc
         codecs/hevc_boxes.h
         codecs/hevc_dec.cc
         codecs/hevc_dec.h
+        codecs/hevc_enc.cc
+        codecs/hevc_enc.h
         image-items/avif.cc
         image-items/avif.h
+        codecs/avif_enc.cc
+        codecs/avif_enc.h
         codecs/avif_dec.cc
         codecs/avif_dec.h
         codecs/avif_boxes.cc
@@ -69,16 +110,22 @@ set(libheif_sources
         codecs/jpeg_boxes.cc
         codecs/jpeg_dec.h
         codecs/jpeg_dec.cc
+        codecs/jpeg_enc.h
+        codecs/jpeg_enc.cc
         image-items/jpeg2000.h
         image-items/jpeg2000.cc
         codecs/jpeg2000_dec.h
         codecs/jpeg2000_dec.cc
+        codecs/jpeg2000_enc.h
+        codecs/jpeg2000_enc.cc
         codecs/jpeg2000_boxes.h
         codecs/jpeg2000_boxes.cc
         image-items/vvc.h
         image-items/vvc.cc
         codecs/vvc_dec.h
         codecs/vvc_dec.cc
+        codecs/vvc_enc.h
+        codecs/vvc_enc.cc
         codecs/vvc_boxes.h
         codecs/vvc_boxes.cc
         image-items/avc.h
@@ -117,6 +164,16 @@ set(libheif_sources
         color-conversion/alpha.h
         color-conversion/chroma_sampling.cc
         color-conversion/chroma_sampling.h
+        sequences/seq_boxes.h
+        sequences/seq_boxes.cc
+        sequences/chunk.h
+        sequences/chunk.cc
+        sequences/track.h
+        sequences/track.cc
+        sequences/track_visual.h
+        sequences/track_visual.cc
+        sequences/track_metadata.h
+        sequences/track_metadata.cc
         ${libheif_headers})
 
 add_library(heif ${libheif_sources})
@@ -228,6 +285,8 @@ if (WITH_UNCOMPRESSED_CODEC)
             codecs/uncompressed/unc_codec.cc
             codecs/uncompressed/unc_dec.h
             codecs/uncompressed/unc_dec.cc
+            codecs/uncompressed/unc_enc.h
+            codecs/uncompressed/unc_enc.cc
             codecs/uncompressed/decoder_abstract.h
             codecs/uncompressed/decoder_abstract.cc
             codecs/uncompressed/decoder_component_interleave.h
diff -pruN 1.19.8-1/libheif/api/libheif/api_structs.h 1.20.1-1/libheif/api/libheif/api_structs.h
--- 1.19.8-1/libheif/api/libheif/api_structs.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/api_structs.h	2025-07-02 13:05:31.000000000 +0000
@@ -25,6 +25,8 @@
 #include "context.h"
 
 #include <memory>
+#include <vector>
+#include <string>
 #include "image-items/image_item.h"
 
 struct heif_image_handle
@@ -36,6 +38,29 @@ struct heif_image_handle
 };
 
 
+struct heif_track
+{
+  std::shared_ptr<Track> track;
+
+  // store reference to keep the context alive while we are using the handle (issue #147)
+  std::shared_ptr<HeifContext> context;
+};
+
+struct heif_raw_sequence_sample
+{
+  ~heif_raw_sequence_sample()
+  {
+    heif_tai_timestamp_packet_release(timestamp);
+  }
+
+  std::vector<uint8_t> data;
+  uint32_t duration = 0;
+
+  heif_tai_timestamp_packet* timestamp = nullptr;
+  std::string gimi_sample_content_id;
+};
+
+
 struct heif_image
 {
   std::shared_ptr<HeifPixelImage> image;
diff -pruN 1.19.8-1/libheif/api/libheif/heif.cc 1.20.1-1/libheif/api/libheif/heif.cc
--- 1.19.8-1/libheif/api/libheif/heif.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif.cc	2025-07-02 13:05:31.000000000 +0000
@@ -1,6 +1,6 @@
 /*
  * HEIF codec.
- * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
  *
  * This file is part of libheif.
  *
@@ -18,3830 +18,14 @@
  * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "heif_plugin.h"
-#include "security_limits.h"
-#include "region.h"
-#include "common_utils.h"
-#include <cstdint>
 #include "heif.h"
-#include "file.h"
-#include "pixelimage.h"
-#include "api_structs.h"
-#include "context.h"
-#include "plugin_registry.h"
-#include "error.h"
-#include "bitstream.h"
-#include "init.h"
-#include "image-items/grid.h"
-#include "image-items/overlay.h"
-#include "image-items/tiled.h"
-#include <set>
-#include <limits>
-
-#if WITH_UNCOMPRESSED_CODEC
-#include "image-items/unc_image.h"
-#endif
 
 #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__)
+#include "api_structs.h"
 #include "heif_emscripten.h"
 #endif
 
-#include <algorithm>
-#include <iostream>
-#include <fstream>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-#include <cstring>
-#include <array>
-
-#ifdef _WIN32
-// for _write
-#include <io.h>
-#else
-
-#include <unistd.h>
-
-#endif
-
-#include <cassert>
 
+#include "error.h"
 
 const struct heif_error heif_error_success = {heif_error_Ok, heif_suberror_Unspecified, Error::kSuccess};
-static struct heif_error error_unsupported_parameter = {heif_error_Usage_error,
-                                                        heif_suberror_Unsupported_parameter,
-                                                        "Unsupported encoder parameter"};
-static struct heif_error error_invalid_parameter_value = {heif_error_Usage_error,
-                                                          heif_suberror_Invalid_parameter_value,
-                                                          "Invalid parameter value"};
-static struct heif_error error_unsupported_plugin_version = {heif_error_Usage_error,
-                                                             heif_suberror_Unsupported_plugin_version,
-                                                             "Unsupported plugin version"};
-static struct heif_error error_null_parameter = {heif_error_Usage_error,
-                                                 heif_suberror_Null_pointer_argument,
-                                                 "NULL passed"};
-
-const char* heif_get_version(void)
-{
-  return (LIBHEIF_VERSION);
-}
-
-uint32_t heif_get_version_number(void)
-{
-  return (LIBHEIF_NUMERIC_VERSION);
-}
-
-int heif_get_version_number_major(void)
-{
-  return ((LIBHEIF_NUMERIC_VERSION) >> 24) & 0xFF;
-}
-
-int heif_get_version_number_minor(void)
-{
-  return ((LIBHEIF_NUMERIC_VERSION) >> 16) & 0xFF;
-}
-
-int heif_get_version_number_maintenance(void)
-{
-  return ((LIBHEIF_NUMERIC_VERSION) >> 8) & 0xFF;
-}
-
-
-heif_filetype_result heif_check_filetype(const uint8_t* data, int len)
-{
-  if (len < 8) {
-    return heif_filetype_maybe;
-  }
-
-  if (data[4] != 'f' ||
-      data[5] != 't' ||
-      data[6] != 'y' ||
-      data[7] != 'p') {
-    return heif_filetype_no;
-  }
-
-  if (len >= 12) {
-    heif_brand2 brand = heif_read_main_brand(data, len);
-
-    if (brand == heif_brand2_heic) {
-      return heif_filetype_yes_supported;
-    }
-    else if (brand == heif_brand2_heix) {
-      return heif_filetype_yes_supported;
-    }
-    else if (brand == heif_brand2_avif) {
-      return heif_filetype_yes_supported;
-    }
-    else if (brand == heif_brand2_jpeg) {
-      return heif_filetype_yes_supported;
-    }
-    else if (brand == heif_brand2_j2ki) {
-      return heif_filetype_yes_supported;
-    }
-    else if (brand == heif_brand2_mif1) {
-      return heif_filetype_maybe;
-    }
-    else if (brand == heif_brand2_mif2) {
-      return heif_filetype_maybe;
-    }
-    else {
-      return heif_filetype_yes_unsupported;
-    }
-  }
-
-  return heif_filetype_maybe;
-}
-
-
-heif_error heif_has_compatible_filetype(const uint8_t* data, int len)
-{
-  // Get compatible brands first, because that does validity checks
-  heif_brand2* compatible_brands = nullptr;
-  int nBrands = 0;
-  struct heif_error err = heif_list_compatible_brands(data, len, &compatible_brands, &nBrands);
-  if (err.code) {
-    return err;
-  }
-
-  heif_brand2 main_brand = heif_read_main_brand(data, len);
-
-  std::set<heif_brand2> supported_brands{
-      heif_brand2_avif,
-      heif_brand2_heic,
-      heif_brand2_heix,
-      heif_brand2_j2ki,
-      heif_brand2_jpeg,
-      heif_brand2_miaf,
-      heif_brand2_mif1,
-      heif_brand2_mif2
-#if ENABLE_EXPERIMENTAL_MINI_FORMAT
-      , heif_brand2_mif3
-#endif
-  };
-
-  auto it = supported_brands.find(main_brand);
-  if (it != supported_brands.end()) {
-    heif_free_list_of_compatible_brands(compatible_brands);
-    return heif_error_ok;
-  }
-
-  for (int i = 0; i < nBrands; i++) {
-    heif_brand2 compatible_brand = compatible_brands[i];
-    it = supported_brands.find(compatible_brand);
-    if (it != supported_brands.end()) {
-      heif_free_list_of_compatible_brands(compatible_brands);
-      return heif_error_ok;
-    }
-  }
-  heif_free_list_of_compatible_brands(compatible_brands);
-  return {heif_error_Invalid_input, heif_suberror_Unsupported_image_type, "No supported brands found."};;
-}
-
-
-int heif_check_jpeg_filetype(const uint8_t* data, int len)
-{
-  if (len < 4 || data == nullptr) {
-    return -1;
-  }
-
-  return (data[0] == 0xFF &&
-	  data[1] == 0xD8 &&
-	  data[2] == 0xFF &&
-	  (data[3] & 0xF0) == 0xE0);
-}
-
-
-heif_brand heif_fourcc_to_brand_enum(const char* fourcc)
-{
-  if (fourcc == nullptr || !fourcc[0] || !fourcc[1] || !fourcc[2] || !fourcc[3]) {
-    return heif_unknown_brand;
-  }
-
-  char brand[5];
-  brand[0] = fourcc[0];
-  brand[1] = fourcc[1];
-  brand[2] = fourcc[2];
-  brand[3] = fourcc[3];
-  brand[4] = 0;
-
-  if (strcmp(brand, "heic") == 0) {
-    return heif_heic;
-  }
-  else if (strcmp(brand, "heix") == 0) {
-    return heif_heix;
-  }
-  else if (strcmp(brand, "hevc") == 0) {
-    return heif_hevc;
-  }
-  else if (strcmp(brand, "hevx") == 0) {
-    return heif_hevx;
-  }
-  else if (strcmp(brand, "heim") == 0) {
-    return heif_heim;
-  }
-  else if (strcmp(brand, "heis") == 0) {
-    return heif_heis;
-  }
-  else if (strcmp(brand, "hevm") == 0) {
-    return heif_hevm;
-  }
-  else if (strcmp(brand, "hevs") == 0) {
-    return heif_hevs;
-  }
-  else if (strcmp(brand, "mif1") == 0) {
-    return heif_mif1;
-  }
-  else if (strcmp(brand, "msf1") == 0) {
-    return heif_msf1;
-  }
-  else if (strcmp(brand, "avif") == 0) {
-    return heif_avif;
-  }
-  else if (strcmp(brand, "avis") == 0) {
-    return heif_avis;
-  }
-  else if (strcmp(brand, "vvic") == 0) {
-    return heif_vvic;
-  }
-  else if (strcmp(brand, "j2ki") == 0) {
-    return heif_j2ki;
-  }
-  else if (strcmp(brand, "j2is") == 0) {
-    return heif_j2is;
-  }
-  else {
-    return heif_unknown_brand;
-  }
-}
-
-
-enum heif_brand heif_main_brand(const uint8_t* data, int len)
-{
-  if (len < 12) {
-    return heif_unknown_brand;
-  }
-
-  return heif_fourcc_to_brand_enum((char*) (data + 8));
-}
-
-
-heif_brand2 heif_read_main_brand(const uint8_t* data, int len)
-{
-  if (len < 12) {
-    return heif_unknown_brand;
-  }
-
-  return heif_fourcc_to_brand((char*) (data + 8));
-}
-
-
-heif_brand2 heif_fourcc_to_brand(const char* fourcc_string)
-{
-  if (fourcc_string == nullptr || !fourcc_string[0] || !fourcc_string[1] || !fourcc_string[2] || !fourcc_string[3]) {
-    return 0;
-  }
-
-  return fourcc(fourcc_string);
-}
-
-heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len)
-{
-  if (len < 16) {
-    return heif_unknown_brand;
-  }
-  return heif_fourcc_to_brand((char*) (data + 12));
-}
-
-void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc)
-{
-  if (out_fourcc) {
-    out_fourcc[0] = (char) ((brand >> 24) & 0xFF);
-    out_fourcc[1] = (char) ((brand >> 16) & 0xFF);
-    out_fourcc[2] = (char) ((brand >> 8) & 0xFF);
-    out_fourcc[3] = (char) ((brand >> 0) & 0xFF);
-  }
-}
-
-
-int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc)
-{
-  if (data == nullptr || len <= 0 || brand_fourcc == nullptr || !brand_fourcc[0] || !brand_fourcc[1] || !brand_fourcc[2] || !brand_fourcc[3]) {
-    return -1;
-  }
-
-  auto stream = std::make_shared<StreamReader_memory>(data, len, false);
-  BitstreamRange range(stream, len);
-
-  std::shared_ptr<Box> box;
-  Error err = Box::read(range, &box, heif_get_global_security_limits());
-  if (err) {
-    if (err.sub_error_code == heif_suberror_End_of_data) {
-      return -1;
-    }
-
-    return -2;
-  }
-
-  auto ftyp = std::dynamic_pointer_cast<Box_ftyp>(box);
-  if (!ftyp) {
-    return -2;
-  }
-
-  return ftyp->has_compatible_brand(fourcc(brand_fourcc)) ? 1 : 0;
-}
-
-
-struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size)
-{
-  if (data == nullptr || out_brands == nullptr || out_size == nullptr) {
-    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument"};
-  }
-
-  if (len <= 0) {
-    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "data length must be positive"};
-  }
-
-  auto stream = std::make_shared<StreamReader_memory>(data, len, false);
-  BitstreamRange range(stream, len);
-
-  std::shared_ptr<Box> box;
-  Error err = Box::read(range, &box, heif_get_global_security_limits());
-  if (err) {
-    if (err.sub_error_code == heif_suberror_End_of_data) {
-      return {err.error_code, err.sub_error_code, "insufficient input data"};
-    }
-
-    return {err.error_code, err.sub_error_code, "error reading ftyp box"};
-  }
-
-  auto ftyp = std::dynamic_pointer_cast<Box_ftyp>(box);
-  if (!ftyp) {
-    return {heif_error_Invalid_input, heif_suberror_No_ftyp_box, "input is not a ftyp box"};
-  }
-
-  auto brands = ftyp->list_brands();
-  size_t nBrands = brands.size();
-  *out_brands = (heif_brand2*) malloc(sizeof(heif_brand2) * nBrands);
-  *out_size = (int)nBrands;
-
-  for (size_t i = 0; i < nBrands; i++) {
-    (*out_brands)[i] = brands[i];
-  }
-
-  return heif_error_success;
-}
-
-
-void heif_free_list_of_compatible_brands(heif_brand2* brands_list)
-{
-  if (brands_list) {
-    free(brands_list);
-  }
-}
-
-
-enum class TriBool
-{
-  No, Yes, Unknown
-};
-
-TriBool is_jpeg(const uint8_t* data, int len)
-{
-  if (len < 12) {
-    return TriBool::Unknown;
-  }
-
-  if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE0 &&
-      data[4] == 0x00 && data[5] == 0x10 && data[6] == 0x4A && data[7] == 0x46 &&
-      data[8] == 0x49 && data[9] == 0x46 && data[10] == 0x00 && data[11] == 0x01) {
-    return TriBool::Yes;
-  }
-  if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE1 &&
-      data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66 &&
-      data[10] == 0x00 && data[11] == 0x00) {
-    return TriBool::Yes;
-  }
-  else {
-    return TriBool::No;
-  }
-}
-
-
-TriBool is_png(const uint8_t* data, int len)
-{
-  if (len < 8) {
-    return TriBool::Unknown;
-  }
-
-  if (data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 &&
-      data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A) {
-    return TriBool::Yes;
-  }
-  else {
-    return TriBool::No;
-  }
-}
-
-
-const char* heif_get_file_mime_type(const uint8_t* data, int len)
-{
-  heif_brand mainBrand = heif_main_brand(data, len);
-
-  if (mainBrand == heif_heic ||
-      mainBrand == heif_heix ||
-      mainBrand == heif_heim ||
-      mainBrand == heif_heis) {
-    return "image/heic";
-  }
-  else if (mainBrand == heif_mif1) {
-    return "image/heif";
-  }
-  else if (mainBrand == heif_hevc ||
-           mainBrand == heif_hevx ||
-           mainBrand == heif_hevm ||
-           mainBrand == heif_hevs) {
-    return "image/heic-sequence";
-  }
-  else if (mainBrand == heif_msf1) {
-    return "image/heif-sequence";
-  }
-  else if (mainBrand == heif_avif) {
-    return "image/avif";
-  }
-  else if (mainBrand == heif_avis) {
-    return "image/avif-sequence";
-  }
-#if ENABLE_EXPERIMENTAL_MINI_FORMAT
-  else if (mainBrand == heif_brand2_mif3) {
-    heif_brand2 minorBrand = heif_read_minor_version_brand(data, len);
-    if (minorBrand == heif_brand2_avif) {
-      return "image/avif";
-    }
-    if (minorBrand == heif_brand2_heic ||
-        minorBrand == heif_brand2_heix ||
-        minorBrand == heif_brand2_heim ||
-        minorBrand == heif_brand2_heis) {
-      return "image/heic";
-    }
-    // There could be other options in here, like VVC or J2K
-    return "image/heif";
-  }
-#endif
-  else if (mainBrand == heif_j2ki) {
-    return "image/hej2k";
-  }
-  else if (mainBrand == heif_j2is) {
-    return "image/j2is";
-  }
-  else if (is_jpeg(data, len) == TriBool::Yes) {
-    return "image/jpeg";
-  }
-  else if (is_png(data, len) == TriBool::Yes) {
-    return "image/png";
-  }
-  else {
-    return "";
-  }
-}
-
-
-const struct heif_security_limits* heif_get_global_security_limits()
-{
-  return &global_security_limits;
-}
-
-
-const struct heif_security_limits* heif_get_disabled_security_limits()
-{
-  return &disabled_security_limits;
-}
-
-
-struct heif_security_limits* heif_context_get_security_limits(const struct heif_context* ctx)
-{
-  if (!ctx) {
-    return nullptr;
-  }
-
-  return ctx->context->get_security_limits();
-}
-
-
-struct heif_error heif_context_set_security_limits(struct heif_context* ctx, const struct heif_security_limits* limits)
-{
-  if (ctx==nullptr || limits==nullptr) {
-    return {heif_error_Usage_error,
-            heif_suberror_Null_pointer_argument};
-  }
-
-  ctx->context->set_security_limits(limits);
-
-  return heif_error_ok;
-}
-
-
-
-heif_context* heif_context_alloc()
-{
-  load_plugins_if_not_initialized_yet();
-
-  struct heif_context* ctx = new heif_context;
-  ctx->context = std::make_shared<HeifContext>();
-
-  return ctx;
-}
-
-void heif_context_free(heif_context* ctx)
-{
-  delete ctx;
-}
-
-heif_error heif_context_read_from_file(heif_context* ctx, const char* filename,
-                                       const struct heif_reading_options*)
-{
-  Error err = ctx->context->read_from_file(filename);
-  return err.error_struct(ctx->context.get());
-}
-
-heif_error heif_context_read_from_memory(heif_context* ctx, const void* mem, size_t size,
-                                         const struct heif_reading_options*)
-{
-  Error err = ctx->context->read_from_memory(mem, size, true);
-  return err.error_struct(ctx->context.get());
-}
-
-heif_error heif_context_read_from_memory_without_copy(heif_context* ctx, const void* mem, size_t size,
-                                                      const struct heif_reading_options*)
-{
-  Error err = ctx->context->read_from_memory(mem, size, false);
-  return err.error_struct(ctx->context.get());
-}
-
-heif_error heif_context_read_from_reader(struct heif_context* ctx,
-                                         const struct heif_reader* reader_func_table,
-                                         void* userdata,
-                                         const struct heif_reading_options*)
-{
-  auto reader = std::make_shared<StreamReader_CApi>(reader_func_table, userdata);
-
-  Error err = ctx->context->read(reader);
-  return err.error_struct(ctx->context.get());
-}
-
-// TODO: heif_error heif_context_read_from_file_descriptor(heif_context*, int fd);
-
-void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd)
-{
-  if (!ctx) {
-    return;
-  }
-
-  std::string dump = ctx->context->debug_dump_boxes();
-  // TODO(fancycode): Should we return an error if writing fails?
-#ifdef _WIN32
-  auto written = _write(fd, dump.c_str(), static_cast<unsigned int>(dump.size()));
-#else
-  auto written = write(fd, dump.c_str(), dump.size());
-#endif
-  (void) written;
-}
-
-heif_error heif_context_get_primary_image_handle(heif_context* ctx, heif_image_handle** img)
-{
-  if (!img) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(ctx->context.get());
-  }
-
-  std::shared_ptr<ImageItem> primary_image = ctx->context->get_primary_image(true);
-
-  // It is a requirement of an HEIF file there is always a primary image.
-  // If there is none, an error is generated when loading the file.
-  if (!primary_image) {
-    Error err(heif_error_Invalid_input,
-              heif_suberror_No_or_invalid_primary_item);
-    return err.error_struct(ctx->context.get());
-  }
-
-  if (auto errImage = std::dynamic_pointer_cast<ImageItem_Error>(primary_image)) {
-    Error error = errImage->get_item_error();
-    return error.error_struct(ctx->context.get());
-  }
-
-  *img = new heif_image_handle();
-  (*img)->image = std::move(primary_image);
-  (*img)->context = ctx->context;
-
-  return Error::Ok.error_struct(ctx->context.get());
-}
-
-
-struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id)
-{
-  if (!id) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
-  }
-
-  std::shared_ptr<ImageItem> primary = ctx->context->get_primary_image(true);
-  if (!primary) {
-    return Error(heif_error_Invalid_input,
-                 heif_suberror_No_or_invalid_primary_item).error_struct(ctx->context.get());
-  }
-
-  *id = primary->get_id();
-
-  return Error::Ok.error_struct(ctx->context.get());
-}
-
-
-int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id)
-{
-  const std::vector<std::shared_ptr<ImageItem>> images = ctx->context->get_top_level_images(true);
-
-  for (const auto& img : images) {
-    if (img->get_id() == id) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-
-int heif_context_get_number_of_top_level_images(heif_context* ctx)
-{
-  return (int) ctx->context->get_top_level_images(true).size();
-}
-
-
-int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx,
-                                                 heif_item_id* ID_array,
-                                                 int count)
-{
-  if (ID_array == nullptr || count == 0 || ctx == nullptr) {
-    return 0;
-  }
-
-
-  // fill in ID values into output array
-
-  const std::vector<std::shared_ptr<ImageItem>> imgs = ctx->context->get_top_level_images(true);
-  int n = (int) std::min(count, (int) imgs.size());
-  for (int i = 0; i < n; i++) {
-    ID_array[i] = imgs[i]->get_id();
-  }
-
-  return n;
-}
-
-
-struct heif_error heif_context_get_image_handle(struct heif_context* ctx,
-                                                heif_item_id id,
-                                                struct heif_image_handle** imgHdl)
-{
-  if (!imgHdl) {
-    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, ""};
-  }
-
-  auto image = ctx->context->get_image(id, true);
-
-  if (auto errImage = std::dynamic_pointer_cast<ImageItem_Error>(image)) {
-    Error error = errImage->get_item_error();
-    return error.error_struct(ctx->context.get());
-  }
-
-  if (!image) {
-    *imgHdl = nullptr;
-
-    return {heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced, ""};
-  }
-
-  *imgHdl = new heif_image_handle();
-  (*imgHdl)->image = std::move(image);
-  (*imgHdl)->context = ctx->context;
-
-  return heif_error_success;
-}
-
-
-int heif_image_handle_is_primary_image(const struct heif_image_handle* handle)
-{
-  return handle->image->is_primary();
-}
-
-
-heif_item_id heif_image_handle_get_item_id(const struct heif_image_handle* handle)
-{
-  return handle->image->get_id();
-}
-
-
-int heif_image_handle_get_number_of_thumbnails(const struct heif_image_handle* handle)
-{
-  return (int) handle->image->get_thumbnails().size();
-}
-
-
-int heif_image_handle_get_list_of_thumbnail_IDs(const struct heif_image_handle* handle,
-                                                heif_item_id* ids, int count)
-{
-  if (ids == nullptr) {
-    return 0;
-  }
-
-  auto thumbnails = handle->image->get_thumbnails();
-  int n = (int) std::min(count, (int) thumbnails.size());
-
-  for (int i = 0; i < n; i++) {
-    ids[i] = thumbnails[i]->get_id();
-  }
-
-  return n;
-}
-
-
-heif_error heif_image_handle_get_thumbnail(const struct heif_image_handle* handle,
-                                           heif_item_id thumbnail_id,
-                                           struct heif_image_handle** out_thumbnail_handle)
-{
-  if (!out_thumbnail_handle) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(handle->image.get());
-  }
-
-  auto thumbnails = handle->image->get_thumbnails();
-  for (const auto& thumb : thumbnails) {
-    if (thumb->get_id() == thumbnail_id) {
-      *out_thumbnail_handle = new heif_image_handle();
-      (*out_thumbnail_handle)->image = thumb;
-      (*out_thumbnail_handle)->context = handle->context;
-
-      return Error::Ok.error_struct(handle->image.get());
-    }
-  }
-
-  Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
-  return err.error_struct(handle->image.get());
-}
-
-
-int heif_image_handle_get_number_of_auxiliary_images(const struct heif_image_handle* handle,
-                                                     int include_alpha_image)
-{
-  return (int) handle->image->get_aux_images(include_alpha_image).size();
-}
-
-
-int heif_image_handle_get_list_of_auxiliary_image_IDs(const struct heif_image_handle* handle,
-                                                      int include_alpha_image,
-                                                      heif_item_id* ids, int count)
-{
-  if (ids == nullptr) {
-    return 0;
-  }
-
-  auto auxImages = handle->image->get_aux_images(include_alpha_image);
-  int n = (int) std::min(count, (int) auxImages.size());
-
-  for (int i = 0; i < n; i++) {
-    ids[i] = auxImages[i]->get_id();
-  }
-
-  return n;
-}
-
-
-struct heif_error heif_image_handle_get_auxiliary_type(const struct heif_image_handle* handle,
-                                                       const char** out_type)
-{
-  if (out_type == nullptr) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(handle->image.get());
-  }
-
-  *out_type = nullptr;
-
-  const auto& auxType = handle->image->get_aux_type();
-
-  char* buf = (char*) malloc(auxType.length() + 1);
-
-  if (buf == nullptr) {
-    return Error(heif_error_Memory_allocation_error,
-                 heif_suberror_Unspecified,
-                 "Failed to allocate memory for the type string").error_struct(handle->image.get());
-  }
-
-  strcpy(buf, auxType.c_str());
-  *out_type = buf;
-
-  return heif_error_success;
-}
-
-
-void heif_image_handle_release_auxiliary_type(const struct heif_image_handle* handle,
-                                              const char** out_type)
-{
-  if (out_type && *out_type) {
-    free((void*) *out_type);
-    *out_type = nullptr;
-  }
-}
-
-// DEPRECATED (typo)
-void heif_image_handle_free_auxiliary_types(const struct heif_image_handle* handle,
-                                            const char** out_type)
-{
-  heif_image_handle_release_auxiliary_type(handle, out_type);
-}
-
-
-struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif_image_handle* main_image_handle,
-                                                               heif_item_id auxiliary_id,
-                                                               struct heif_image_handle** out_auxiliary_handle)
-{
-  if (!out_auxiliary_handle) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(main_image_handle->image.get());
-  }
-
-  *out_auxiliary_handle = nullptr;
-
-  auto auxImages = main_image_handle->image->get_aux_images();
-  for (const auto& aux : auxImages) {
-    if (aux->get_id() == auxiliary_id) {
-      *out_auxiliary_handle = new heif_image_handle();
-      (*out_auxiliary_handle)->image = aux;
-      (*out_auxiliary_handle)->context = main_image_handle->context;
-
-      return Error::Ok.error_struct(main_image_handle->image.get());
-    }
-  }
-
-  Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
-  return err.error_struct(main_image_handle->image.get());
-}
-
-
-int heif_image_handle_get_width(const struct heif_image_handle* handle)
-{
-  if (handle && handle->image) {
-    return handle->image->get_width();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-int heif_image_handle_get_height(const struct heif_image_handle* handle)
-{
-  if (handle && handle->image) {
-    return handle->image->get_height();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-int heif_image_handle_get_ispe_width(const struct heif_image_handle* handle)
-{
-  if (handle && handle->image) {
-    return handle->image->get_ispe_width();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-int heif_image_handle_get_ispe_height(const struct heif_image_handle* handle)
-{
-  if (handle && handle->image) {
-    return handle->image->get_ispe_height();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle)
-{
-  auto ctx = new heif_context();
-  ctx->context = handle->context;
-  return ctx;
-}
-
-
-heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* tiling)
-{
-  if (!handle || !tiling) {
-    return {heif_error_Usage_error,
-            heif_suberror_Null_pointer_argument,
-            "NULL passed to heif_image_handle_get_image_tiling()"};
-  }
-
-  *tiling = handle->image->get_heif_image_tiling();
-
-  if (process_image_transformations) {
-    Error error = handle->image->process_image_transformations_on_tiling(*tiling);
-    if (error) {
-      return error.error_struct(handle->context.get());
-    }
-  }
-
-  return heif_error_ok;
-}
-
-
-struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle,
-                                                           int process_image_transformations,
-                                                           uint32_t tile_x, uint32_t tile_y,
-                                                           heif_item_id* tile_item_id)
-{
-  if (!handle || !tile_item_id) {
-    return { heif_error_Usage_error,
-             heif_suberror_Null_pointer_argument };
-  }
-
-  std::shared_ptr<ImageItem_Grid> gridItem = std::dynamic_pointer_cast<ImageItem_Grid>(handle->image);
-  if (!gridItem) {
-    return { heif_error_Usage_error,
-             heif_suberror_Unspecified,
-             "Image is no grid image" };
-  }
-
-  const ImageGrid& gridspec = gridItem->get_grid_spec();
-  if (tile_x >= gridspec.get_columns() || tile_y >= gridspec.get_rows()) {
-    return { heif_error_Usage_error,
-             heif_suberror_Unspecified,
-             "Grid tile index out of range" };
-  }
-
-  if (process_image_transformations) {
-    gridItem->transform_requested_tile_position_to_original_tile_position(tile_x, tile_y);
-  }
-
-  *tile_item_id = gridItem->get_grid_tiles()[tile_y * gridspec.get_columns() + tile_x];
-
-  return heif_error_ok;
-}
-
-
-#if 0
-// TODO: do we need this ? This does not handle rotations. We can use heif_image_handle_get_image_tiling() to get the same information.
-struct heif_error heif_image_handle_get_tile_size(const struct heif_image_handle* handle,
-                                                  uint32_t* tile_width, uint32_t* tile_height)
-{
-  if (!handle) {
-    return error_null_parameter;
-  }
-
-
-  uint32_t w,h;
-
-  handle->image->get_tile_size(w,h);
-
-  if (tile_width) {
-    *tile_width = w;
-  }
-
-  if (tile_height) {
-    *tile_height = h;
-  }
-
-  return heif_error_success;
-}
-#endif
-
-
-struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context* ctx,
-                                                         uint32_t type_filter,
-                                                         heif_item_id item_filter,
-                                                         int* out_num_groups)
-{
-  std::shared_ptr<Box_grpl> grplBox = ctx->context->get_heif_file()->get_grpl_box();
-  if (!grplBox) {
-    *out_num_groups = 0;
-    return nullptr;
-  }
-
-  std::vector<std::shared_ptr<Box>> all_entity_group_boxes = grplBox->get_all_child_boxes();
-  if (all_entity_group_boxes.empty()) {
-    *out_num_groups = 0;
-    return nullptr;
-  }
-
-  // --- filter groups
-
-  std::vector<std::shared_ptr<Box_EntityToGroup>> entity_group_boxes;
-  for (auto& group : all_entity_group_boxes) {
-    if (type_filter != 0 && group->get_short_type() != type_filter) {
-      continue;
-    }
-
-    auto groupBox = std::dynamic_pointer_cast<Box_EntityToGroup>(group);
-    const std::vector<heif_item_id>& items = groupBox->get_item_ids();
-
-    if (item_filter != 0 && std::all_of(items.begin(), items.end(), [item_filter](heif_item_id item) {
-      return item != item_filter;
-    })) {
-      continue;
-    }
-
-    entity_group_boxes.emplace_back(groupBox);
-  }
-
-  // --- convert to C structs
-
-  auto* groups = new heif_entity_group[entity_group_boxes.size()];
-  for (size_t i = 0; i < entity_group_boxes.size(); i++) {
-    const auto& groupBox = entity_group_boxes[i];
-    const std::vector<heif_item_id>& items = groupBox->get_item_ids();
-
-    groups[i].entity_group_id = groupBox->get_group_id();
-    groups[i].entity_group_type = groupBox->get_short_type();
-    groups[i].entities = (items.empty() ? nullptr : new heif_item_id[items.size()]);
-    groups[i].num_entities = static_cast<uint32_t>(items.size());
-
-    if (groups[i].entities) { // avoid clang static analyzer false positive
-      for (size_t k = 0; k < items.size(); k++) {
-        groups[i].entities[k] = items[k];
-      }
-    }
-  }
-
-  *out_num_groups = static_cast<int>(entity_group_boxes.size());
-  return groups;
-}
-
-
-void heif_entity_groups_release(struct heif_entity_group* grp, int num_groups)
-{
-  for (int i=0;i<num_groups;i++) {
-    delete[] grp[i].entities;
-  }
-
-  delete[] grp;
-}
-
-
-struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx,
-                                                        const heif_item_id* layer_item_ids,
-                                                        size_t num_layers,
-                                                        /*
-                                                        uint16_t tile_width,
-                                                        uint16_t tile_height,
-                                                        uint32_t num_layers,
-                                                        const heif_pyramid_layer_info* in_layers,
-                                                         */
-                                                        heif_item_id* out_group_id)
-{
-  if (!layer_item_ids) {
-    return error_null_parameter;
-  }
-
-  if (num_layers == 0) {
-    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Number of layers cannot be 0."};
-  }
-
-  std::vector<heif_item_id> layers(num_layers);
-  for (size_t i = 0; i < num_layers; i++) {
-    layers[i] = layer_item_ids[i];
-  }
-
-  Result<heif_item_id> result = ctx->context->add_pyramid_group(layers);
-
-  if (result) {
-    if (out_group_id) {
-      *out_group_id = result.value;
-    }
-    return heif_error_success;
-  }
-  else {
-    return result.error.error_struct(ctx->context.get());
-  }
-}
-
-
-struct heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(struct heif_context* ctx, heif_entity_group_id id, int* out_num_layers)
-{
-  if (!out_num_layers) {
-    return nullptr;
-  }
-
-  std::shared_ptr<Box_EntityToGroup> groupBox = ctx->context->get_heif_file()->get_entity_group(id);
-  if (!groupBox) {
-    return nullptr;
-  }
-
-  const auto pymdBox = std::dynamic_pointer_cast<Box_pymd>(groupBox);
-  if (!pymdBox) {
-    return nullptr;
-  }
-
-  const std::vector<Box_pymd::LayerInfo> pymd_layers = pymdBox->get_layers();
-  if (pymd_layers.empty()) {
-    return nullptr;
-  }
-
-  auto items = pymdBox->get_item_ids();
-  assert(items.size() == pymd_layers.size());
-
-  auto* layerInfo = new heif_pyramid_layer_info[pymd_layers.size()];
-  for (size_t i=0; i<pymd_layers.size(); i++) {
-    layerInfo[i].layer_image_id = items[i];
-    layerInfo[i].layer_binning = pymd_layers[i].layer_binning;
-    layerInfo[i].tile_rows_in_layer = pymd_layers[i].tiles_in_layer_row_minus1 + 1;
-    layerInfo[i].tile_columns_in_layer = pymd_layers[i].tiles_in_layer_column_minus1 + 1;
-  }
-
-  *out_num_layers = static_cast<int>(pymd_layers.size());
-
-  return layerInfo;
-}
-
-
-void heif_pyramid_layer_info_release(struct heif_pyramid_layer_info* infos)
-{
-  delete[] infos;
-}
-
-
-struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle,
-                                                                      enum heif_colorspace* out_colorspace,
-                                                                      enum heif_chroma* out_chroma)
-{
-  Error err = image_handle->image->get_coded_image_colorspace(out_colorspace, out_chroma);
-  if (err) {
-    return err.error_struct(image_handle->image.get());
-  }
-
-  return heif_error_success;
-}
-
-
-int heif_image_handle_has_alpha_channel(const struct heif_image_handle* handle)
-{
-  // TODO: for now, also scan the grid tiles for alpha information (issue #708), but depending about
-  // how the discussion about this structure goes forward, we might remove this again.
-
-  return handle->context->has_alpha(handle->image->get_id());   // handle case in issue #708
-  //return handle->image->get_alpha_channel() != nullptr;       // old alpha check that fails on alpha in grid tiles
-}
-
-
-int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle* handle)
-{
-  // TODO: what about images that have the alpha in the grid tiles (issue #708) ?
-  return handle->image->is_premultiplied_alpha();
-}
-
-
-int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle* handle)
-{
-  return handle->image->get_luma_bits_per_pixel();
-}
-
-
-int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle* handle)
-{
-  return handle->image->get_chroma_bits_per_pixel();
-}
-
-
-int heif_image_handle_has_depth_image(const struct heif_image_handle* handle)
-{
-  return handle->image->get_depth_channel() != nullptr;
-}
-
-void heif_depth_representation_info_free(const struct heif_depth_representation_info* info)
-{
-  delete info;
-}
-
-int heif_image_handle_get_depth_image_representation_info(const struct heif_image_handle* handle,
-                                                          heif_item_id depth_image_id,
-                                                          const struct heif_depth_representation_info** out)
-{
-  std::shared_ptr<ImageItem> depth_image;
-
-  if (out) {
-    if (handle->image->is_depth_channel()) {
-      // Because of an API bug before v1.11.0, the input handle may be the depth image (#422).
-      depth_image = handle->image;
-    }
-    else {
-      depth_image = handle->image->get_depth_channel();
-    }
-
-    if (depth_image->has_depth_representation_info()) {
-      auto info = new heif_depth_representation_info;
-      *info = depth_image->get_depth_representation_info();
-      *out = info;
-      return true;
-    }
-    else {
-      *out = nullptr;
-    }
-  }
-
-  return false;
-}
-
-
-int heif_image_handle_get_number_of_depth_images(const struct heif_image_handle* handle)
-{
-  auto depth_image = handle->image->get_depth_channel();
-
-  if (depth_image) {
-    return 1;
-  }
-  else {
-    return 0;
-  }
-}
-
-
-int heif_image_handle_get_list_of_depth_image_IDs(const struct heif_image_handle* handle,
-                                                  heif_item_id* ids, int count)
-{
-  auto depth_image = handle->image->get_depth_channel();
-
-  if (count == 0) {
-    return 0;
-  }
-
-  if (depth_image) {
-    ids[0] = depth_image->get_id();
-    return 1;
-  }
-  else {
-    return 0;
-  }
-}
-
-
-struct heif_error heif_image_handle_get_depth_image_handle(const struct heif_image_handle* handle,
-                                                           heif_item_id depth_id,
-                                                           struct heif_image_handle** out_depth_handle)
-{
-  if (out_depth_handle == nullptr) {
-    return {heif_error_Usage_error,
-            heif_suberror_Null_pointer_argument,
-            "NULL out_depth_handle passed to heif_image_handle_get_depth_image_handle()"};
-  }
-
-  auto depth_image = handle->image->get_depth_channel();
-
-  if (depth_image->get_id() != depth_id) {
-    *out_depth_handle = nullptr;
-
-    Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
-    return err.error_struct(handle->image.get());
-  }
-
-  *out_depth_handle = new heif_image_handle();
-  (*out_depth_handle)->image = depth_image;
-  (*out_depth_handle)->context = handle->context;
-
-  return Error::Ok.error_struct(handle->image.get());
-}
-
-
-void fill_default_decoding_options(heif_decoding_options& options)
-{
-  options.version = 6;
-
-  options.ignore_transformations = false;
-
-  options.start_progress = nullptr;
-  options.on_progress = nullptr;
-  options.end_progress = nullptr;
-  options.progress_user_data = nullptr;
-
-  // version 2
-
-  options.convert_hdr_to_8bit = false;
-
-  // version 3
-
-  options.strict_decoding = false;
-
-  // version 4
-
-  options.decoder_id = nullptr;
-
-  // version 5
-
-  options.color_conversion_options.version = 1;
-  options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
-  options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
-  options.color_conversion_options.only_use_preferred_chroma_algorithm = false;
-
-  // version 6
-
-  options.cancel_decoding = nullptr;
-}
-
-
-// overwrite the (possibly lower version) input options over the default options
-static heif_decoding_options normalize_options(const heif_decoding_options* input_options)
-{
-  heif_decoding_options options{};
-  fill_default_decoding_options(options);
-
-  if (input_options) {
-    switch (input_options->version) {
-      case 6:
-        options.cancel_decoding = input_options->cancel_decoding;
-        // fallthrough
-      case 5:
-        options.color_conversion_options = input_options->color_conversion_options;
-        // fallthrough
-      case 4:
-        options.decoder_id = input_options->decoder_id;
-        // fallthrough
-      case 3:
-        options.strict_decoding = input_options->strict_decoding;
-        // fallthrough
-      case 2:
-        options.convert_hdr_to_8bit = input_options->convert_hdr_to_8bit;
-        // fallthrough
-      case 1:
-        options.ignore_transformations = input_options->ignore_transformations;
-
-        options.start_progress = input_options->start_progress;
-        options.on_progress = input_options->on_progress;
-        options.end_progress = input_options->end_progress;
-        options.progress_user_data = input_options->progress_user_data;
-    }
-  }
-
-  return options;
-}
-
-
-void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options* options)
-{
-  options->version = 1;
-#if HAVE_LIBSHARPYUV
-  options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_sharp_yuv;
-#else
-  options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
-#endif
-
-  options->preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
-  options->only_use_preferred_chroma_algorithm = true;
-}
-
-
-heif_decoding_options* heif_decoding_options_alloc()
-{
-  auto options = new heif_decoding_options;
-
-  fill_default_decoding_options(*options);
-
-  return options;
-}
-
-
-void heif_decoding_options_free(heif_decoding_options* options)
-{
-  delete options;
-}
-
-
-struct heif_error heif_decode_image(const struct heif_image_handle* in_handle,
-                                    struct heif_image** out_img,
-                                    heif_colorspace colorspace,
-                                    heif_chroma chroma,
-                                    const struct heif_decoding_options* input_options)
-{
-  if (out_img == nullptr) {
-    return {heif_error_Usage_error,
-            heif_suberror_Null_pointer_argument,
-            "NULL out_img passed to heif_decode_image()"};
-  }
-
-  *out_img = nullptr;
-
-  heif_item_id id = in_handle->image->get_id();
-
-  heif_decoding_options dec_options = normalize_options(input_options);
-
-  Result<std::shared_ptr<HeifPixelImage>> decodingResult = in_handle->context->decode_image(id,
-                                                                                            colorspace,
-                                                                                            chroma,
-                                                                                            dec_options,
-                                                                                            false, 0,0);
-  if (decodingResult.error.error_code != heif_error_Ok) {
-    return decodingResult.error.error_struct(in_handle->image.get());
-  }
-
-  std::shared_ptr<HeifPixelImage> img = decodingResult.value;
-
-  *out_img = new heif_image();
-  (*out_img)->image = std::move(img);
-
-  return Error::Ok.error_struct(in_handle->image.get());
-}
-
-
-struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle,
-                                                      struct heif_image** out_img,
-                                                      enum heif_colorspace colorspace,
-                                                      enum heif_chroma chroma,
-                                                      const struct heif_decoding_options* input_options,
-                                                      uint32_t x0, uint32_t y0)
-{
-  if (!in_handle) {
-    return error_null_parameter;
-  }
-
-  heif_item_id id = in_handle->image->get_id();
-
-  heif_decoding_options dec_options = normalize_options(input_options);
-
-  Result<std::shared_ptr<HeifPixelImage>> decodingResult = in_handle->context->decode_image(id,
-                                                                                            colorspace,
-                                                                                            chroma,
-                                                                                            dec_options,
-                                                                                            true, x0,y0);
-  if (decodingResult.error.error_code != heif_error_Ok) {
-    return decodingResult.error.error_struct(in_handle->image.get());
-  }
-
-  std::shared_ptr<HeifPixelImage> img = decodingResult.value;
-
-  *out_img = new heif_image();
-  (*out_img)->image = std::move(img);
-
-  return Error::Ok.error_struct(in_handle->image.get());
-}
-
-
-struct heif_error heif_image_create(int width, int height,
-                                    heif_colorspace colorspace,
-                                    heif_chroma chroma,
-                                    struct heif_image** image)
-{
-  if (image == nullptr) {
-    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_image_create: NULL passed as image pointer."};
-  }
-
-  // auto-correct colorspace_YCbCr + chroma_monochrome to colorspace_monochrome
-  // TODO: this should return an error in a later version (see below)
-  if (chroma == heif_chroma_monochrome && colorspace == heif_colorspace_YCbCr) {
-    colorspace = heif_colorspace_monochrome;
-
-    std::cerr << "libheif warning: heif_image_create() used with an illegal colorspace/chroma combination. This will return an error in a future version.\n";
-  }
-
-  // return error if invalid colorspace + chroma combination is used
-  auto validChroma = get_valid_chroma_values_for_colorspace(colorspace);
-  if (std::find(validChroma.begin(), validChroma.end(), chroma) == validChroma.end()) {
-    *image = nullptr;
-    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Invalid colorspace/chroma combination."};
-  }
-
-  struct heif_image* img = new heif_image;
-  img->image = std::make_shared<HeifPixelImage>();
-
-  img->image->create(width, height, colorspace, chroma);
-
-  *image = img;
-
-  return heif_error_success;
-}
-
-int heif_image_get_decoding_warnings(struct heif_image* image,
-                                     int first_warning_idx,
-                                     struct heif_error* out_warnings,
-                                     int max_output_buffer_entries)
-{
-  if (max_output_buffer_entries == 0) {
-    return (int) image->image->get_warnings().size();
-  }
-  else {
-    const auto& warnings = image->image->get_warnings();
-    int n;
-    for (n = 0; n + first_warning_idx < (int) warnings.size(); n++) {
-      out_warnings[n] = warnings[n + first_warning_idx].error_struct(image->image.get());
-    }
-    return n;
-  }
-}
-
-void heif_image_add_decoding_warning(struct heif_image* image,
-                                     struct heif_error err)
-{
-  image->image->add_warning(Error(err.code, err.subcode));
-}
-
-
-int heif_image_has_content_light_level(const struct heif_image* image)
-{
-  return image->image->has_clli();
-}
-
-void heif_image_get_content_light_level(const struct heif_image* image, struct heif_content_light_level* out)
-{
-  if (out) {
-    *out = image->image->get_clli();
-  }
-}
-
-int heif_image_handle_get_content_light_level(const struct heif_image_handle* handle, struct heif_content_light_level* out)
-{
-  auto clli = handle->image->get_property<Box_clli>();
-  if (out && clli) {
-    *out = clli->clli;
-  }
-
-  return clli ? 1 : 0;
-}
-
-void heif_image_set_content_light_level(const struct heif_image* image, const struct heif_content_light_level* in)
-{
-  if (in == nullptr) {
-    return;
-  }
-
-  image->image->set_clli(*in);
-}
-
-
-int heif_image_has_mastering_display_colour_volume(const struct heif_image* image)
-{
-  return image->image->has_mdcv();
-}
-
-void heif_image_get_mastering_display_colour_volume(const struct heif_image* image, struct heif_mastering_display_colour_volume* out)
-{
-  *out = image->image->get_mdcv();
-}
-
-int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle* handle, struct heif_mastering_display_colour_volume* out)
-{
-  auto mdcv = handle->image->get_property<Box_mdcv>();
-  if (out && mdcv) {
-    *out = mdcv->mdcv;
-  }
-
-  return mdcv ? 1 : 0;
-}
-
-void heif_image_set_mastering_display_colour_volume(const struct heif_image* image, const struct heif_mastering_display_colour_volume* in)
-{
-  if (in == nullptr) {
-    return;
-  }
-
-  image->image->set_mdcv(*in);
-}
-
-float mdcv_coord_decode_x(uint16_t coord)
-{
-  // check for unspecified value
-  if (coord < 5 || coord > 37000) {
-    return 0.0f;
-  }
-
-  return (float) (coord * 0.00002);
-}
-
-float mdcv_coord_decode_y(uint16_t coord)
-{
-  // check for unspecified value
-  if (coord < 5 || coord > 42000) {
-    return 0.0f;
-  }
-
-  return (float) (coord * 0.00002);
-}
-
-struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_mastering_display_colour_volume* in,
-                                                              struct heif_decoded_mastering_display_colour_volume* out)
-{
-  if (in == nullptr || out == nullptr) {
-    return error_null_parameter;
-  }
-
-  for (int c = 0; c < 3; c++) {
-    out->display_primaries_x[c] = mdcv_coord_decode_x(in->display_primaries_x[c]);
-    out->display_primaries_y[c] = mdcv_coord_decode_y(in->display_primaries_y[c]);
-  }
-
-  out->white_point_x = mdcv_coord_decode_x(in->white_point_x);
-  out->white_point_y = mdcv_coord_decode_y(in->white_point_y);
-
-  if (in->max_display_mastering_luminance < 50000 || in->max_display_mastering_luminance > 100000000) {
-    out->max_display_mastering_luminance = 0;
-  }
-  else {
-    out->max_display_mastering_luminance = in->max_display_mastering_luminance * 0.0001;
-  }
-
-  if (in->min_display_mastering_luminance < 1 || in->min_display_mastering_luminance > 50000) {
-    out->min_display_mastering_luminance = 0;
-  }
-  else {
-    out->min_display_mastering_luminance = in->min_display_mastering_luminance * 0.0001;
-  }
-
-  return heif_error_success;
-}
-
-
-void heif_image_get_pixel_aspect_ratio(const struct heif_image* image, uint32_t* aspect_h, uint32_t* aspect_v)
-{
-  image->image->get_pixel_ratio(aspect_h, aspect_v);
-}
-
-int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle* handle, uint32_t* aspect_h, uint32_t* aspect_v)
-{
-  auto pasp = handle->image->get_property<Box_pasp>();
-  if (pasp) {
-    *aspect_h = pasp->hSpacing;
-    *aspect_v = pasp->vSpacing;
-    return 1;
-  }
-  else {
-    *aspect_h = 1;
-    *aspect_v = 1;
-    return 0;
-  }
-}
-
-void heif_image_set_pixel_aspect_ratio(struct heif_image* image, uint32_t aspect_h, uint32_t aspect_v)
-{
-  image->image->set_pixel_ratio(aspect_h, aspect_v);
-}
-
-
-void heif_image_release(const struct heif_image* img)
-{
-  delete img;
-}
-
-void heif_image_handle_release(const struct heif_image_handle* handle)
-{
-  delete handle;
-}
-
-
-heif_colorspace heif_image_get_colorspace(const struct heif_image* img)
-{
-  return img->image->get_colorspace();
-}
-
-enum heif_chroma heif_image_get_chroma_format(const struct heif_image* img)
-{
-  return img->image->get_chroma_format();
-}
-
-
-static int uint32_to_int(uint32_t v)
-{
-  if (v == 0 || v > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
-    return -1;
-  }
-  else {
-    return static_cast<int>(v);
-  }
-}
-
-
-int heif_image_get_width(const struct heif_image* img, enum heif_channel channel)
-{
-  return uint32_to_int(img->image->get_width(channel));
-}
-
-
-int heif_image_get_height(const struct heif_image* img, enum heif_channel channel)
-{
-  return uint32_to_int(img->image->get_height(channel));
-}
-
-
-int heif_image_get_primary_width(const struct heif_image* img)
-{
-  if (img->image->get_colorspace() == heif_colorspace_RGB) {
-    if (img->image->get_chroma_format() == heif_chroma_444) {
-      return uint32_to_int(img->image->get_width(heif_channel_G));
-    }
-    else {
-      return uint32_to_int(img->image->get_width(heif_channel_interleaved));
-    }
-  }
-  else {
-    return uint32_to_int(img->image->get_width(heif_channel_Y));
-  }
-}
-
-
-int heif_image_get_primary_height(const struct heif_image* img)
-{
-  if (img->image->get_colorspace() == heif_colorspace_RGB) {
-    if (img->image->get_chroma_format() == heif_chroma_444) {
-      return uint32_to_int(img->image->get_height(heif_channel_G));
-    }
-    else {
-      return uint32_to_int(img->image->get_height(heif_channel_interleaved));
-    }
-  }
-  else {
-    return uint32_to_int(img->image->get_height(heif_channel_Y));
-  }
-}
-
-
-heif_error heif_image_crop(struct heif_image* img,
-                           int left, int right, int top, int bottom)
-{
-  uint32_t w = img->image->get_width();
-  uint32_t h = img->image->get_height();
-
-  if (w==0 || w>0x7FFFFFFF ||
-      h==0 || h>0x7FFFFFFF) {
-    return heif_error{heif_error_Usage_error,
-                      heif_suberror_Invalid_image_size,
-                      "Image size exceeds maximum supported size"};
-  }
-
-  auto cropResult = img->image->crop(left, static_cast<int>(w) - 1 - right, top, static_cast<int>(h) - 1 - bottom, nullptr);
-  if (cropResult.error) {
-    return cropResult.error.error_struct(img->image.get());
-  }
-
-  img->image = cropResult.value;
-
-  return heif_error_success;
-}
-
-
-int heif_image_get_bits_per_pixel(const struct heif_image* img, enum heif_channel channel)
-{
-  return img->image->get_storage_bits_per_pixel(channel);
-}
-
-
-int heif_image_get_bits_per_pixel_range(const struct heif_image* img, enum heif_channel channel)
-{
-  return img->image->get_bits_per_pixel(channel);
-}
-
-
-int heif_image_has_channel(const struct heif_image* img, enum heif_channel channel)
-{
-  return img->image->has_channel(channel);
-}
-
-
-struct heif_error heif_image_add_plane(struct heif_image* image,
-                                       heif_channel channel, int width, int height, int bit_depth)
-{
-  // Note: no security limit, because this is explicitly requested by the user.
-  if (auto err = image->image->add_plane(channel, width, height, bit_depth, nullptr)) {
-    return err.error_struct(image->image.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-struct heif_error heif_image_add_channel(struct heif_image* image,
-                                         enum heif_channel channel,
-                                         int width, int height,
-                                         heif_channel_datatype datatype, int bit_depth)
-{
-  if (auto err = image->image->add_channel(channel, width, height, datatype, bit_depth, nullptr)) {
-    return err.error_struct(image->image.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-const uint8_t* heif_image_get_plane_readonly(const struct heif_image* image,
-                                             enum heif_channel channel,
-                                             int* out_stride)
-{
-  if (!out_stride) {
-    return nullptr;
-  }
-
-  if (!image || !image->image) {
-    *out_stride = 0;
-    return nullptr;
-  }
-
-  size_t stride;
-  const auto* p = image->image->get_plane(channel, &stride);
-
-  // TODO: use C++20 std::cmp_greater()
-  if (stride > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
-    return nullptr;
-  }
-
-  *out_stride = static_cast<int>(stride);
-  return p;
-}
-
-
-uint8_t* heif_image_get_plane(struct heif_image* image,
-                              enum heif_channel channel,
-                              int* out_stride)
-{
-  if (!out_stride) {
-    return nullptr;
-  }
-
-  if (!image || !image->image) {
-    *out_stride = 0;
-    return nullptr;
-  }
-
-  size_t stride;
-  uint8_t* p = image->image->get_plane(channel, &stride);
-
-  // TODO: use C++20 std::cmp_greater()
-  if (stride > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
-    return nullptr;
-  }
-
-  *out_stride = static_cast<int>(stride);
-  return p;
-}
-
-
-enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* image, enum heif_channel channel)
-{
-  if (image == nullptr) {
-    return heif_channel_datatype_undefined;
-  }
-
-  return image->image->get_datatype(channel);
-}
-
-
-int heif_image_list_channels(struct heif_image* image,
-                             enum heif_channel** out_channels)
-{
-  if (!image || !out_channels) {
-    return 0;
-  }
-
-  auto channels = image->image->get_channel_set();
-
-  *out_channels = new heif_channel[channels.size()];
-  heif_channel* p = *out_channels;
-  for (heif_channel c : channels) {
-    *p++ = c;
-  }
-
-  assert(channels.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
-
-  return static_cast<int>(channels.size());
-}
-
-
-void heif_channel_release_list(enum heif_channel** channels)
-{
-  delete[] channels;
-}
-
-
-
-#define heif_image_get_channel_X(name, type, datatype, bits) \
-const type* heif_image_get_channel_ ## name ## _readonly(const struct heif_image* image, \
-                                                         enum heif_channel channel, \
-                                                         size_t* out_stride) \
-{                                                            \
-  if (!image || !image->image) {                             \
-    *out_stride = 0;                                         \
-    return nullptr;                                          \
-  }                                                          \
-                                                             \
-  if (image->image->get_datatype(channel) != datatype) {     \
-    return nullptr;                                          \
-  }                                                          \
-  if (image->image->get_storage_bits_per_pixel(channel) != bits) {     \
-    return nullptr;                                          \
-  }                                                          \
-  return  image->image->get_channel<type>(channel, out_stride);                      \
-}                                                            \
-                                                             \
-type* heif_image_get_channel_ ## name (struct heif_image* image, \
-                                       enum heif_channel channel, \
-                                       size_t* out_stride)      \
-{                                                            \
-  if (!image || !image->image) {                             \
-    *out_stride = 0;                                         \
-    return nullptr;                                          \
-  }                                                          \
-                                                             \
-  if (image->image->get_datatype(channel) != datatype) {     \
-    return nullptr;                                          \
-  }                                                          \
-  if (image->image->get_storage_bits_per_pixel(channel) != bits) {     \
-    return nullptr;                                          \
-  }                                                          \
-  return image->image->get_channel<type>(channel, out_stride); \
-}
-
-heif_image_get_channel_X(uint16, uint16_t, heif_channel_datatype_unsigned_integer, 16)
-heif_image_get_channel_X(uint32, uint32_t, heif_channel_datatype_unsigned_integer, 32)
-heif_image_get_channel_X(uint64, uint64_t, heif_channel_datatype_unsigned_integer, 64)
-heif_image_get_channel_X(int16, int16_t, heif_channel_datatype_signed_integer, 16)
-heif_image_get_channel_X(int32, int32_t, heif_channel_datatype_signed_integer, 32)
-heif_image_get_channel_X(int64, int64_t, heif_channel_datatype_signed_integer, 64)
-heif_image_get_channel_X(float32, float, heif_channel_datatype_floating_point, 32)
-heif_image_get_channel_X(float64, double, heif_channel_datatype_floating_point, 64)
-heif_image_get_channel_X(complex32, heif_complex32, heif_channel_datatype_complex_number, 64)
-heif_image_get_channel_X(complex64, heif_complex64, heif_channel_datatype_complex_number, 64)
-
-
-void heif_image_set_premultiplied_alpha(struct heif_image* image,
-                                        int is_premultiplied_alpha)
-{
-  if (image == nullptr) {
-    return;
-  }
-
-  image->image->set_premultiplied_alpha(is_premultiplied_alpha);
-}
-
-
-int heif_image_is_premultiplied_alpha(struct heif_image* image)
-{
-  if (image == nullptr) {
-    return 0;
-  }
-
-  return image->image->is_premultiplied_alpha();
-}
-
-
-struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height)
-{
-  Error err = image->image->extend_padding_to_size(min_physical_width, min_physical_height, false, nullptr);
-  if (err) {
-    return err.error_struct(image->image.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-struct heif_error heif_image_scale_image(const struct heif_image* input,
-                                         struct heif_image** output,
-                                         int width, int height,
-                                         const struct heif_scaling_options* options)
-{
-  std::shared_ptr<HeifPixelImage> out_img;
-
-  Error err = input->image->scale_nearest_neighbor(out_img, width, height, nullptr);
-  if (err) {
-    return err.error_struct(input->image.get());
-  }
-
-  *output = new heif_image;
-  (*output)->image = std::move(out_img);
-
-  return Error::Ok.error_struct(input->image.get());
-}
-
-
-struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image,
-                                                           uint32_t width, uint32_t height)
-{
-  Error err = image->image->extend_to_size_with_zero(width, height, nullptr);
-  if (err) {
-    return err.error_struct(image->image.get());
-  }
-
-  return heif_error_ok;
-}
-
-
-struct heif_error heif_image_set_raw_color_profile(struct heif_image* image,
-                                                   const char* color_profile_type_fourcc,
-                                                   const void* profile_data,
-                                                   const size_t profile_size)
-{
-  if (strlen(color_profile_type_fourcc) != 4) {
-    heif_error err = {heif_error_Usage_error,
-                      heif_suberror_Unspecified,
-                      "Invalid color_profile_type (must be 4 characters)"};
-    return err;
-  }
-
-  uint32_t color_profile_type = fourcc(color_profile_type_fourcc);
-
-  std::vector<uint8_t> data;
-  data.insert(data.end(),
-              (const uint8_t*) profile_data,
-              (const uint8_t*) profile_data + profile_size);
-
-  auto color_profile = std::make_shared<color_profile_raw>(color_profile_type, data);
-
-  image->image->set_color_profile_icc(color_profile);
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_image_set_nclx_color_profile(struct heif_image* image,
-                                                    const struct heif_color_profile_nclx* color_profile)
-{
-  auto nclx = std::make_shared<color_profile_nclx>();
-
-  nclx->set_colour_primaries(color_profile->color_primaries);
-  nclx->set_transfer_characteristics(color_profile->transfer_characteristics);
-  nclx->set_matrix_coefficients(color_profile->matrix_coefficients);
-  nclx->set_full_range_flag(color_profile->full_range_flag);
-
-  image->image->set_color_profile_nclx(nclx);
-
-  return heif_error_success;
-}
-
-
-/*
-void heif_image_remove_color_profile(struct heif_image* image)
-{
-  image->image->set_color_profile(nullptr);
-}
-*/
-
-
-int heif_image_handle_get_number_of_metadata_blocks(const struct heif_image_handle* handle,
-                                                    const char* type_filter)
-{
-  int cnt = 0;
-  for (const auto& metadata : handle->image->get_metadata()) {
-    if (type_filter == nullptr ||
-        metadata->item_type == type_filter) {
-      cnt++;
-    }
-  }
-
-  return cnt;
-}
-
-
-int heif_image_handle_get_list_of_metadata_block_IDs(const struct heif_image_handle* handle,
-                                                     const char* type_filter,
-                                                     heif_item_id* ids, int count)
-{
-  int cnt = 0;
-  for (const auto& metadata : handle->image->get_metadata()) {
-    if (type_filter == nullptr ||
-        metadata->item_type == type_filter) {
-      if (cnt < count) {
-        ids[cnt] = metadata->item_id;
-        cnt++;
-      }
-      else {
-        break;
-      }
-    }
-  }
-
-  return cnt;
-}
-
-
-const char* heif_image_handle_get_metadata_type(const struct heif_image_handle* handle,
-                                                heif_item_id metadata_id)
-{
-  for (auto& metadata : handle->image->get_metadata()) {
-    if (metadata->item_id == metadata_id) {
-      return metadata->item_type.c_str();
-    }
-  }
-
-  return nullptr;
-}
-
-
-const char* heif_image_handle_get_metadata_content_type(const struct heif_image_handle* handle,
-                                                        heif_item_id metadata_id)
-{
-  for (auto& metadata : handle->image->get_metadata()) {
-    if (metadata->item_id == metadata_id) {
-      return metadata->content_type.c_str();
-    }
-  }
-
-  return nullptr;
-}
-
-
-const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle,
-                                                         heif_item_id metadata_id)
-{
-  for (auto& metadata : handle->image->get_metadata()) {
-    if (metadata->item_id == metadata_id) {
-      return metadata->item_uri_type.c_str();
-    }
-  }
-
-  return nullptr;
-}
-
-
-size_t heif_image_handle_get_metadata_size(const struct heif_image_handle* handle,
-                                           heif_item_id metadata_id)
-{
-  for (auto& metadata : handle->image->get_metadata()) {
-    if (metadata->item_id == metadata_id) {
-      return metadata->m_data.size();
-    }
-  }
-
-  return 0;
-}
-
-
-struct heif_error heif_image_handle_get_metadata(const struct heif_image_handle* handle,
-                                                 heif_item_id metadata_id,
-                                                 void* out_data)
-{
-  for (auto& metadata : handle->image->get_metadata()) {
-    if (metadata->item_id == metadata_id) {
-
-      if (!metadata->m_data.empty()) {
-        if (out_data == nullptr) {
-          Error err(heif_error_Usage_error,
-                    heif_suberror_Null_pointer_argument);
-          return err.error_struct(handle->image.get());
-        }
-
-        memcpy(out_data,
-               metadata->m_data.data(),
-               metadata->m_data.size());
-      }
-
-      return Error::Ok.error_struct(handle->image.get());
-    }
-  }
-
-  Error err(heif_error_Usage_error,
-            heif_suberror_Nonexisting_item_referenced);
-  return err.error_struct(handle->image.get());
-}
-
-heif_color_profile_type heif_image_handle_get_color_profile_type(const struct heif_image_handle* handle)
-{
-  auto profile_icc = handle->image->get_color_profile_icc();
-  if (profile_icc) {
-    return (heif_color_profile_type) profile_icc->get_type();
-  }
-
-  auto profile_nclx = handle->image->get_color_profile_nclx();
-  if (profile_nclx) {
-    return (heif_color_profile_type) profile_nclx->get_type();
-  }
-  else {
-    return heif_color_profile_type_not_present;
-  }
-}
-
-size_t heif_image_handle_get_raw_color_profile_size(const struct heif_image_handle* handle)
-{
-  auto profile_icc = handle->image->get_color_profile_icc();
-  if (profile_icc) {
-    return profile_icc->get_data().size();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-static const std::set<typename std::underlying_type<heif_color_primaries>::type> known_color_primaries{
-    heif_color_primaries_ITU_R_BT_709_5,
-    heif_color_primaries_unspecified,
-    heif_color_primaries_ITU_R_BT_470_6_System_M,
-    heif_color_primaries_ITU_R_BT_470_6_System_B_G,
-    heif_color_primaries_ITU_R_BT_601_6,
-    heif_color_primaries_SMPTE_240M,
-    heif_color_primaries_generic_film,
-    heif_color_primaries_ITU_R_BT_2020_2_and_2100_0,
-    heif_color_primaries_SMPTE_ST_428_1,
-    heif_color_primaries_SMPTE_RP_431_2,
-    heif_color_primaries_SMPTE_EG_432_1,
-    heif_color_primaries_EBU_Tech_3213_E,
-};
-
-struct heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp)
-{
-  if (static_cast<std::underlying_type<heif_color_primaries>::type>(cp) > std::numeric_limits<std::underlying_type<heif_color_primaries>::type>::max()) {
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr);
-  }
-
-  auto n = static_cast<typename std::underlying_type<heif_color_primaries>::type>(cp);
-  if (known_color_primaries.find(n) != known_color_primaries.end()) {
-    nclx->color_primaries = static_cast<heif_color_primaries>(n);
-  }
-  else {
-    nclx->color_primaries = heif_color_primaries_unspecified;
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr);
-  }
-
-  return Error::Ok.error_struct(nullptr);
-}
-
-
-static const std::set<typename std::underlying_type<heif_transfer_characteristics>::type> known_transfer_characteristics{
-    heif_transfer_characteristic_ITU_R_BT_709_5,
-    heif_transfer_characteristic_unspecified,
-    heif_transfer_characteristic_ITU_R_BT_470_6_System_M,
-    heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G,
-    heif_transfer_characteristic_ITU_R_BT_601_6,
-    heif_transfer_characteristic_SMPTE_240M,
-    heif_transfer_characteristic_linear,
-    heif_transfer_characteristic_logarithmic_100,
-    heif_transfer_characteristic_logarithmic_100_sqrt10,
-    heif_transfer_characteristic_IEC_61966_2_4,
-    heif_transfer_characteristic_ITU_R_BT_1361,
-    heif_transfer_characteristic_IEC_61966_2_1,
-    heif_transfer_characteristic_ITU_R_BT_2020_2_10bit,
-    heif_transfer_characteristic_ITU_R_BT_2020_2_12bit,
-    heif_transfer_characteristic_ITU_R_BT_2100_0_PQ,
-    heif_transfer_characteristic_SMPTE_ST_428_1,
-    heif_transfer_characteristic_ITU_R_BT_2100_0_HLG
-};
-
-
-struct heif_error heif_nclx_color_profile_set_transfer_characteristics(struct heif_color_profile_nclx* nclx, uint16_t tc)
-{
-  if (static_cast<std::underlying_type<heif_color_primaries>::type>(tc) > std::numeric_limits<std::underlying_type<heif_transfer_characteristics>::type>::max()) {
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr);
-  }
-
-  auto n = static_cast<typename std::underlying_type<heif_transfer_characteristics>::type>(tc);
-  if (known_transfer_characteristics.find(n) != known_transfer_characteristics.end()) {
-    nclx->transfer_characteristics = static_cast<heif_transfer_characteristics>(n);
-  }
-  else {
-    nclx->transfer_characteristics = heif_transfer_characteristic_unspecified;
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr);
-  }
-
-  return Error::Ok.error_struct(nullptr);
-}
-
-
-static const std::set<typename std::underlying_type<heif_matrix_coefficients>::type> known_matrix_coefficients{
-    heif_matrix_coefficients_RGB_GBR,
-    heif_matrix_coefficients_ITU_R_BT_709_5,
-    heif_matrix_coefficients_unspecified,
-    heif_matrix_coefficients_US_FCC_T47,
-    heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G,
-    heif_matrix_coefficients_ITU_R_BT_601_6,
-    heif_matrix_coefficients_SMPTE_240M,
-    heif_matrix_coefficients_YCgCo,
-    heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance,
-    heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance,
-    heif_matrix_coefficients_SMPTE_ST_2085,
-    heif_matrix_coefficients_chromaticity_derived_non_constant_luminance,
-    heif_matrix_coefficients_chromaticity_derived_constant_luminance,
-    heif_matrix_coefficients_ICtCp
-};
-
-struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t mc)
-{
-  if (static_cast<std::underlying_type<heif_color_primaries>::type>(mc) > std::numeric_limits<std::underlying_type<heif_matrix_coefficients>::type>::max()) {
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr);
-  }
-
-  auto n = static_cast<typename std::underlying_type<heif_matrix_coefficients>::type>(mc);
-  if (known_matrix_coefficients.find(n) != known_matrix_coefficients.end()) {
-    nclx->matrix_coefficients = static_cast<heif_matrix_coefficients>(n);;
-  }
-  else {
-    nclx->matrix_coefficients = heif_matrix_coefficients_unspecified;
-    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr);
-  }
-
-  return Error::Ok.error_struct(nullptr);
-}
-
-
-struct heif_error heif_image_handle_get_nclx_color_profile(const struct heif_image_handle* handle,
-                                                           struct heif_color_profile_nclx** out_data)
-{
-  if (!out_data) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(handle->image.get());
-  }
-
-  auto nclx_profile = handle->image->get_color_profile_nclx();
-  if (!nclx_profile) {
-    Error err(heif_error_Color_profile_does_not_exist,
-              heif_suberror_Unspecified);
-    return err.error_struct(handle->image.get());
-  }
-
-  Error err = nclx_profile->get_nclx_color_profile(out_data);
-
-  return err.error_struct(handle->image.get());
-}
-
-
-struct heif_error heif_image_handle_get_raw_color_profile(const struct heif_image_handle* handle,
-                                                          void* out_data)
-{
-  if (out_data == nullptr) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(handle->image.get());
-  }
-
-  auto raw_profile = handle->image->get_color_profile_icc();
-  if (raw_profile) {
-    memcpy(out_data,
-           raw_profile->get_data().data(),
-           raw_profile->get_data().size());
-  }
-  else {
-    Error err(heif_error_Color_profile_does_not_exist,
-              heif_suberror_Unspecified);
-    return err.error_struct(handle->image.get());
-  }
-
-  return Error::Ok.error_struct(handle->image.get());
-}
-
-
-enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image)
-{
-  std::shared_ptr<const color_profile> profile;
-
-  profile = image->image->get_color_profile_icc();
-  if (!profile) {
-    profile = image->image->get_color_profile_nclx();
-  }
-
-  if (!profile) {
-    return heif_color_profile_type_not_present;
-  }
-  else {
-    return (heif_color_profile_type) profile->get_type();
-  }
-}
-
-
-size_t heif_image_get_raw_color_profile_size(const struct heif_image* image)
-{
-  auto raw_profile = image->image->get_color_profile_icc();
-  if (raw_profile) {
-    return raw_profile->get_data().size();
-  }
-  else {
-    return 0;
-  }
-}
-
-
-struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image,
-                                                   void* out_data)
-{
-  if (out_data == nullptr) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(image->image.get());
-  }
-
-  auto raw_profile = image->image->get_color_profile_icc();
-  if (raw_profile) {
-    memcpy(out_data,
-           raw_profile->get_data().data(),
-           raw_profile->get_data().size());
-  }
-  else {
-    Error err(heif_error_Color_profile_does_not_exist,
-              heif_suberror_Unspecified);
-    return err.error_struct(image->image.get());
-  }
-
-  return Error::Ok.error_struct(image->image.get());
-}
-
-
-struct heif_error heif_image_get_nclx_color_profile(const struct heif_image* image,
-                                                    struct heif_color_profile_nclx** out_data)
-{
-  if (!out_data) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(image->image.get());
-  }
-
-  auto nclx_profile = image->image->get_color_profile_nclx();
-
-  if (!nclx_profile) {
-    Error err(heif_error_Color_profile_does_not_exist,
-              heif_suberror_Unspecified);
-    return err.error_struct(image->image.get());
-  }
-
-  Error err = nclx_profile->get_nclx_color_profile(out_data);
-
-  return err.error_struct(image->image.get());
-}
-
-
-struct heif_color_profile_nclx* heif_nclx_color_profile_alloc()
-{
-  return color_profile_nclx::alloc_nclx_color_profile();
-}
-
-
-void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile)
-{
-  color_profile_nclx::free_nclx_color_profile(nclx_profile);
-}
-
-int heif_image_handle_has_camera_intrinsic_matrix(const struct heif_image_handle* handle)
-{
-  if (!handle) {
-    return false;
-  }
-
-  return handle->image->has_intrinsic_matrix();
-}
-
-struct heif_error heif_image_handle_get_camera_intrinsic_matrix(const struct heif_image_handle* handle,
-                                                                struct heif_camera_intrinsic_matrix* out_matrix)
-{
-  if (handle == nullptr || out_matrix == nullptr) {
-    return heif_error{heif_error_Usage_error,
-                      heif_suberror_Null_pointer_argument};
-  }
-
-  if (!handle->image->has_intrinsic_matrix()) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Camera_intrinsic_matrix_undefined);
-    return err.error_struct(handle->image.get());
-  }
-
-  const auto& m = handle->image->get_intrinsic_matrix();
-  out_matrix->focal_length_x = m.focal_length_x;
-  out_matrix->focal_length_y = m.focal_length_y;
-  out_matrix->principal_point_x = m.principal_point_x;
-  out_matrix->principal_point_y = m.principal_point_y;
-  out_matrix->skew = m.skew;
-
-  return heif_error_success;
-}
-
-int heif_image_handle_has_camera_extrinsic_matrix(const struct heif_image_handle* handle)
-{
-  if (!handle) {
-    return false;
-  }
-
-  return handle->image->has_extrinsic_matrix();
-}
-
-struct heif_camera_extrinsic_matrix
-{
-  Box_cmex::ExtrinsicMatrix matrix;
-};
-
-struct heif_error heif_image_handle_get_camera_extrinsic_matrix(const struct heif_image_handle* handle,
-                                                                struct heif_camera_extrinsic_matrix** out_matrix)
-{
-  if (handle == nullptr || out_matrix == nullptr) {
-    return heif_error{heif_error_Usage_error,
-                      heif_suberror_Null_pointer_argument};
-  }
-
-  if (!handle->image->has_extrinsic_matrix()) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Camera_extrinsic_matrix_undefined);
-    return err.error_struct(handle->image.get());
-  }
-
-  *out_matrix = new heif_camera_extrinsic_matrix;
-  (*out_matrix)->matrix = handle->image->get_extrinsic_matrix();
-
-  return heif_error_success;
-}
-
-void heif_camera_extrinsic_matrix_release(struct heif_camera_extrinsic_matrix* matrix)
-{
-  delete matrix;
-}
-
-struct heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_camera_extrinsic_matrix* matrix,
-                                                      double* out_matrix_row_major)
-{
-  if (matrix == nullptr || out_matrix_row_major == nullptr) {
-    return heif_error{heif_error_Usage_error,
-                      heif_suberror_Null_pointer_argument};
-  }
-
-  auto m3x3 = matrix->matrix.calculate_rotation_matrix();
-
-  for (int i=0;i<9;i++) {
-    out_matrix_row_major[i] = m3x3[i];
-  }
-
-  return heif_error_success;
-}
-
-
-
-// DEPRECATED
-struct heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin* decoder_plugin)
-{
-  return heif_register_decoder_plugin(decoder_plugin);
-}
-
-
-struct heif_error heif_register_decoder_plugin(const heif_decoder_plugin* decoder_plugin)
-{
-  if (!decoder_plugin) {
-    return error_null_parameter;
-  }
-  else if (decoder_plugin->plugin_api_version > 3) {
-    return error_unsupported_plugin_version;
-  }
-
-  register_decoder(decoder_plugin);
-  return heif_error_success;
-}
-
-struct heif_error heif_register_encoder_plugin(const heif_encoder_plugin* encoder_plugin)
-{
-  if (!encoder_plugin) {
-    return error_null_parameter;
-  }
-  else if (encoder_plugin->plugin_api_version > 3) {
-    return error_unsupported_plugin_version;
-  }
-
-  register_encoder(encoder_plugin);
-  return heif_error_success;
-}
-
-
-/*
-int  heif_image_get_number_of_data_chunks(heif_image* img);
-
-void heif_image_get_data_chunk(heif_image* img, int chunk_index,
-                               uint8_t const*const* dataptr,
-                               int const* data_size);
-
-void heif_image_free_data_chunk(heif_image* img, int chunk_index);
-*/
-
-
-/*
-void heif_context_reset(struct heif_context* ctx)
-{
-  ctx->context->reset_to_empty_heif();
-}
-*/
-
-static struct heif_error heif_file_writer_write(struct heif_context* ctx,
-                                                const void* data, size_t size, void* userdata)
-{
-  const char* filename = static_cast<const char*>(userdata);
-
-#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
-  std::ofstream ostr(HeifFile::convert_utf8_path_to_utf16(filename).c_str(), std::ios_base::binary);
-#else
-  std::ofstream ostr(filename, std::ios_base::binary);
-#endif
-  ostr.write(static_cast<const char*>(data), size);
-  // TODO: handle write errors
-  return Error::Ok.error_struct(ctx->context.get());
-}
-
-
-struct heif_error heif_context_write_to_file(struct heif_context* ctx,
-                                             const char* filename)
-{
-  heif_writer writer;
-  writer.writer_api_version = 1;
-  writer.write = heif_file_writer_write;
-  return heif_context_write(ctx, &writer, (void*) filename);
-}
-
-
-struct heif_error heif_context_write(struct heif_context* ctx,
-                                     struct heif_writer* writer,
-                                     void* userdata)
-{
-  if (!writer) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
-  }
-  else if (writer->writer_api_version != 1) {
-    Error err(heif_error_Usage_error, heif_suberror_Unsupported_writer_version);
-    return err.error_struct(ctx->context.get());
-  }
-
-  StreamWriter swriter;
-  ctx->context->write(swriter);
-
-  const auto& data = swriter.get_data();
-  heif_error writer_error = writer->write(ctx, data.data(), data.size(), userdata);
-  if (!writer_error.message) {
-    // It is now allowed to return a NULL error message on success. It will be replaced by "Success". An error message is still required when there is an error.
-    if (writer_error.code == heif_error_Ok) {
-      writer_error.message = Error::kSuccess;
-      return writer_error;
-    }
-    else {
-      return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_writer callback returned a null error text"};
-    }
-  }
-  else {
-    return writer_error;
-  }
-}
-
-
-void heif_context_add_compatible_brand(struct heif_context* ctx,
-                                       heif_brand2 compatible_brand)
-{
-  ctx->context->get_heif_file()->get_ftyp_box()->add_compatible_brand(compatible_brand);
-}
-
-
-int heif_context_get_encoder_descriptors(struct heif_context* ctx,
-                                         enum heif_compression_format format,
-                                         const char* name,
-                                         const struct heif_encoder_descriptor** out_encoder_descriptors,
-                                         int count)
-{
-  return heif_get_encoder_descriptors(format, name, out_encoder_descriptors, count);
-}
-
-
-int heif_get_encoder_descriptors(enum heif_compression_format format,
-                                 const char* name,
-                                 const struct heif_encoder_descriptor** out_encoder_descriptors,
-                                 int count)
-{
-  if (out_encoder_descriptors != nullptr && count <= 0) {
-    return 0;
-  }
-
-  std::vector<const struct heif_encoder_descriptor*> descriptors;
-  descriptors = get_filtered_encoder_descriptors(format, name);
-
-  if (out_encoder_descriptors == nullptr) {
-    return static_cast<int>(descriptors.size());
-  }
-
-  int i;
-  for (i = 0; i < count && static_cast<size_t>(i) < descriptors.size(); i++) {
-    out_encoder_descriptors[i] = descriptors[i];
-  }
-
-  return i;
-}
-
-
-const char* heif_encoder_descriptor_get_name(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->get_plugin_name();
-}
-
-
-const char* heif_encoder_descriptor_get_id_name(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->id_name;
-}
-
-
-int heif_get_decoder_descriptors(enum heif_compression_format format_filter,
-                                 const struct heif_decoder_descriptor** out_decoders,
-                                 int count)
-{
-  struct decoder_with_priority
-  {
-    const heif_decoder_plugin* plugin;
-    int priority;
-  };
-
-  std::vector<decoder_with_priority> plugins;
-  std::vector<heif_compression_format> formats;
-  if (format_filter == heif_compression_undefined) {
-    formats = {heif_compression_HEVC, heif_compression_AV1, heif_compression_JPEG, heif_compression_JPEG2000, heif_compression_HTJ2K, heif_compression_VVC};
-  }
-  else {
-    formats.emplace_back(format_filter);
-  }
-
-  for (const auto* plugin : get_decoder_plugins()) {
-    for (auto& format : formats) {
-      int priority = plugin->does_support_format(format);
-      if (priority) {
-        plugins.push_back({plugin, priority});
-        break;
-      }
-    }
-  }
-
-  if (out_decoders == nullptr) {
-    return (int) plugins.size();
-  }
-
-  std::sort(plugins.begin(), plugins.end(), [](const decoder_with_priority& a, const decoder_with_priority& b) {
-    return a.priority > b.priority;
-  });
-
-  int nDecodersReturned = std::min(count, (int) plugins.size());
-
-  for (int i = 0; i < nDecodersReturned; i++) {
-    out_decoders[i] = (heif_decoder_descriptor*) (plugins[i].plugin);
-  }
-
-  return nDecodersReturned;
-}
-
-
-const char* heif_decoder_descriptor_get_name(const struct heif_decoder_descriptor* descriptor)
-{
-  auto decoder = (heif_decoder_plugin*) descriptor;
-  return decoder->get_plugin_name();
-}
-
-
-const char* heif_decoder_descriptor_get_id_name(const struct heif_decoder_descriptor* descriptor)
-{
-  auto decoder = (heif_decoder_plugin*) descriptor;
-  if (decoder->plugin_api_version < 3) {
-    return nullptr;
-  }
-  else {
-    return decoder->id_name;
-  }
-}
-
-
-enum heif_compression_format
-heif_encoder_descriptor_get_compression_format(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->compression_format;
-}
-
-
-int heif_encoder_descriptor_supports_lossy_compression(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->supports_lossy_compression;
-}
-
-
-int heif_encoder_descriptor_supports_lossless_compression(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->supports_lossless_compression;
-}
-
-
-// DEPRECATED: typo in function name
-int heif_encoder_descriptor_supportes_lossy_compression(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->supports_lossy_compression;
-}
-
-
-// DEPRECATED: typo in function name
-int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor* descriptor)
-{
-  return descriptor->plugin->supports_lossless_compression;
-}
-
-
-const char* heif_encoder_get_name(const struct heif_encoder* encoder)
-{
-  return encoder->plugin->get_plugin_name();
-}
-
-
-struct heif_error heif_context_get_encoder(struct heif_context* context,
-                                           const struct heif_encoder_descriptor* descriptor,
-                                           struct heif_encoder** encoder)
-{
-  // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation.
-
-  if (!descriptor || !encoder) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(context ? context->context.get() : nullptr);
-  }
-
-  *encoder = new struct heif_encoder(descriptor->plugin);
-  return (*encoder)->alloc();
-}
-
-
-int heif_have_decoder_for_format(enum heif_compression_format format)
-{
-  auto plugin = get_decoder(format, nullptr);
-  return plugin != nullptr;
-}
-
-
-int heif_have_encoder_for_format(enum heif_compression_format format)
-{
-  auto plugin = get_encoder(format);
-  return plugin != nullptr;
-}
-
-
-struct heif_error heif_context_get_encoder_for_format(struct heif_context* context,
-                                                      enum heif_compression_format format,
-                                                      struct heif_encoder** encoder)
-{
-  // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation.
-
-  if (!encoder) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Null_pointer_argument);
-    return err.error_struct(context ? context->context.get() : nullptr);
-  }
-
-  std::vector<const struct heif_encoder_descriptor*> descriptors;
-  descriptors = get_filtered_encoder_descriptors(format, nullptr);
-
-  if (descriptors.size() > 0) {
-    *encoder = new struct heif_encoder(descriptors[0]->plugin);
-    return (*encoder)->alloc();
-  }
-  else {
-    *encoder = nullptr;
-    Error err(heif_error_Unsupported_filetype, // TODO: is this the right error code?
-              heif_suberror_Unspecified);
-    return err.error_struct(context ? context->context.get() : nullptr);
-  }
-}
-
-
-void heif_encoder_release(struct heif_encoder* encoder)
-{
-  if (encoder) {
-    delete encoder;
-  }
-}
-
-
-//struct heif_encoder_param* heif_encoder_get_param(struct heif_encoder* encoder)
-//{
-//  return nullptr;
-//}
-
-
-//void heif_encoder_release_param(struct heif_encoder_param* param)
-//{
-//}
-
-
-// Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is
-// encoder dependent.
-struct heif_error heif_encoder_set_lossy_quality(struct heif_encoder* encoder,
-                                                 int quality)
-{
-  if (!encoder) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
-  }
-
-  return encoder->plugin->set_parameter_quality(encoder->encoder, quality);
-}
-
-
-struct heif_error heif_encoder_set_lossless(struct heif_encoder* encoder, int enable)
-{
-  if (!encoder) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
-  }
-
-  return encoder->plugin->set_parameter_lossless(encoder->encoder, enable);
-}
-
-
-struct heif_error heif_encoder_set_logging_level(struct heif_encoder* encoder, int level)
-{
-  if (!encoder) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
-  }
-
-  if (encoder->plugin->set_parameter_logging_level) {
-    return encoder->plugin->set_parameter_logging_level(encoder->encoder, level);
-  }
-
-  return heif_error_success;
-}
-
-
-const struct heif_encoder_parameter* const* heif_encoder_list_parameters(struct heif_encoder* encoder)
-{
-  return encoder->plugin->list_parameters(encoder->encoder);
-}
-
-
-const char* heif_encoder_parameter_get_name(const struct heif_encoder_parameter* param)
-{
-  return param->name;
-}
-
-enum heif_encoder_parameter_type
-heif_encoder_parameter_get_type(const struct heif_encoder_parameter* param)
-{
-  return param->type;
-}
-
-
-struct heif_error heif_encoder_set_parameter_integer(struct heif_encoder* encoder,
-                                                     const char* parameter_name,
-                                                     int value)
-{
-  // --- check if parameter is valid
-
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-
-      int have_minimum = 0, have_maximum = 0, minimum = 0, maximum = 0, num_valid_values = 0;
-      const int* valid_values = nullptr;
-      heif_error err = heif_encoder_parameter_get_valid_integer_values((*params), &have_minimum, &have_maximum,
-                                                                       &minimum, &maximum,
-                                                                       &num_valid_values,
-                                                                       &valid_values);
-      if (err.code) {
-        return err;
-      }
-
-      if ((have_minimum && value < minimum) ||
-          (have_maximum && value > maximum)) {
-        return error_invalid_parameter_value;
-      }
-
-      if (num_valid_values > 0) {
-        bool found = false;
-        for (int i = 0; i < num_valid_values; i++) {
-          if (valid_values[i] == value) {
-            found = true;
-            break;
-          }
-        }
-
-        if (!found) {
-          return error_invalid_parameter_value;
-        }
-      }
-    }
-  }
-
-
-  // --- parameter is ok, pass it to the encoder plugin
-
-  return encoder->plugin->set_parameter_integer(encoder->encoder, parameter_name, value);
-}
-
-struct heif_error heif_encoder_get_parameter_integer(struct heif_encoder* encoder,
-                                                     const char* parameter_name,
-                                                     int* value_ptr)
-{
-  return encoder->plugin->get_parameter_integer(encoder->encoder, parameter_name, value_ptr);
-}
-
-struct heif_error
-heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_parameter* param,
-                                               int* have_minimum_maximum,
-                                               int* minimum, int* maximum)
-{
-  if (param->type != heif_encoder_parameter_type_integer) {
-    return error_unsupported_parameter; // TODO: correct error ?
-  }
-
-  if (param->integer.have_minimum_maximum) {
-    if (minimum) {
-      *minimum = param->integer.minimum;
-    }
-
-    if (maximum) {
-      *maximum = param->integer.maximum;
-    }
-  }
-
-  if (have_minimum_maximum) {
-    *have_minimum_maximum = param->integer.have_minimum_maximum;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter* param,
-                                                                  int* have_minimum, int* have_maximum,
-                                                                  int* minimum, int* maximum,
-                                                                  int* num_valid_values,
-                                                                  const int** out_integer_array)
-{
-  if (param->type != heif_encoder_parameter_type_integer) {
-    return error_unsupported_parameter; // TODO: correct error ?
-  }
-
-
-  // --- range of values
-
-  if (param->integer.have_minimum_maximum) {
-    if (minimum) {
-      *minimum = param->integer.minimum;
-    }
-
-    if (maximum) {
-      *maximum = param->integer.maximum;
-    }
-  }
-
-  if (have_minimum) {
-    *have_minimum = param->integer.have_minimum_maximum;
-  }
-
-  if (have_maximum) {
-    *have_maximum = param->integer.have_minimum_maximum;
-  }
-
-
-  // --- set of valid values
-
-  if (param->integer.num_valid_values > 0) {
-    if (out_integer_array) {
-      *out_integer_array = param->integer.valid_values;
-    }
-  }
-
-  if (num_valid_values) {
-    *num_valid_values = param->integer.num_valid_values;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error
-heif_encoder_parameter_get_valid_string_values(const struct heif_encoder_parameter* param,
-                                               const char* const** out_stringarray)
-{
-  if (param->type != heif_encoder_parameter_type_string) {
-    return error_unsupported_parameter; // TODO: correct error ?
-  }
-
-  if (out_stringarray) {
-    *out_stringarray = param->string.valid_values;
-  }
-
-  return heif_error_success;
-}
-
-struct heif_error heif_encoder_parameter_integer_valid_range(struct heif_encoder* encoder,
-                                                             const char* parameter_name,
-                                                             int* have_minimum_maximum,
-                                                             int* minimum, int* maximum)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-      return heif_encoder_parameter_get_valid_integer_range(*params, have_minimum_maximum,
-                                                            minimum, maximum);
-    }
-  }
-
-  return error_unsupported_parameter;
-}
-
-struct heif_error heif_encoder_set_parameter_boolean(struct heif_encoder* encoder,
-                                                     const char* parameter_name,
-                                                     int value)
-{
-  return encoder->plugin->set_parameter_boolean(encoder->encoder, parameter_name, value);
-}
-
-struct heif_error heif_encoder_get_parameter_boolean(struct heif_encoder* encoder,
-                                                     const char* parameter_name,
-                                                     int* value_ptr)
-{
-  return encoder->plugin->get_parameter_boolean(encoder->encoder, parameter_name, value_ptr);
-}
-
-struct heif_error heif_encoder_set_parameter_string(struct heif_encoder* encoder,
-                                                    const char* parameter_name,
-                                                    const char* value)
-{
-  return encoder->plugin->set_parameter_string(encoder->encoder, parameter_name, value);
-}
-
-struct heif_error heif_encoder_get_parameter_string(struct heif_encoder* encoder,
-                                                    const char* parameter_name,
-                                                    char* value_ptr, int value_size)
-{
-  return encoder->plugin->get_parameter_string(encoder->encoder, parameter_name,
-                                               value_ptr, value_size);
-}
-
-struct heif_error heif_encoder_parameter_string_valid_values(struct heif_encoder* encoder,
-                                                             const char* parameter_name,
-                                                             const char* const** out_stringarray)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-      return heif_encoder_parameter_get_valid_string_values(*params, out_stringarray);
-    }
-  }
-
-  return error_unsupported_parameter;
-}
-
-struct heif_error heif_encoder_parameter_integer_valid_values(struct heif_encoder* encoder,
-                                                              const char* parameter_name,
-                                                              int* have_minimum, int* have_maximum,
-                                                              int* minimum, int* maximum,
-                                                              int* num_valid_values,
-                                                              const int** out_integer_array)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-      return heif_encoder_parameter_get_valid_integer_values(*params, have_minimum, have_maximum, minimum, maximum,
-                                                             num_valid_values, out_integer_array);
-    }
-  }
-
-  return error_unsupported_parameter;
-}
-
-
-static bool parse_boolean(const char* value)
-{
-  if (strcmp(value, "true") == 0) {
-    return true;
-  }
-  else if (strcmp(value, "false") == 0) {
-    return false;
-  }
-  else if (strcmp(value, "1") == 0) {
-    return true;
-  }
-  else if (strcmp(value, "0") == 0) {
-    return false;
-  }
-
-  return false;
-}
-
-
-struct heif_error heif_encoder_set_parameter(struct heif_encoder* encoder,
-                                             const char* parameter_name,
-                                             const char* value)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-      switch ((*params)->type) {
-        case heif_encoder_parameter_type_integer:
-          return heif_encoder_set_parameter_integer(encoder, parameter_name, atoi(value));
-
-        case heif_encoder_parameter_type_boolean:
-          return heif_encoder_set_parameter_boolean(encoder, parameter_name, parse_boolean(value));
-
-        case heif_encoder_parameter_type_string:
-          return heif_encoder_set_parameter_string(encoder, parameter_name, value);
-          break;
-      }
-
-      return heif_error_success;
-    }
-  }
-
-  return heif_encoder_set_parameter_string(encoder, parameter_name, value);
-
-  //return error_unsupported_parameter;
-}
-
-
-struct heif_error heif_encoder_get_parameter(struct heif_encoder* encoder,
-                                             const char* parameter_name,
-                                             char* value_ptr, int value_size)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-      switch ((*params)->type) {
-        case heif_encoder_parameter_type_integer: {
-          int value;
-          struct heif_error error = heif_encoder_get_parameter_integer(encoder, parameter_name, &value);
-          if (error.code) {
-            return error;
-          }
-          else {
-            snprintf(value_ptr, value_size, "%d", value);
-          }
-        }
-          break;
-
-        case heif_encoder_parameter_type_boolean: {
-          int value;
-          struct heif_error error = heif_encoder_get_parameter_boolean(encoder, parameter_name, &value);
-          if (error.code) {
-            return error;
-          }
-          else {
-            snprintf(value_ptr, value_size, "%d", value);
-          }
-        }
-          break;
-
-        case heif_encoder_parameter_type_string: {
-          struct heif_error error = heif_encoder_get_parameter_string(encoder, parameter_name,
-                                                                      value_ptr, value_size);
-          if (error.code) {
-            return error;
-          }
-        }
-          break;
-      }
-
-      return heif_error_success;
-    }
-  }
-
-  return error_unsupported_parameter;
-}
-
-
-int heif_encoder_has_default(struct heif_encoder* encoder,
-                             const char* parameter_name)
-{
-  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
-       *params;
-       params++) {
-    if (strcmp((*params)->name, parameter_name) == 0) {
-
-      if ((*params)->version >= 2) {
-        return (*params)->has_default;
-      }
-      else {
-        return true;
-      }
-    }
-  }
-
-  return false;
-}
-
-
-void set_default_encoding_options(heif_encoding_options& options)
-{
-  options.version = 7;
-
-  options.save_alpha_channel = true;
-  options.macOS_compatibility_workaround = false;
-  options.save_two_colr_boxes_when_ICC_and_nclx_available = false;
-  options.output_nclx_profile = nullptr;
-  options.macOS_compatibility_workaround_no_nclx_profile = false;
-  options.image_orientation = heif_orientation_normal;
-
-  options.color_conversion_options.version = 1;
-  options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
-  options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
-  options.color_conversion_options.only_use_preferred_chroma_algorithm = false;
-
-  options.prefer_uncC_short_form = true;
-}
-
-static void copy_options(heif_encoding_options& options, const heif_encoding_options& input_options)
-{
-  switch (input_options.version) {
-    case 7:
-      options.prefer_uncC_short_form = input_options.prefer_uncC_short_form;
-      // fallthrough
-    case 6:
-      options.color_conversion_options = input_options.color_conversion_options;
-      // fallthrough
-    case 5:
-      options.image_orientation = input_options.image_orientation;
-      // fallthrough
-    case 4:
-      options.output_nclx_profile = input_options.output_nclx_profile;
-      options.macOS_compatibility_workaround_no_nclx_profile = input_options.macOS_compatibility_workaround_no_nclx_profile;
-      // fallthrough
-    case 3:
-      options.save_two_colr_boxes_when_ICC_and_nclx_available = input_options.save_two_colr_boxes_when_ICC_and_nclx_available;
-      // fallthrough
-    case 2:
-      options.macOS_compatibility_workaround = input_options.macOS_compatibility_workaround;
-      // fallthrough
-    case 1:
-      options.save_alpha_channel = input_options.save_alpha_channel;
-  }
-}
-
-
-heif_encoding_options* heif_encoding_options_alloc()
-{
-  auto options = new heif_encoding_options;
-
-  set_default_encoding_options(*options);
-
-  return options;
-}
-
-
-void heif_encoding_options_free(heif_encoding_options* options)
-{
-  delete options;
-}
-
-struct heif_error heif_context_encode_image(struct heif_context* ctx,
-                                            const struct heif_image* input_image,
-                                            struct heif_encoder* encoder,
-                                            const struct heif_encoding_options* input_options,
-                                            struct heif_image_handle** out_image_handle)
-{
-  if (!encoder) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
-  }
-
-  if (out_image_handle) {
-    *out_image_handle = nullptr;
-  }
-
-  heif_encoding_options options;
-  heif_color_profile_nclx nclx;
-  set_default_encoding_options(options);
-  if (input_options) {
-    copy_options(options, *input_options);
-
-    if (options.output_nclx_profile == nullptr) {
-      auto input_nclx = input_image->image->get_color_profile_nclx();
-      if (input_nclx) {
-        options.output_nclx_profile = &nclx;
-        nclx.version = 1;
-        nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries();
-        nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics();
-        nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients();
-        nclx.full_range_flag = input_nclx->get_full_range_flag();
-      }
-    }
-  }
-
-  auto encodingResult = ctx->context->encode_image(input_image->image,
-                                     encoder,
-                                     options,
-                                     heif_image_input_class_normal);
-  if (encodingResult.error != Error::Ok) {
-    return encodingResult.error.error_struct(ctx->context.get());
-  }
-
-  std::shared_ptr<ImageItem> image = *encodingResult;
-
-  // mark the new image as primary image
-
-  if (ctx->context->is_primary_image_set() == false) {
-    ctx->context->set_primary_image(image);
-  }
-
-  if (out_image_handle) {
-    *out_image_handle = new heif_image_handle;
-    (*out_image_handle)->image = std::move(image);
-    (*out_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_encode_grid(struct heif_context* ctx,
-                                           struct heif_image** tiles,
-                                           uint16_t columns,
-                                           uint16_t rows,
-                                           struct heif_encoder* encoder,
-                                           const struct heif_encoding_options* input_options,
-                                           struct heif_image_handle** out_image_handle)
-{
-  if (!encoder || !tiles) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
-  }
-  else if (rows == 0 || columns == 0) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
-  }
-
-  // TODO: Don't repeat this code from heif_context_encode_image()
-  heif_encoding_options options;
-  heif_color_profile_nclx nclx;
-  set_default_encoding_options(options);
-  if (input_options) {
-    copy_options(options, *input_options);
-
-    if (options.output_nclx_profile == nullptr) {
-      auto input_nclx = tiles[0]->image->get_color_profile_nclx();
-      if (input_nclx) {
-        options.output_nclx_profile = &nclx;
-        nclx.version = 1;
-        nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries();
-        nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics();
-        nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients();
-        nclx.full_range_flag = input_nclx->get_full_range_flag();
-      }
-    }
-  }
-
-  // Convert heif_images to a vector of HeifPixelImages
-  std::vector<std::shared_ptr<HeifPixelImage>> pixel_tiles;
-  for (int i=0; i<rows*columns; i++) {
-    pixel_tiles.push_back(tiles[i]->image);
-  }
-
-  // Encode Grid
-  std::shared_ptr<ImageItem> out_grid;
-  auto addGridResult = ImageItem_Grid::add_and_encode_full_grid(ctx->context.get(),
-                                                                pixel_tiles,
-                                                                rows, columns,
-                                                                encoder,
-                                                                options);
-  if (addGridResult.error) {
-    return addGridResult.error.error_struct(ctx->context.get());
-  }
-
-  out_grid = addGridResult.value;
-
-  // Mark as primary image
-  if (ctx->context->is_primary_image_set() == false) {
-    ctx->context->set_primary_image(out_grid);
-  }
-
-  if (out_image_handle) {
-    *out_image_handle = new heif_image_handle;
-    (*out_image_handle)->image = std::move(out_grid);
-    (*out_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_add_grid_image(struct heif_context* ctx,
-                                              uint32_t image_width,
-                                              uint32_t image_height,
-                                              uint32_t tile_columns,
-                                              uint32_t tile_rows,
-                                              const struct heif_encoding_options* encoding_options,
-                                              struct heif_image_handle** out_grid_image_handle)
-{
-  if (tile_rows == 0 || tile_columns == 0) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
-  }
-  else if (tile_rows > 0xFFFF || tile_columns > 0xFFFF) {
-    return heif_error{heif_error_Usage_error,
-                      heif_suberror_Invalid_image_size,
-                      "Number of tile rows/columns may not exceed 65535"};
-  }
-
-  auto generateGridItemResult = ImageItem_Grid::add_new_grid_item(ctx->context.get(),
-                                                                  image_width,
-                                                                  image_height,
-                                                                  static_cast<uint16_t>(tile_rows),
-                                                                  static_cast<uint16_t>(tile_columns),
-                                                                  encoding_options);
-  if (generateGridItemResult.error) {
-    return generateGridItemResult.error.error_struct(ctx->context.get());
-  }
-
-  if (out_grid_image_handle) {
-    *out_grid_image_handle = new heif_image_handle;
-    (*out_grid_image_handle)->image = generateGridItemResult.value;
-    (*out_grid_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_add_overlay_image(struct heif_context* ctx,
-                                                 uint32_t image_width,
-                                                 uint32_t image_height,
-                                                 uint16_t nImages,
-                                                 const heif_item_id* image_ids,
-                                                 int32_t* offsets,
-                                                 const uint16_t background_rgba[4],
-                                                 struct heif_image_handle** out_iovl_image_handle)
-{
-  if (!image_ids) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
-  }
-  else if (nImages == 0) {
-    return Error(heif_error_Usage_error,
-                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
-  }
-
-
-  std::vector<heif_item_id> refs;
-  refs.insert(refs.end(), image_ids, image_ids + nImages);
-
-  ImageOverlay overlay;
-  overlay.set_canvas_size(image_width, image_height);
-
-  if (background_rgba) {
-    overlay.set_background_color(background_rgba);
-  }
-
-  for (uint16_t i=0;i<nImages;i++) {
-    overlay.add_image_on_top(image_ids[i],
-                             offsets ? offsets[2 * i] : 0,
-                             offsets ? offsets[2 * i + 1] : 0);
-  }
-
-  Result<std::shared_ptr<ImageItem_Overlay>> addImageResult = ImageItem_Overlay::add_new_overlay_item(ctx->context.get(), overlay);
-
-  if (addImageResult.error != Error::Ok) {
-    return addImageResult.error.error_struct(ctx->context.get());
-  }
-
-  std::shared_ptr<ImageItem> iovlimage = addImageResult.value;
-
-
-  if (out_iovl_image_handle) {
-    *out_iovl_image_handle = new heif_image_handle;
-    (*out_iovl_image_handle)->image = std::move(iovlimage);
-    (*out_iovl_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_add_tiled_image(struct heif_context* ctx,
-                                               const struct heif_tiled_image_parameters* parameters,
-                                               const struct heif_encoding_options* options, // TODO: do we need this?
-                                               const struct heif_encoder* encoder,
-                                               struct heif_image_handle** out_grid_image_handle)
-{
-  if (out_grid_image_handle) {
-    *out_grid_image_handle = nullptr;
-  }
-
-  Result<std::shared_ptr<ImageItem_Tiled>> gridImageResult;
-  gridImageResult = ImageItem_Tiled::add_new_tiled_item(ctx->context.get(), parameters, encoder);
-
-  if (gridImageResult.error != Error::Ok) {
-    return gridImageResult.error.error_struct(ctx->context.get());
-  }
-
-  if (out_grid_image_handle) {
-    *out_grid_image_handle = new heif_image_handle;
-    (*out_grid_image_handle)->image = gridImageResult.value;
-    (*out_grid_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_add_image_tile(struct heif_context* ctx,
-                                              struct heif_image_handle* tiled_image,
-                                              uint32_t tile_x, uint32_t tile_y,
-                                              const struct heif_image* image,
-                                              struct heif_encoder* encoder)
-{
-  if (auto tili_image = std::dynamic_pointer_cast<ImageItem_Tiled>(tiled_image->image)) {
-    Error err = tili_image->add_image_tile(tile_x, tile_y, image->image, encoder);
-    return err.error_struct(ctx->context.get());
-  }
-#if WITH_UNCOMPRESSED_CODEC
-  else if (auto unci = std::dynamic_pointer_cast<ImageItem_uncompressed>(tiled_image->image)) {
-    Error err = unci->add_image_tile(tile_x, tile_y, image->image);
-    return err.error_struct(ctx->context.get());
-  }
-#endif
-  else if (auto grid_item = std::dynamic_pointer_cast<ImageItem_Grid>(tiled_image->image)) {
-    Error err = grid_item->add_image_tile(tile_x, tile_y, image->image, encoder);
-    return err.error_struct(ctx->context.get());
-  }
-  else {
-    return {
-      heif_error_Usage_error,
-      heif_suberror_Unspecified,
-      "Cannot add tile to a non-tiled image"
-    };
-  }
-}
-
-
-struct heif_error heif_context_add_unci_image(struct heif_context* ctx,
-                                              const struct heif_unci_image_parameters* parameters,
-                                              const struct heif_encoding_options* encoding_options,
-                                              const heif_image* prototype,
-                                              struct heif_image_handle** out_unci_image_handle)
-{
-#if WITH_UNCOMPRESSED_CODEC
-  Result<std::shared_ptr<ImageItem_uncompressed>> unciImageResult;
-  unciImageResult = ImageItem_uncompressed::add_unci_item(ctx->context.get(), parameters, encoding_options, prototype->image);
-
-  if (unciImageResult.error != Error::Ok) {
-    return unciImageResult.error.error_struct(ctx->context.get());
-  }
-
-  if (out_unci_image_handle) {
-    *out_unci_image_handle = new heif_image_handle;
-    (*out_unci_image_handle)->image = unciImageResult.value;
-    (*out_unci_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-#else
-  return {heif_error_Unsupported_feature,
-          heif_suberror_Unspecified,
-          "support for uncompressed images (ISO23001-17) has been disabled."};
-#endif
-}
-
-
-
-struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx,
-                                                const struct heif_image_handle* master_image,
-                                                const struct heif_image_handle* thumbnail_image)
-{
-  Error error = ctx->context->assign_thumbnail(thumbnail_image->image, master_image->image);
-  return error.error_struct(ctx->context.get());
-}
-
-
-struct heif_error heif_context_encode_thumbnail(struct heif_context* ctx,
-                                                const struct heif_image* image,
-                                                const struct heif_image_handle* image_handle,
-                                                struct heif_encoder* encoder,
-                                                const struct heif_encoding_options* input_options,
-                                                int bbox_size,
-                                                struct heif_image_handle** out_image_handle)
-{
-  heif_encoding_options options;
-  set_default_encoding_options(options);
-
-  if (input_options != nullptr) {
-    copy_options(options, *input_options);
-  }
-
-  auto encodingResult = ctx->context->encode_thumbnail(image->image,
-                                               encoder,
-                                               options,
-                                               bbox_size);
-  if (encodingResult.error != Error::Ok) {
-    return encodingResult.error.error_struct(ctx->context.get());
-  }
-
-  std::shared_ptr<ImageItem> thumbnail_image = *encodingResult;
-
-  if (!thumbnail_image) {
-    Error err(heif_error_Usage_error,
-              heif_suberror_Invalid_parameter_value,
-              "Thumbnail images must be smaller than the original image.");
-    return err.error_struct(ctx->context.get());
-  }
-
-  Error error = ctx->context->assign_thumbnail(image_handle->image, thumbnail_image);
-  if (error != Error::Ok) {
-    return error.error_struct(ctx->context.get());
-  }
-
-
-  if (out_image_handle) {
-    *out_image_handle = new heif_image_handle;
-    (*out_image_handle)->image = thumbnail_image;
-    (*out_image_handle)->context = ctx->context;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_set_primary_image(struct heif_context* ctx,
-                                                 struct heif_image_handle* image_handle)
-{
-  ctx->context->set_primary_image(image_handle->image);
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_context_add_exif_metadata(struct heif_context* ctx,
-                                                 const struct heif_image_handle* image_handle,
-                                                 const void* data, int size)
-{
-  Error error = ctx->context->add_exif_metadata(image_handle->image, data, size);
-  if (error != Error::Ok) {
-    return error.error_struct(ctx->context.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-struct heif_error heif_context_add_XMP_metadata(struct heif_context* ctx,
-                                                const struct heif_image_handle* image_handle,
-                                                const void* data, int size)
-{
-  return heif_context_add_XMP_metadata2(ctx, image_handle, data, size,
-                                        heif_metadata_compression_off);
-}
-
-
-struct heif_error heif_context_add_XMP_metadata2(struct heif_context* ctx,
-                                                 const struct heif_image_handle* image_handle,
-                                                 const void* data, int size,
-                                                 heif_metadata_compression compression)
-{
-  Error error = ctx->context->add_XMP_metadata(image_handle->image, data, size, compression);
-  if (error != Error::Ok) {
-    return error.error_struct(ctx->context.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx,
-                                                    const struct heif_image_handle* image_handle,
-                                                    const void* data, int size,
-                                                    const char* item_type, const char* content_type)
-{
-  if (item_type == nullptr || strlen(item_type) != 4) {
-    return {heif_error_Usage_error,
-            heif_suberror_Invalid_parameter_value,
-            "called heif_context_add_generic_metadata() with invalid 'item_type'."};
-  }
-
-  Error error = ctx->context->add_generic_metadata(image_handle->image, data, size,
-                                                   fourcc(item_type), content_type, nullptr, heif_metadata_compression_off, nullptr);
-  if (error != Error::Ok) {
-    return error.error_struct(ctx->context.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx,
-                                                        const struct heif_image_handle* image_handle,
-                                                        const void* data, int size,
-                                                        const char* item_uri_type,
-                                                        heif_item_id* out_item_id)
-{
-  Error error = ctx->context->add_generic_metadata(image_handle->image, data, size,
-                                                   fourcc("uri "), nullptr, item_uri_type, heif_metadata_compression_off, out_item_id);
-  if (error != Error::Ok) {
-    return error.error_struct(ctx->context.get());
-  }
-  else {
-    return heif_error_success;
-  }
-}
-
-
-void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width)
-{
-  ctx->context->get_security_limits()->max_image_size_pixels = static_cast<uint64_t>(maximum_width) * maximum_width;
-}
-
-
-void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads)
-{
-  ctx->context->set_max_decoding_threads(max_threads);
-}
diff -pruN 1.19.8-1/libheif/api/libheif/heif.h 1.20.1-1/libheif/api/libheif/heif.h
--- 1.19.8-1/libheif/api/libheif/heif.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif.h	2025-07-02 13:05:31.000000000 +0000
@@ -1,6 +1,6 @@
 /*
  * HEIF codec.
- * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
  *
  * This file is part of libheif.
  *
@@ -21,2608 +21,21 @@
 #ifndef LIBHEIF_HEIF_H
 #define LIBHEIF_HEIF_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*! \file heif.h
- *
- * Public API for libheif.
-*/
-
-#include <stddef.h>
-#include <stdint.h>
-
+#include <libheif/heif_library.h>
 #include <libheif/heif_version.h>
-
-
-// API versions table
-//
-// release    dec.options   enc.options   heif_reader   heif_writer   depth.rep   col.profile
-// ------------------------------------------------------------------------------------------
-//  1.0            1           N/A           N/A           N/A           1           N/A
-//  1.1            1           N/A           N/A            1            1           N/A
-//  1.3            1            1             1             1            1           N/A
-//  1.4            1            1             1             1            1            1
-//  1.7            2            1             1             1            1            1
-//  1.9.2          2            2             1             1            1            1
-//  1.10           2            3             1             1            1            1
-//  1.11           2            4             1             1            1            1
-//  1.13           3            4             1             1            1            1
-//  1.14           3            5             1             1            1            1
-//  1.15           4            5             1             1            1            1
-//  1.16           5            6             1             1            1            1
-//  1.18           5            7             1             1            1            1
-//  1.19           6            7             2             1            1            1
-
-#if (defined(_WIN32) || defined __CYGWIN__) && !defined(LIBHEIF_STATIC_BUILD)
-#ifdef LIBHEIF_EXPORTS
-#define LIBHEIF_API __declspec(dllexport)
-#else
-#define LIBHEIF_API __declspec(dllimport)
-#endif
-#elif defined(HAVE_VISIBILITY) && HAVE_VISIBILITY
-#ifdef LIBHEIF_EXPORTS
-#define LIBHEIF_API __attribute__((__visibility__("default")))
-#else
-#define LIBHEIF_API
-#endif
-#else
-#define LIBHEIF_API
-#endif
-
-#define heif_fourcc(a, b, c, d) ((uint32_t)((a<<24) | (b<<16) | (c<<8) | d))
-
-
-/* === version numbers === */
-
-// Version string of linked libheif library.
-LIBHEIF_API const char* heif_get_version(void);
-
-// Numeric version of linked libheif library, encoded as 0xHHMMLL00 = hh.mm.ll, where hh, mm, ll is the decimal representation of HH, MM, LL.
-// For example: 0x02150300 is version 2.21.3
-LIBHEIF_API uint32_t heif_get_version_number(void);
-
-// Numeric part "HH" from above. Returned as a decimal number.
-LIBHEIF_API int heif_get_version_number_major(void);
-// Numeric part "MM" from above. Returned as a decimal number.
-LIBHEIF_API int heif_get_version_number_minor(void);
-// Numeric part "LL" from above. Returned as a decimal number.
-LIBHEIF_API int heif_get_version_number_maintenance(void);
-
-// Helper macros to check for given versions of libheif at compile time.
-#define LIBHEIF_MAKE_VERSION(h, m, l) ((h) << 24 | (m) << 16 | (l) << 8)
-#define LIBHEIF_HAVE_VERSION(h, m, l) (LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(h, m, l))
-
-struct heif_context;
-struct heif_image_handle;
-struct heif_image;
-
-
-enum heif_error_code
-{
-  // Everything ok, no error occurred.
-  heif_error_Ok = 0,
-
-  // Input file does not exist.
-  heif_error_Input_does_not_exist = 1,
-
-  // Error in input file. Corrupted or invalid content.
-  heif_error_Invalid_input = 2,
-
-  // Input file type is not supported.
-  heif_error_Unsupported_filetype = 3,
-
-  // Image requires an unsupported decoder feature.
-  heif_error_Unsupported_feature = 4,
-
-  // Library API has been used in an invalid way.
-  heif_error_Usage_error = 5,
-
-  // Could not allocate enough memory.
-  heif_error_Memory_allocation_error = 6,
-
-  // The decoder plugin generated an error
-  heif_error_Decoder_plugin_error = 7,
-
-  // The encoder plugin generated an error
-  heif_error_Encoder_plugin_error = 8,
-
-  // Error during encoding or when writing to the output
-  heif_error_Encoding_error = 9,
-
-  // Application has asked for a color profile type that does not exist
-  heif_error_Color_profile_does_not_exist = 10,
-
-  // Error loading a dynamic plugin
-  heif_error_Plugin_loading_error = 11,
-
-  // Operation has been canceled
-  heif_error_Canceled = 12
-};
-
-
-enum heif_suberror_code
-{
-  // no further information available
-  heif_suberror_Unspecified = 0,
-
-  // --- Invalid_input ---
-
-  // End of data reached unexpectedly.
-  heif_suberror_End_of_data = 100,
-
-  // Size of box (defined in header) is wrong
-  heif_suberror_Invalid_box_size = 101,
-
-  // Mandatory 'ftyp' box is missing
-  heif_suberror_No_ftyp_box = 102,
-
-  heif_suberror_No_idat_box = 103,
-
-  heif_suberror_No_meta_box = 104,
-
-  heif_suberror_No_hdlr_box = 105,
-
-  heif_suberror_No_hvcC_box = 106,
-
-  heif_suberror_No_pitm_box = 107,
-
-  heif_suberror_No_ipco_box = 108,
-
-  heif_suberror_No_ipma_box = 109,
-
-  heif_suberror_No_iloc_box = 110,
-
-  heif_suberror_No_iinf_box = 111,
-
-  heif_suberror_No_iprp_box = 112,
-
-  heif_suberror_No_iref_box = 113,
-
-  heif_suberror_No_pict_handler = 114,
-
-  // An item property referenced in the 'ipma' box is not existing in the 'ipco' container.
-  heif_suberror_Ipma_box_references_nonexisting_property = 115,
-
-  // No properties have been assigned to an item.
-  heif_suberror_No_properties_assigned_to_item = 116,
-
-  // Image has no (compressed) data
-  heif_suberror_No_item_data = 117,
-
-  // Invalid specification of image grid (tiled image)
-  heif_suberror_Invalid_grid_data = 118,
-
-  // Tile-images in a grid image are missing
-  heif_suberror_Missing_grid_images = 119,
-
-  heif_suberror_Invalid_clean_aperture = 120,
-
-  // Invalid specification of overlay image
-  heif_suberror_Invalid_overlay_data = 121,
-
-  // Overlay image completely outside of visible canvas area
-  heif_suberror_Overlay_image_outside_of_canvas = 122,
-
-  heif_suberror_Auxiliary_image_type_unspecified = 123,
-
-  heif_suberror_No_or_invalid_primary_item = 124,
-
-  heif_suberror_No_infe_box = 125,
-
-  heif_suberror_Unknown_color_profile_type = 126,
-
-  heif_suberror_Wrong_tile_image_chroma_format = 127,
-
-  heif_suberror_Invalid_fractional_number = 128,
-
-  heif_suberror_Invalid_image_size = 129,
-
-  heif_suberror_Invalid_pixi_box = 130,
-
-  heif_suberror_No_av1C_box = 131,
-
-  heif_suberror_Wrong_tile_image_pixel_depth = 132,
-
-  heif_suberror_Unknown_NCLX_color_primaries = 133,
-
-  heif_suberror_Unknown_NCLX_transfer_characteristics = 134,
-
-  heif_suberror_Unknown_NCLX_matrix_coefficients = 135,
-
-  // Invalid specification of region item
-  heif_suberror_Invalid_region_data = 136,
-
-  // Image has no ispe property
-  heif_suberror_No_ispe_property = 137,
-
-  heif_suberror_Camera_intrinsic_matrix_undefined = 138,
-
-  heif_suberror_Camera_extrinsic_matrix_undefined = 139,
-
-  // Invalid JPEG 2000 codestream - usually a missing marker
-  heif_suberror_Invalid_J2K_codestream = 140,
-
-  heif_suberror_No_vvcC_box = 141,
-
-  // icbr is only needed in some situations, this error is for those cases
-  heif_suberror_No_icbr_box = 142,
-
-  heif_suberror_No_avcC_box = 143,
-
-  // we got a mini box, but could not read it properly
-  heif_suberror_Invalid_mini_box = 149,
-
-  // Decompressing generic compression or header compression data failed (e.g. bitstream corruption)
-  heif_suberror_Decompression_invalid_data = 150,
-
-  // --- Memory_allocation_error ---
-
-  // A security limit preventing unreasonable memory allocations was exceeded by the input file.
-  // Please check whether the file is valid. If it is, contact us so that we could increase the
-  // security limits further.
-  heif_suberror_Security_limit_exceeded = 1000,
-
-  // There was an error from the underlying compression / decompression library.
-  // One possibility is lack of resources (e.g. memory).
-  heif_suberror_Compression_initialisation_error = 1001,
-
-  // --- Usage_error ---
-
-  // An item ID was used that is not present in the file.
-  heif_suberror_Nonexisting_item_referenced = 2000, // also used for Invalid_input
-
-  // An API argument was given a NULL pointer, which is not allowed for that function.
-  heif_suberror_Null_pointer_argument = 2001,
-
-  // Image channel referenced that does not exist in the image
-  heif_suberror_Nonexisting_image_channel_referenced = 2002,
-
-  // The version of the passed plugin is not supported.
-  heif_suberror_Unsupported_plugin_version = 2003,
-
-  // The version of the passed writer is not supported.
-  heif_suberror_Unsupported_writer_version = 2004,
-
-  // The given (encoder) parameter name does not exist.
-  heif_suberror_Unsupported_parameter = 2005,
-
-  // The value for the given parameter is not in the valid range.
-  heif_suberror_Invalid_parameter_value = 2006,
-
-  // Error in property specification
-  heif_suberror_Invalid_property = 2007,
-
-  // Image reference cycle found in iref
-  heif_suberror_Item_reference_cycle = 2008,
-
-
-  // --- Unsupported_feature ---
-
-  // Image was coded with an unsupported compression method.
-  heif_suberror_Unsupported_codec = 3000,
-
-  // Image is specified in an unknown way, e.g. as tiled grid image (which is supported)
-  heif_suberror_Unsupported_image_type = 3001,
-
-  heif_suberror_Unsupported_data_version = 3002,
-
-  // The conversion of the source image to the requested chroma / colorspace is not supported.
-  heif_suberror_Unsupported_color_conversion = 3003,
-
-  heif_suberror_Unsupported_item_construction_method = 3004,
-
-  heif_suberror_Unsupported_header_compression_method = 3005,
-
-  // Generically compressed data used an unsupported compression method
-  heif_suberror_Unsupported_generic_compression_method = 3006,
-
-  heif_suberror_Unsupported_essential_property = 3007,
-
-  // --- Encoder_plugin_error ---
-
-  heif_suberror_Unsupported_bit_depth = 4000,
-
-
-  // --- Encoding_error ---
-
-  heif_suberror_Cannot_write_output_data = 5000,
-
-  heif_suberror_Encoder_initialization = 5001,
-  heif_suberror_Encoder_encoding = 5002,
-  heif_suberror_Encoder_cleanup = 5003,
-
-  heif_suberror_Too_many_regions = 5004,
-
-
-  // --- Plugin loading error ---
-
-  heif_suberror_Plugin_loading_error = 6000,         // a specific plugin file cannot be loaded
-  heif_suberror_Plugin_is_not_loaded = 6001,         // trying to remove a plugin that is not loaded
-  heif_suberror_Cannot_read_plugin_directory = 6002, // error while scanning the directory for plugins
-  heif_suberror_No_matching_decoder_installed = 6003 // no decoder found for that compression format
-};
-
-
-struct heif_error
-{
-  // main error category
-  enum heif_error_code code;
-
-  // more detailed error code
-  enum heif_suberror_code subcode;
-
-  // textual error message (is always defined, you do not have to check for NULL)
-  const char* message;
-};
-
-// Default success return value. Intended for use in user-supplied callback functions.
-LIBHEIF_API extern const struct heif_error heif_error_success;
-
-
-typedef uint32_t heif_item_id;
-typedef uint32_t heif_property_id;
-
-
-
-// ========================= enum types ======================
-
-/**
- * libheif known compression formats.
- */
-enum heif_compression_format
-{
-  /**
-   * Unspecified / undefined compression format.
-   *
-   * This is used to mean "no match" or "any decoder" for some parts of the
-   * API. It does not indicate a specific compression format.
-   */
-  heif_compression_undefined = 0,
-  /**
-   * HEVC compression, used for HEIC images.
-   *
-   * This is equivalent to H.265.
-  */
-  heif_compression_HEVC = 1,
-  /**
-   * AVC compression. (Currently unused in libheif.)
-   *
-   * The compression is defined in ISO/IEC 14496-10. This is equivalent to H.264.
-   *
-   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex E.
-   */
-  heif_compression_AVC = 2,
-  /**
-   * JPEG compression.
-   *
-   * The compression format is defined in ISO/IEC 10918-1. The encapsulation
-   * of JPEG is specified in ISO/IEC 23008-12:2022 Annex H.
-  */
-  heif_compression_JPEG = 3,
-  /**
-   * AV1 compression, used for AVIF images.
-   *
-   * The compression format is provided at https://aomediacodec.github.io/av1-spec/
-   *
-   * The encapsulation is defined in https://aomediacodec.github.io/av1-avif/
-   */
-  heif_compression_AV1 = 4,
-  /**
-   * VVC compression.
-   *
-   * The compression format is defined in ISO/IEC 23090-3. This is equivalent to H.266.
-   *
-   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex L.
-   */
-  heif_compression_VVC = 5,
-  /**
-   * EVC compression. (Currently unused in libheif.)
-   *
-   * The compression format is defined in ISO/IEC 23094-1.
-   *
-   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex M.
-   */
-  heif_compression_EVC = 6,
-  /**
-   * JPEG 2000 compression.
-   *
-   * The encapsulation of JPEG 2000 is specified in ISO/IEC 15444-16:2021.
-   * The core encoding is defined in ISO/IEC 15444-1, or ITU-T T.800.
-  */
-  heif_compression_JPEG2000 = 7,
-  /**
-   * Uncompressed encoding.
-   *
-   * This is defined in ISO/IEC 23001-17:2024.
-  */
-  heif_compression_uncompressed = 8,
-  /**
-   * Mask image encoding.
-   *
-   * See ISO/IEC 23008-12:2022 Section 6.10.2
-   */
-  heif_compression_mask = 9,
-  /**
-   * High Throughput JPEG 2000 (HT-J2K) compression.
-   *
-   * The encapsulation of HT-J2K is specified in ISO/IEC 15444-16:2021.
-   * The core encoding is defined in ISO/IEC 15444-15, or ITU-T T.814.
-  */
-  heif_compression_HTJ2K = 10
-};
-
-enum heif_chroma
-{
-  heif_chroma_undefined = 99,
-  heif_chroma_monochrome = 0,
-  heif_chroma_420 = 1,
-  heif_chroma_422 = 2,
-  heif_chroma_444 = 3,
-  heif_chroma_interleaved_RGB = 10,
-  heif_chroma_interleaved_RGBA = 11,
-  heif_chroma_interleaved_RRGGBB_BE = 12,   // HDR, big endian.
-  heif_chroma_interleaved_RRGGBBAA_BE = 13, // HDR, big endian.
-  heif_chroma_interleaved_RRGGBB_LE = 14,   // HDR, little endian.
-  heif_chroma_interleaved_RRGGBBAA_LE = 15  // HDR, little endian.
-};
-
-// DEPRECATED ENUM NAMES
-#define heif_chroma_interleaved_24bit  heif_chroma_interleaved_RGB
-#define heif_chroma_interleaved_32bit  heif_chroma_interleaved_RGBA
-
-
-enum heif_colorspace
-{
-  heif_colorspace_undefined = 99,
-
-  // heif_colorspace_YCbCr should be used with one of these heif_chroma values:
-  // * heif_chroma_444
-  // * heif_chroma_422
-  // * heif_chroma_420
-  heif_colorspace_YCbCr = 0,
-
-  // heif_colorspace_RGB should be used with one of these heif_chroma values:
-  // * heif_chroma_444 (for planar RGB)
-  // * heif_chroma_interleaved_RGB
-  // * heif_chroma_interleaved_RGBA
-  // * heif_chroma_interleaved_RRGGBB_BE
-  // * heif_chroma_interleaved_RRGGBBAA_BE
-  // * heif_chroma_interleaved_RRGGBB_LE
-  // * heif_chroma_interleaved_RRGGBBAA_LE
-  heif_colorspace_RGB = 1,
-
-  // heif_colorspace_monochrome should only be used with heif_chroma = heif_chroma_monochrome
-  heif_colorspace_monochrome = 2,
-
-  // Indicates that this image has no visual channels.
-  heif_colorspace_nonvisual = 3
-};
-
-enum heif_channel
-{
-  heif_channel_Y = 0,
-  heif_channel_Cb = 1,
-  heif_channel_Cr = 2,
-  heif_channel_R = 3,
-  heif_channel_G = 4,
-  heif_channel_B = 5,
-  heif_channel_Alpha = 6,
-  heif_channel_interleaved = 10,
-  heif_channel_filter_array = 11,
-  heif_channel_depth = 12,
-  heif_channel_disparity = 13
-};
-
-enum heif_metadata_compression
-{
-  heif_metadata_compression_off = 0,
-  heif_metadata_compression_auto = 1,
-  heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file
-  heif_metadata_compression_deflate = 3,
-  heif_metadata_compression_zlib = 4,    // do not use for header data
-  heif_metadata_compression_brotli = 5
-};
-
-// ========================= library initialization ======================
-
-struct heif_init_params
-{
-  int version;
-
-  // currently no parameters
-};
-
-
-/**
- * Initialise library.
- *
- * You should call heif_init() when you start using libheif and heif_deinit() when you are finished.
- * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit().
- *
- * For backwards compatibility, it is not really necessary to call heif_init(), but some library memory objects
- * will never be freed if you do not call heif_init()/heif_deinit().
- *
- * heif_init() will load the external modules installed in the default plugin path. Thus, you need it when you
- * want to load external plugins from the default path.
- * Codec plugins that are compiled into the library directly (selected by the compile-time parameters of libheif)
- * will be available even without heif_init().
- *
- * Make sure that you do not have one part of your program use heif_init()/heif_deinit() and another part that does
- * not use it as the latter may try to use an uninitialized library. If in doubt, enclose everything with init/deinit.
- *
- * You may pass nullptr to get default parameters. Currently, no parameters are supported.
- */
-LIBHEIF_API
-struct heif_error heif_init(struct heif_init_params*);
-
-/**
- * Deinitialise and clean up library.
- *
- * You should call heif_init() when you start using libheif and heif_deinit() when you are finished.
- * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit().
- *
- * Note: heif_deinit() must not be called after exit(), for example in a global C++ object's destructor.
- * If you do, global variables in libheif might have already been released when heif_deinit() is running,
- * leading to a crash.
- *
- * \sa heif_init()
- */
-LIBHEIF_API
-void heif_deinit(void);
-
-
-// --- Plugins are currently only supported on Unix platforms.
-
-enum heif_plugin_type
-{
-  heif_plugin_type_encoder,
-  heif_plugin_type_decoder
-};
-
-struct heif_plugin_info
-{
-  int version; // version of this info struct
-  enum heif_plugin_type type;
-  const void* plugin;
-  void* internal_handle; // for internal use only
-};
-
-LIBHEIF_API
-struct heif_error heif_load_plugin(const char* filename, struct heif_plugin_info const** out_plugin);
-
-LIBHEIF_API
-struct heif_error heif_load_plugins(const char* directory,
-                                    const struct heif_plugin_info** out_plugins,
-                                    int* out_nPluginsLoaded,
-                                    int output_array_size);
-
-LIBHEIF_API
-struct heif_error heif_unload_plugin(const struct heif_plugin_info* plugin);
-
-// Get a NULL terminated array of the plugin directories that are searched by libheif.
-// This includes the paths specified in the environment variable LIBHEIF_PLUGIN_PATHS and the built-in path
-// (if not overridden by the environment variable).
-LIBHEIF_API
-const char*const* heif_get_plugin_directories(void);
-
-LIBHEIF_API
-void heif_free_plugin_directories(const char*const*);
-
-
-// ========================= file type check ======================
-
-enum heif_filetype_result
-{
-  heif_filetype_no,
-  heif_filetype_yes_supported,   // it is heif and can be read by libheif
-  heif_filetype_yes_unsupported, // it is heif, but cannot be read by libheif
-  heif_filetype_maybe // not sure whether it is an heif, try detection with more input data
-};
-
-// input data should be at least 12 bytes
-LIBHEIF_API
-enum heif_filetype_result heif_check_filetype(const uint8_t* data, int len);
-
-/**
- * Check the filetype box content for a supported file type.
- *
- * <p>The data is assumed to start from the start of the `ftyp` box.
- *
- * <p>This function checks the compatible brands.
- * 
- * @returns heif_error_ok if a supported brand is found, or other error if not.
- */
-LIBHEIF_API
-struct heif_error heif_has_compatible_filetype(const uint8_t* data, int len);
-
-LIBHEIF_API
-int heif_check_jpeg_filetype(const uint8_t* data, int len);
-
-
-// DEPRECATED, use heif_brand2 and the heif_brand2_* constants below instead
-enum heif_brand
-{
-  heif_unknown_brand,
-  heif_heic, // HEIF image with h265
-  heif_heix, // 10bit images, or anything that uses h265 with range extension
-  heif_hevc, heif_hevx, // brands for image sequences
-  heif_heim, // multiview
-  heif_heis, // scalable
-  heif_hevm, // multiview sequence
-  heif_hevs, // scalable sequence
-  heif_mif1, // image, any coding algorithm
-  heif_msf1, // sequence, any coding algorithm
-  heif_avif, // HEIF image with AV1
-  heif_avis,
-  heif_vvic, // VVC image
-  heif_vvis, // VVC sequence
-  heif_evbi, // EVC image
-  heif_evbs, // EVC sequence
-  heif_j2ki, // JPEG2000 image
-  heif_j2is, // JPEG2000 image sequence
-};
-
-// input data should be at least 12 bytes
-// DEPRECATED, use heif_read_main_brand() instead
-LIBHEIF_API
-enum heif_brand heif_main_brand(const uint8_t* data, int len);
-
-
-typedef uint32_t heif_brand2;
-
-/**
- * HEVC image (`heic`) brand.
- *
- * Image conforms to HEVC (H.265) Main or Main Still profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.1.
- */
-#define heif_brand2_heic   heif_fourcc('h','e','i','c')
-
-/**
- * HEVC image (`heix`) brand.
- *
- * Image conforms to HEVC (H.265) Main 10 profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.1.
- */
-#define heif_brand2_heix   heif_fourcc('h','e','i','x')
-
-/**
- * HEVC image sequence (`hevc`) brand.
- *
- * Image sequence conforms to HEVC (H.265) Main profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.2.
- */
-#define heif_brand2_hevc   heif_fourcc('h','e','v','c')
-
-/**
- * HEVC image sequence (`hevx`) brand.
- *
- * Image sequence conforms to HEVC (H.265) Main 10 profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.2.
- */
-#define heif_brand2_hevx   heif_fourcc('h','e','v','x')
-
-/**
- * HEVC layered image (`heim`) brand.
- *
- * Image layers conform to HEVC (H.265) Main or Multiview Main profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.3.
- */
-#define heif_brand2_heim   heif_fourcc('h','e','i','m')
-
-/**
- * HEVC layered image (`heis`) brand.
- *
- * Image layers conform to HEVC (H.265) Main, Main 10, Scalable Main
- * or Scalable Main 10 profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.3.
- */
-#define heif_brand2_heis   heif_fourcc('h','e','i','s')
-
-/**
- * HEVC layered image sequence (`hevm`) brand.
- *
- * Image sequence layers conform to HEVC (H.265) Main or Multiview Main profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.4.
- */
-#define heif_brand2_hevm   heif_fourcc('h','e','v','m')
-
-/**
- * HEVC layered image sequence (`hevs`) brand.
- *
- * Image sequence layers conform to HEVC (H.265) Main, Main 10, Scalable Main
- * or Scalable Main 10 profile.
- *
- * See ISO/IEC 23008-12:2022 Section B.4.4.
- */
-#define heif_brand2_hevs   heif_fourcc('h','e','v','s')
-
-/**
- * AV1 image (`avif`) brand.
- *
- * See https://aomediacodec.github.io/av1-avif/#image-and-image-collection-brand
- */
-#define heif_brand2_avif   heif_fourcc('a','v','i','f')
-
-/**
- * AV1 image sequence (`avis`) brand.
- *
- * See https://aomediacodec.github.io/av1-avif/#image-sequence-brand
- */
-#define heif_brand2_avis   heif_fourcc('a','v','i','s') // AVIF sequence
-
-/**
- * HEIF image structural brand (`mif1`).
- *
- * This does not imply a specific coding algorithm.
- *
- * See ISO/IEC 23008-12:2022 Section 10.2.2.
- */
-#define heif_brand2_mif1   heif_fourcc('m','i','f','1')
-
-/**
- * HEIF image structural brand (`mif2`).
- *
- * This does not imply a specific coding algorithm. `mif2` extends
- * the requirements of `mif1` to include the `rref` and `iscl` item
- * properties.
- *
- * See ISO/IEC 23008-12:2022 Section 10.2.3.
- */
-#define heif_brand2_mif2   heif_fourcc('m','i','f','2')
-
-/**
- * HEIF image structural brand (`mif3`).
- *
- * This indicates the low-overhead (ftyp+mini) structure.
- */
-#define heif_brand2_mif3   heif_fourcc('m','i','f','3')
-
-/**
- * HEIF image sequence structural brand (`msf1`).
- *
- * This does not imply a specific coding algorithm.
- *
- * See ISO/IEC 23008-12:2022 Section 10.3.1.
- */
-#define heif_brand2_msf1   heif_fourcc('m','s','f','1')
-
-/**
- * VVC image (`vvic`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section L.4.1.
- */
-#define heif_brand2_vvic   heif_fourcc('v','v','i','c')
-
-/**
- * VVC image sequence (`vvis`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section L.4.2.
- */
-#define heif_brand2_vvis   heif_fourcc('v','v','i','s')
-
-/**
- * EVC baseline image (`evbi`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section M.4.1.
- */
-#define heif_brand2_evbi   heif_fourcc('e','v','b','i')
-
-/**
- * EVC main profile image (`evmi`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section M.4.2.
- */
-#define heif_brand2_evmi   heif_fourcc('e','v','m','i')
-
-/**
- * EVC baseline image sequence (`evbs`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section M.4.3.
- */
-#define heif_brand2_evbs   heif_fourcc('e','v','b','s')
-
-/**
- * EVC main profile image sequence (`evms`) brand.
- *
- * See ISO/IEC 23008-12:2022 Section M.4.4.
- */
-#define heif_brand2_evms   heif_fourcc('e','v','m','s')
-
-/**
- * JPEG image (`jpeg`) brand.
- *
- * See ISO/IEC 23008-12:2022 Annex H.4
- */
-#define heif_brand2_jpeg   heif_fourcc('j','p','e','g')
-
-/**
- * JPEG image sequence (`jpgs`) brand.
- *
- * See ISO/IEC 23008-12:2022 Annex H.5
- */
-#define heif_brand2_jpgs   heif_fourcc('j','p','g','s')
-
-/**
- * JPEG 2000 image (`j2ki`) brand.
- *
- * See ISO/IEC 15444-16:2021 Section 6.5 
- */
-#define heif_brand2_j2ki   heif_fourcc('j','2','k','i')
-
-/**
- * JPEG 2000 image sequence (`j2is`) brand.
- *
- * See ISO/IEC 15444-16:2021 Section 7.6
- */
-#define heif_brand2_j2is   heif_fourcc('j','2','i','s')
-
-/**
- * Multi-image application format (MIAF) brand.
- *
- * This is HEIF with additional constraints for interoperability.
- *
- * See ISO/IEC 23000-22.
- */
-#define heif_brand2_miaf   heif_fourcc('m','i','a','f')
-
-/**
- * Single picture file brand.
- *
- * This is a compatible brand indicating the file contains a single intra-coded picture.
- *
- * See ISO/IEC 23008-12:2022 Section 10.2.5.
-*/
-#define heif_brand2_1pic   heif_fourcc('1','p','i','c')
-
-// input data should be at least 12 bytes
-LIBHEIF_API
-heif_brand2 heif_read_main_brand(const uint8_t* data, int len);
-
-// input data should be at least 16 bytes
-LIBHEIF_API
-heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len);
-
-// 'brand_fourcc' must be 4 character long, but need not be 0-terminated
-LIBHEIF_API
-heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc);
-
-// the output buffer must be at least 4 bytes long
-LIBHEIF_API
-void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc);
-
-// 'brand_fourcc' must be 4 character long, but need not be 0-terminated
-// returns 1 if file includes the brand, and 0 if it does not
-// returns -1 if the provided data is not sufficient
-//            (you should input at least as many bytes as indicated in the first 4 bytes of the file, usually ~50 bytes will do)
-// returns -2 on other errors
-LIBHEIF_API
-int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc);
-
-// Returns an array of compatible brands. The array is allocated by this function and has to be freed with 'heif_free_list_of_compatible_brands()'.
-// The number of entries is returned in out_size.
-LIBHEIF_API
-struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size);
-
-LIBHEIF_API
-void heif_free_list_of_compatible_brands(heif_brand2* brands_list);
-
-
-// Returns one of these MIME types:
-// - image/heic           HEIF file using h265 compression
-// - image/heif           HEIF file using any other compression
-// - image/heic-sequence  HEIF image sequence using h265 compression
-// - image/heif-sequence  HEIF image sequence using any other compression
-// - image/avif           AVIF image
-// - image/avif-sequence  AVIF sequence
-// - image/jpeg    JPEG image
-// - image/png     PNG image
-// If the format could not be detected, an empty string is returned.
-//
-// Provide at least 12 bytes of input. With less input, its format might not
-// be detected. You may also provide more input to increase detection accuracy.
-//
-// Note that JPEG and PNG images cannot be decoded by libheif even though the
-// formats are detected by this function.
-LIBHEIF_API
-const char* heif_get_file_mime_type(const uint8_t* data, int len);
-
-
-
-// ========================= heif_context =========================
-// A heif_context represents a HEIF file that has been read.
-// In the future, you will also be able to add pictures to a heif_context
-// and write it into a file again.
-
-
-// Allocate a new context for reading HEIF files.
-// Has to be freed again with heif_context_free().
-LIBHEIF_API
-struct heif_context* heif_context_alloc(void);
-
-// Free a previously allocated HEIF context. You should not free a context twice.
-LIBHEIF_API
-void heif_context_free(struct heif_context*);
-
-
-
-struct heif_reading_options;
-
-enum heif_reader_grow_status
-{
-  heif_reader_grow_status_size_reached,    // requested size has been reached, we can read until this point
-  heif_reader_grow_status_timeout,         // size has not been reached yet, but it may still grow further (deprecated)
-  heif_reader_grow_status_size_beyond_eof, // size has not been reached and never will. The file has grown to its full size
-  heif_reader_grow_status_error            // an error has occurred
-};
-
-struct heif_reader_range_request_result
-{
-  enum heif_reader_grow_status status; // should not return 'heif_reader_grow_status_timeout'
-
-  // Indicates up to what position the file has been read.
-  // If we cannot read the whole file range (status == 'heif_reader_grow_status_size_beyond_eof'), this is the actual end position.
-  // On the other hand, it may be that the reader was reading more data than requested. In that case, it should indicate the full size here
-  // and libheif may decide to make use of the additional data (e.g. for filling 'tili' offset tables).
-  uint64_t range_end;
-
-  // for status == 'heif_reader_grow_status_error'
-  int reader_error_code;        // a reader specific error code
-  const char* reader_error_msg; // libheif will call heif_reader.release_error_msg on this if it is not NULL
-};
-
-
-struct heif_reader
-{
-  // API version supported by this reader
-  int reader_api_version;
-
-  // --- version 1 functions ---
-  int64_t (* get_position)(void* userdata);
-
-  // The functions read(), and seek() return 0 on success.
-  // Generally, libheif will make sure that we do not read past the file size.
-  int (* read)(void* data,
-               size_t size,
-               void* userdata);
-
-  int (* seek)(int64_t position,
-               void* userdata);
-
-  // When calling this function, libheif wants to make sure that it can read the file
-  // up to 'target_size'. This is useful when the file is currently downloaded and may
-  // grow with time. You may, for example, extract the image sizes even before the actual
-  // compressed image data has been completely downloaded.
-  //
-  // Even if your input files will not grow, you will have to implement at least
-  // detection whether the target_size is above the (fixed) file length
-  // (in this case, return 'size_beyond_eof').
-  enum heif_reader_grow_status (* wait_for_file_size)(int64_t target_size, void* userdata);
-
-  // --- version 2 functions ---
-
-  // These two functions are for applications that want to stream HEIF files on demand.
-  // For example, a large HEIF file that is served over HTTPS and we only want to download
-  // it partially to decode individual tiles.
-  // If you do not have this use case, you do not have to implement these functions and
-  // you can set them to NULL. For simple linear loading, you may use the 'wait_for_file_size'
-  // function above instead.
-
-  // If this function is defined, libheif will often request a file range before accessing it.
-  // The purpose of this function is that libheif will usually read very small chunks of data with the
-  // read() callback above. However, it is inefficient to request such a small chunk of data over a network
-  // and the network delay will significantly increase the decoding time.
-  // Thus, libheif will call request_range() with a larger block of data that should be preloaded and the
-  // subsequent read() calls will work within the requested ranges.
-  //
-  // Note: `end_pos` is one byte after the last position to be read.
-  // You should return
-  // - 'heif_reader_grow_status_size_reached' if the requested range is available, or
-  // - 'heif_reader_grow_status_size_beyond_eof' if the requested range exceeds the file size
-  //   (the valid part of the range has been read).
-  struct heif_reader_range_request_result (*request_range)(uint64_t start_pos, uint64_t end_pos, void* userdata);
-
-  // libheif might issue hints when it assumes that a file range might be needed in the future.
-  // This may happen, for example, when your are doing selective tile accesses and libheif proposes
-  // to preload offset pointer tables.
-  // Another difference to request_file_range() is that this call should be non-blocking.
-  // If you preload any data, do this in a background thread.
-  void (*preload_range_hint)(uint64_t start_pos, uint64_t end_pos, void* userdata);
-
-  // If libheif does not need access to a file range anymore, it may call this function to
-  // give a hint to the reader that it may release the range from a cache.
-  // If you do not maintain a file cache that wants to reduce its size dynamically, you do not
-  // need to implement this function.
-  void (*release_file_range)(uint64_t start_pos, uint64_t end_pos, void* userdata);
-
-  // Release an error message that was returned by heif_reader in an earlier call.
-  // If this function is NULL, the error message string will not be released.
-  // This is a viable option if you are only returning static strings.
-  void (*release_error_msg)(const char* msg);
-};
-
-
-// Read a HEIF file from a named disk file.
-// The heif_reading_options should currently be set to NULL.
-LIBHEIF_API
-struct heif_error heif_context_read_from_file(struct heif_context*, const char* filename,
-                                              const struct heif_reading_options*);
-
-// Read a HEIF file stored completely in memory.
-// The heif_reading_options should currently be set to NULL.
-// DEPRECATED: use heif_context_read_from_memory_without_copy() instead.
-LIBHEIF_API
-struct heif_error heif_context_read_from_memory(struct heif_context*,
-                                                const void* mem, size_t size,
-                                                const struct heif_reading_options*);
-
-// Same as heif_context_read_from_memory() except that the provided memory is not copied.
-// That means, you will have to keep the memory area alive as long as you use the heif_context.
-LIBHEIF_API
-struct heif_error heif_context_read_from_memory_without_copy(struct heif_context*,
-                                                             const void* mem, size_t size,
-                                                             const struct heif_reading_options*);
-
-LIBHEIF_API
-struct heif_error heif_context_read_from_reader(struct heif_context*,
-                                                const struct heif_reader* reader,
-                                                void* userdata,
-                                                const struct heif_reading_options*);
-
-// Number of top-level images in the HEIF file. This does not include the thumbnails or the
-// tile images that are composed to an image grid. You can get access to the thumbnails via
-// the main image handle.
-LIBHEIF_API
-int heif_context_get_number_of_top_level_images(struct heif_context* ctx);
-
-LIBHEIF_API
-int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id);
-
-// Fills in image IDs into the user-supplied int-array 'ID_array', preallocated with 'count' entries.
-// Function returns the total number of IDs filled into the array.
-LIBHEIF_API
-int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx,
-                                                 heif_item_id* ID_array,
-                                                 int count);
-
-LIBHEIF_API
-struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id);
-
-// Get a handle to the primary image of the HEIF file.
-// This is the image that should be displayed primarily when there are several images in the file.
-LIBHEIF_API
-struct heif_error heif_context_get_primary_image_handle(struct heif_context* ctx,
-                                                        struct heif_image_handle**);
-
-// Get the image handle for a known image ID.
-LIBHEIF_API
-struct heif_error heif_context_get_image_handle(struct heif_context* ctx,
-                                                heif_item_id id,
-                                                struct heif_image_handle**);
-
-// Print information about the boxes of a HEIF file to file descriptor.
-// This is for debugging and informational purposes only. You should not rely on
-// the output having a specific format. At best, you should not use this at all.
-LIBHEIF_API
-void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd);
-
-
-// Set the maximum image size security limit. This function will set the maximum image area (number of pixels)
-// to maximum_width ^ 2. Alternatively to using this function, you can also set the maximum image area
-// in the security limits structure returned by heif_context_get_security_limits().
-LIBHEIF_API
-void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width);
-
-// If the maximum threads number is set to 0, the image tiles are decoded in the main thread.
-// This is different from setting it to 1, which will generate a single background thread to decode the tiles.
-// Note that this setting only affects libheif itself. The codecs itself may still use multi-threaded decoding.
-// You can use it, for example, in cases where you are decoding several images in parallel anyway you thus want
-// to minimize parallelism in each decoder.
-LIBHEIF_API
-void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads);
-
-
-// --- security limits
-
-// If you set a limit to 0, the limit is disabled.
-struct heif_security_limits {
-  uint8_t version;
-
-  // --- version 1
-
-  // Limit on the maximum image size to avoid allocating too much memory.
-  // For example, setting this to 32768^2 pixels = 1 Gigapixels results
-  // in 1.5 GB memory need for YUV-4:2:0 or 4 GB for RGB32.
-  uint64_t max_image_size_pixels;
-  uint64_t max_number_of_tiles;
-  uint32_t max_bayer_pattern_pixels;
-  uint32_t max_items;
-
-  uint32_t max_color_profile_size;
-  uint64_t max_memory_block_size;
-
-  uint32_t max_components;
-
-  uint32_t max_iloc_extents_per_item;
-  uint32_t max_size_entity_group;
-
-  uint32_t max_children_per_box; // for all boxes that are not covered by other limits
-};
-
-// The global security limits are the default for new heif_contexts.
-// These global limits cannot be changed, but you can override the limits for a specific heif_context.
-LIBHEIF_API
-const struct heif_security_limits* heif_get_global_security_limits();
-
-// Returns a set of fully disabled security limits. Use with care and only after user confirmation.
-LIBHEIF_API
-const struct heif_security_limits* heif_get_disabled_security_limits();
-
-// Returns the security limits for a heif_context.
-// By default, the limits are set to the global limits, but you can change them in the returned object.
-LIBHEIF_API
-struct heif_security_limits* heif_context_get_security_limits(const struct heif_context*);
-
-// Overwrites the security limits of a heif_context.
-// This is a convenience function to easily copy limits.
-LIBHEIF_API
-struct heif_error heif_context_set_security_limits(struct heif_context*, const struct heif_security_limits*);
-
-
-// ========================= heif_image_handle =========================
-
-// An heif_image_handle is a handle to a logical image in the HEIF file.
-// To get the actual pixel data, you have to decode the handle to an heif_image.
-// An heif_image_handle also gives you access to the thumbnails and Exif data
-// associated with an image.
-
-// Once you obtained an heif_image_handle, you can already release the heif_context,
-// since it is internally ref-counted.
-
-// Release image handle.
-LIBHEIF_API
-void heif_image_handle_release(const struct heif_image_handle*);
-
-// Check whether the given image_handle is the primary image of the file.
-LIBHEIF_API
-int heif_image_handle_is_primary_image(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-heif_item_id heif_image_handle_get_item_id(const struct heif_image_handle* handle);
-
-// Get the resolution of an image.
-LIBHEIF_API
-int heif_image_handle_get_width(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-int heif_image_handle_get_height(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-int heif_image_handle_has_alpha_channel(const struct heif_image_handle*);
-
-LIBHEIF_API
-int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle*);
-
-// Returns -1 on error, e.g. if this information is not present in the image.
-// Only defined for images coded in the YCbCr or monochrome colorspace.
-LIBHEIF_API
-int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle*);
-
-// Returns -1 on error, e.g. if this information is not present in the image.
-// Only defined for images coded in the YCbCr colorspace.
-LIBHEIF_API
-int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle*);
-
-// Return the colorspace that libheif proposes to use for decoding.
-// Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images
-// encoded with matrix_coefficients=0 or for images coded natively in RGB.
-// It may also return *_undefined if the file misses relevant information to determine this without decoding.
-// These are only proposed values that avoid colorspace conversions as much as possible.
-// You can still request the output in your preferred colorspace, but this may involve an internal conversion.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle,
-                                                                      enum heif_colorspace* out_colorspace,
-                                                                      enum heif_chroma* out_chroma);
-
-// Get the image width from the 'ispe' box. This is the original image size without
-// any transformations applied to it. Do not use this unless you know exactly what
-// you are doing.
-LIBHEIF_API
-int heif_image_handle_get_ispe_width(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-int heif_image_handle_get_ispe_height(const struct heif_image_handle* handle);
-
-// This gets the context associated with the image handle.
-// Note that you have to release the returned context with heif_context_free() in any case.
-//
-// This means: when you have several image-handles that originate from the same file and you get the
-// context of each of them, the returned pointer may be different even though it refers to the same
-// logical context. You have to call heif_context_free() on all those context pointers.
-// After you freed a context pointer, you can still use the context through a different pointer that you
-// might have acquired from elsewhere.
-LIBHEIF_API
-struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle);
-
-
-struct heif_image_tiling
-{
-  int version;
-
-  // --- version 1
-
-  uint32_t num_columns;
-  uint32_t num_rows;
-  uint32_t tile_width;
-  uint32_t tile_height;
-
-  uint32_t image_width;
-  uint32_t image_height;
-
-  // Position of the top left tile.
-  // Usually, this is (0;0), but if a tiled image is rotated or cropped, it may be that the top left tile should be placed at a negative position.
-  // The offsets define this negative shift.
-  uint32_t top_offset;
-  uint32_t left_offset;
-
-  uint8_t number_of_extra_dimensions;  // 0 for normal images, 1 for volumetric (3D), ...
-  uint32_t extra_dimension_size[8];    // size of extra dimensions (first 8 dimensions)
-};
-
-
-// If 'process_image_transformations' is true, this returns modified sizes.
-// If it is false, the top_offset and left_offset will always be (0;0).
-LIBHEIF_API
-struct heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* out_tiling);
-
-
-// For grid images, return the image item ID of a specific grid tile.
-// If 'process_image_transformations' is true, the tile positions are given in the transformed image coordinate system and
-// are internally mapped to the original image tile positions.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle,
-                                                           int process_image_transformations,
-                                                           uint32_t tile_x, uint32_t tile_y,
-                                                           heif_item_id* out_tile_item_id);
-
-
-struct heif_decoding_options;
-
-// The tile position is given in tile indices, not in pixel coordinates.
-// If the image transformations are processed (option->ignore_image_transformations==false), the tile position
-// is given in the transformed coordinates.
-LIBHEIF_API
-struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle,
-                                                      struct heif_image** out_img,
-                                                      enum heif_colorspace colorspace,
-                                                      enum heif_chroma chroma,
-                                                      const struct heif_decoding_options* options,
-                                                      uint32_t tile_x, uint32_t tile_y);
-
-
-// ------------------------- entity groups ------------------------
-
-typedef uint32_t heif_entity_group_id;
-
-struct heif_entity_group
-{
-  heif_entity_group_id entity_group_id;
-  uint32_t entity_group_type;  // this is a FourCC constant
-  heif_item_id* entities;
-  uint32_t num_entities;
-};
-
-// Use 0 for `type_filter` or `item_filter` to disable the filter.
-// Returns an array of heif_entity_group structs with *out_num_groups entries.
-LIBHEIF_API
-struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context*,
-                                                         uint32_t type_filter,
-                                                         heif_item_id item_filter,
-                                                         int* out_num_groups);
-
-// Release an array of entity groups returned by heif_context_get_entity_groups().
-LIBHEIF_API
-void heif_entity_groups_release(struct heif_entity_group*, int num_groups);
-
-
-// ------------------------- depth images -------------------------
-
-LIBHEIF_API
-int heif_image_handle_has_depth_image(const struct heif_image_handle*);
-
-LIBHEIF_API
-int heif_image_handle_get_number_of_depth_images(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-int heif_image_handle_get_list_of_depth_image_IDs(const struct heif_image_handle* handle,
-                                                  heif_item_id* ids, int count);
-
-LIBHEIF_API
-struct heif_error heif_image_handle_get_depth_image_handle(const struct heif_image_handle* handle,
-                                                           heif_item_id depth_image_id,
-                                                           struct heif_image_handle** out_depth_handle);
-
-
-enum heif_depth_representation_type
-{
-  heif_depth_representation_type_uniform_inverse_Z = 0,
-  heif_depth_representation_type_uniform_disparity = 1,
-  heif_depth_representation_type_uniform_Z = 2,
-  heif_depth_representation_type_nonuniform_disparity = 3
-};
-
-struct heif_depth_representation_info
-{
-  uint8_t version;
-
-  // version 1 fields
-
-  uint8_t has_z_near;
-  uint8_t has_z_far;
-  uint8_t has_d_min;
-  uint8_t has_d_max;
-
-  double z_near;
-  double z_far;
-  double d_min;
-  double d_max;
-
-  enum heif_depth_representation_type depth_representation_type;
-  uint32_t disparity_reference_view;
-
-  uint32_t depth_nonlinear_representation_model_size;
-  uint8_t* depth_nonlinear_representation_model;
-
-  // version 2 fields below
-};
-
-
-LIBHEIF_API
-void heif_depth_representation_info_free(const struct heif_depth_representation_info* info);
-
-// Returns true when there is depth_representation_info available
-// Note 1: depth_image_id is currently unused because we support only one depth channel per image, but
-// you should still provide the correct ID for future compatibility.
-// Note 2: Because of an API bug before v1.11.0, the function also works when 'handle' is the handle of the depth image.
-// However, you should pass the handle of the main image. Please adapt your code if needed.
-LIBHEIF_API
-int heif_image_handle_get_depth_image_representation_info(const struct heif_image_handle* handle,
-                                                          heif_item_id depth_image_id,
-                                                          const struct heif_depth_representation_info** out);
-
-
-
-// ------------------------- thumbnails -------------------------
-
-// List the number of thumbnails assigned to this image handle. Usually 0 or 1.
-LIBHEIF_API
-int heif_image_handle_get_number_of_thumbnails(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-int heif_image_handle_get_list_of_thumbnail_IDs(const struct heif_image_handle* handle,
-                                                heif_item_id* ids, int count);
-
-// Get the image handle of a thumbnail image.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_thumbnail(const struct heif_image_handle* main_image_handle,
-                                                  heif_item_id thumbnail_id,
-                                                  struct heif_image_handle** out_thumbnail_handle);
-
-
-// ------------------------- auxiliary images -------------------------
-
-#define LIBHEIF_AUX_IMAGE_FILTER_OMIT_ALPHA (1UL<<1)
-#define LIBHEIF_AUX_IMAGE_FILTER_OMIT_DEPTH (2UL<<1)
-
-// List the number of auxiliary images assigned to this image handle.
-LIBHEIF_API
-int heif_image_handle_get_number_of_auxiliary_images(const struct heif_image_handle* handle,
-                                                     int aux_filter);
-
-LIBHEIF_API
-int heif_image_handle_get_list_of_auxiliary_image_IDs(const struct heif_image_handle* handle,
-                                                      int aux_filter,
-                                                      heif_item_id* ids, int count);
-
-// You are responsible to deallocate the returned buffer with heif_image_handle_release_auxiliary_type().
-LIBHEIF_API
-struct heif_error heif_image_handle_get_auxiliary_type(const struct heif_image_handle* handle,
-                                                       const char** out_type);
-
-LIBHEIF_API
-void heif_image_handle_release_auxiliary_type(const struct heif_image_handle* handle,
-                                              const char** out_type);
-
-// DEPRECATED (because typo in function name). Use heif_image_handle_release_auxiliary_type() instead.
-LIBHEIF_API
-void heif_image_handle_free_auxiliary_types(const struct heif_image_handle* handle,
-                                            const char** out_type);
-
-// Get the image handle of an auxiliary image.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif_image_handle* main_image_handle,
-                                                               heif_item_id auxiliary_id,
-                                                               struct heif_image_handle** out_auxiliary_handle);
-
-
-// ------------------------- metadata (Exif / XMP) -------------------------
-
-// How many metadata blocks are attached to an image. If you only want to get EXIF data,
-// set the type_filter to "Exif". Otherwise, set the type_filter to NULL.
-LIBHEIF_API
-int heif_image_handle_get_number_of_metadata_blocks(const struct heif_image_handle* handle,
-                                                    const char* type_filter);
-
-// 'type_filter' can be used to get only metadata of specific types, like "Exif".
-// If 'type_filter' is NULL, it will return all types of metadata IDs.
-LIBHEIF_API
-int heif_image_handle_get_list_of_metadata_block_IDs(const struct heif_image_handle* handle,
-                                                     const char* type_filter,
-                                                     heif_item_id* ids, int count);
-
-// Return a string indicating the type of the metadata, as specified in the HEIF file.
-// Exif data will have the type string "Exif".
-// This string will be valid until the next call to a libheif function.
-// You do not have to free this string.
-LIBHEIF_API
-const char* heif_image_handle_get_metadata_type(const struct heif_image_handle* handle,
-                                                heif_item_id metadata_id);
-
-// For EXIF, the content type is empty.
-// For XMP, the content type is "application/rdf+xml".
-LIBHEIF_API
-const char* heif_image_handle_get_metadata_content_type(const struct heif_image_handle* handle,
-                                                        heif_item_id metadata_id);
-
-// Get the size of the raw metadata, as stored in the HEIF file.
-LIBHEIF_API
-size_t heif_image_handle_get_metadata_size(const struct heif_image_handle* handle,
-                                           heif_item_id metadata_id);
-
-// 'out_data' must point to a memory area of the size reported by heif_image_handle_get_metadata_size().
-// The data is returned exactly as stored in the HEIF file.
-// For Exif data, you probably have to skip the first four bytes of the data, since they
-// indicate the offset to the start of the TIFF header of the Exif data.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_metadata(const struct heif_image_handle* handle,
-                                                 heif_item_id metadata_id,
-                                                 void* out_data);
-
-// Only valid for item type == "uri ", an absolute URI
-LIBHEIF_API
-const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle,
-                                                         heif_item_id metadata_id);
-
-// ------------------------- color profiles -------------------------
-
-enum heif_color_profile_type
-{
-  heif_color_profile_type_not_present = 0,
-  heif_color_profile_type_nclx = heif_fourcc('n', 'c', 'l', 'x'),
-  heif_color_profile_type_rICC = heif_fourcc('r', 'I', 'C', 'C'),
-  heif_color_profile_type_prof = heif_fourcc('p', 'r', 'o', 'f')
-};
-
-
-// Returns 'heif_color_profile_type_not_present' if there is no color profile.
-// If there is an ICC profile and an NCLX profile, the ICC profile is returned.
-// TODO: we need a new API for this function as images can contain both NCLX and ICC at the same time.
-//       However, you can still use heif_image_handle_get_raw_color_profile() and
-//       heif_image_handle_get_nclx_color_profile() to access both profiles.
-LIBHEIF_API
-enum heif_color_profile_type heif_image_handle_get_color_profile_type(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-size_t heif_image_handle_get_raw_color_profile_size(const struct heif_image_handle* handle);
-
-// Returns 'heif_error_Color_profile_does_not_exist' when there is no ICC profile.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_raw_color_profile(const struct heif_image_handle* handle,
-                                                          void* out_data);
-
-
-enum heif_color_primaries
-{
-  heif_color_primaries_ITU_R_BT_709_5 = 1, // g=0.3;0.6, b=0.15;0.06, r=0.64;0.33, w=0.3127,0.3290
-  heif_color_primaries_unspecified = 2,
-  heif_color_primaries_ITU_R_BT_470_6_System_M = 4,
-  heif_color_primaries_ITU_R_BT_470_6_System_B_G = 5,
-  heif_color_primaries_ITU_R_BT_601_6 = 6,
-  heif_color_primaries_SMPTE_240M = 7,
-  heif_color_primaries_generic_film = 8,
-  heif_color_primaries_ITU_R_BT_2020_2_and_2100_0 = 9,
-  heif_color_primaries_SMPTE_ST_428_1 = 10,
-  heif_color_primaries_SMPTE_RP_431_2 = 11,
-  heif_color_primaries_SMPTE_EG_432_1 = 12,
-  heif_color_primaries_EBU_Tech_3213_E = 22
-};
-
-enum heif_transfer_characteristics
-{
-  heif_transfer_characteristic_ITU_R_BT_709_5 = 1,
-  heif_transfer_characteristic_unspecified = 2,
-  heif_transfer_characteristic_ITU_R_BT_470_6_System_M = 4,
-  heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G = 5,
-  heif_transfer_characteristic_ITU_R_BT_601_6 = 6,
-  heif_transfer_characteristic_SMPTE_240M = 7,
-  heif_transfer_characteristic_linear = 8,
-  heif_transfer_characteristic_logarithmic_100 = 9,
-  heif_transfer_characteristic_logarithmic_100_sqrt10 = 10,
-  heif_transfer_characteristic_IEC_61966_2_4 = 11,
-  heif_transfer_characteristic_ITU_R_BT_1361 = 12,
-  heif_transfer_characteristic_IEC_61966_2_1 = 13,
-  heif_transfer_characteristic_ITU_R_BT_2020_2_10bit = 14,
-  heif_transfer_characteristic_ITU_R_BT_2020_2_12bit = 15,
-  heif_transfer_characteristic_ITU_R_BT_2100_0_PQ = 16,
-  heif_transfer_characteristic_SMPTE_ST_428_1 = 17,
-  heif_transfer_characteristic_ITU_R_BT_2100_0_HLG = 18
-};
-
-enum heif_matrix_coefficients
-{
-  heif_matrix_coefficients_RGB_GBR = 0,
-  heif_matrix_coefficients_ITU_R_BT_709_5 = 1,  // TODO: or 709-6 according to h.273
-  heif_matrix_coefficients_unspecified = 2,
-  heif_matrix_coefficients_US_FCC_T47 = 4,
-  heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G = 5,
-  heif_matrix_coefficients_ITU_R_BT_601_6 = 6,  // TODO: or 601-7 according to h.273
-  heif_matrix_coefficients_SMPTE_240M = 7,
-  heif_matrix_coefficients_YCgCo = 8,
-  heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance = 9,
-  heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance = 10,
-  heif_matrix_coefficients_SMPTE_ST_2085 = 11,
-  heif_matrix_coefficients_chromaticity_derived_non_constant_luminance = 12,
-  heif_matrix_coefficients_chromaticity_derived_constant_luminance = 13,
-  heif_matrix_coefficients_ICtCp = 14
-};
-
-struct heif_color_profile_nclx
-{
-  // === version 1 fields
-
-  uint8_t version;
-
-  enum heif_color_primaries color_primaries;
-  enum heif_transfer_characteristics transfer_characteristics;
-  enum heif_matrix_coefficients matrix_coefficients;
-  uint8_t full_range_flag;
-
-  // --- decoded values (not used when saving nclx)
-
-  float color_primary_red_x, color_primary_red_y;
-  float color_primary_green_x, color_primary_green_y;
-  float color_primary_blue_x, color_primary_blue_y;
-  float color_primary_white_x, color_primary_white_y;
-};
-
-LIBHEIF_API
-struct heif_error heif_nclx_color_profile_set_color_primaries(struct heif_color_profile_nclx* nclx, uint16_t cp);
-
-LIBHEIF_API
-struct heif_error heif_nclx_color_profile_set_transfer_characteristics(struct heif_color_profile_nclx* nclx, uint16_t transfer_characteristics);
-
-LIBHEIF_API
-struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t matrix_coefficients);
-
-// Returns 'heif_error_Color_profile_does_not_exist' when there is no NCLX profile.
-// TODO: This function does currently not return an NCLX profile if it is stored in the image bitstream.
-//       Only NCLX profiles stored as colr boxes are returned. This may change in the future.
-LIBHEIF_API
-struct heif_error heif_image_handle_get_nclx_color_profile(const struct heif_image_handle* handle,
-                                                           struct heif_color_profile_nclx** out_data);
-
-// Returned color profile has 'version' field set to the maximum allowed.
-// Do not fill values for higher versions as these might be outside the allocated structure size.
-// May return NULL.
-LIBHEIF_API
-struct heif_color_profile_nclx* heif_nclx_color_profile_alloc(void);
-
-LIBHEIF_API
-void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile);
-
-// Note: in early versions of HEIF, there could only be one color profile per image. However, this has been changed.
-// This function will now return ICC if one is present and NCLX only if there is no ICC.
-// You may better avoid this function and simply query for NCLX and ICC directly.
-LIBHEIF_API
-enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image);
-
-// Returns the size of the ICC profile if one is assigned to the image. Otherwise, it returns 0.
-LIBHEIF_API
-size_t heif_image_get_raw_color_profile_size(const struct heif_image* image);
-
-// Returns the ICC profile if one is assigned to the image. Otherwise, it returns an error.
-LIBHEIF_API
-struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image,
-                                                   void* out_data);
-
-LIBHEIF_API
-struct heif_error heif_image_get_nclx_color_profile(const struct heif_image* image,
-                                                    struct heif_color_profile_nclx** out_data);
-
-
-// ------------------------- intrinsic and extrinsic matrices -------------------------
-
-struct heif_camera_intrinsic_matrix
-{
-  double focal_length_x;
-  double focal_length_y;
-  double principal_point_x;
-  double principal_point_y;
-  double skew;
-};
-
-
-LIBHEIF_API
-int heif_image_handle_has_camera_intrinsic_matrix(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-struct heif_error heif_image_handle_get_camera_intrinsic_matrix(const struct heif_image_handle* handle,
-                                                                struct heif_camera_intrinsic_matrix* out_matrix);
-
-
-struct heif_camera_extrinsic_matrix;
-
-LIBHEIF_API
-int heif_image_handle_has_camera_extrinsic_matrix(const struct heif_image_handle* handle);
-
-LIBHEIF_API
-struct heif_error heif_image_handle_get_camera_extrinsic_matrix(const struct heif_image_handle* handle,
-                                                                struct heif_camera_extrinsic_matrix** out_matrix);
-
-LIBHEIF_API
-void heif_camera_extrinsic_matrix_release(struct heif_camera_extrinsic_matrix*);
-
-LIBHEIF_API
-struct heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_camera_extrinsic_matrix*,
-                                                                   double* out_matrix_row_major);
-
-
-
-// ========================= heif_image =========================
-
-// An heif_image contains a decoded pixel image in various colorspaces, chroma formats,
-// and bit depths.
-
-// Note: when converting images to an interleaved chroma format, the resulting
-// image contains only a single channel of type channel_interleaved with, e.g., 3 bytes per pixel,
-// containing the interleaved R,G,B values.
-
-// Planar RGB images are specified as heif_colorspace_RGB / heif_chroma_444.
-
-enum heif_progress_step
-{
-  heif_progress_step_total = 0,
-  heif_progress_step_load_tile = 1
-};
-
-
-enum heif_chroma_downsampling_algorithm
-{
-  heif_chroma_downsampling_nearest_neighbor = 1,
-  heif_chroma_downsampling_average = 2,
-
-  // Combine with 'heif_chroma_upsampling_bilinear' for best quality.
-  // Makes edges look sharper when using YUV 420 with bilinear chroma upsampling.
-  heif_chroma_downsampling_sharp_yuv = 3
-};
-
-enum heif_chroma_upsampling_algorithm
-{
-  heif_chroma_upsampling_nearest_neighbor = 1,
-  heif_chroma_upsampling_bilinear = 2
-};
-
-
-struct heif_color_conversion_options
-{
-  // 'version' must be 1.
-  uint8_t version;
-
-  // --- version 1 options
-
-  enum heif_chroma_downsampling_algorithm preferred_chroma_downsampling_algorithm;
-  enum heif_chroma_upsampling_algorithm preferred_chroma_upsampling_algorithm;
-
-  // When set to 'false' libheif may also use a different algorithm if the preferred one is not available
-  // or using a different algorithm is computationally less complex. Note that currently (v1.17.0) this
-  // means that for RGB input it will usually choose nearest-neighbor sampling because this is computationally
-  // the simplest.
-  // Set this field to 'true' if you want to make sure that the specified algorithm is used even
-  // at the cost of slightly higher computation times.
-  uint8_t only_use_preferred_chroma_algorithm;
-
-  // --- Note that we cannot extend this struct because it is embedded in
-  //     other structs (heif_decoding_options and heif_encoding_options).
-};
-
-// Assumes that it is a version=1 struct.
-LIBHEIF_API
-void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options*);
-
-
-struct heif_decoding_options
-{
-  uint8_t version;
-
-  // version 1 options
-
-  // Ignore geometric transformations like cropping, rotation, mirroring.
-  // Default: false (do not ignore).
-  uint8_t ignore_transformations;
-
-  // Any of the progress functions may be called from background threads.
-  void (* start_progress)(enum heif_progress_step step, int max_progress, void* progress_user_data);
-
-  void (* on_progress)(enum heif_progress_step step, int progress, void* progress_user_data);
-
-  void (* end_progress)(enum heif_progress_step step, void* progress_user_data);
-
-  void* progress_user_data;
-
-  // version 2 options
-
-  uint8_t convert_hdr_to_8bit;
-
-  // version 3 options
-
-  // When enabled, an error is returned for invalid input. Otherwise, it will try its best and
-  // add decoding warnings to the decoded heif_image. Default is non-strict.
-  uint8_t strict_decoding;
-
-  // version 4 options
-
-  // name_id of the decoder to use for the decoding.
-  // If set to NULL (default), the highest priority decoder is chosen.
-  // The priority is defined in the plugin.
-  const char* decoder_id;
-
-  // version 5 options
-
-  struct heif_color_conversion_options color_conversion_options;
-
-  // version 6 options
-
-  int (* cancel_decoding)(void* progress_user_data);
-};
-
-
-// Allocate decoding options and fill with default values.
-// Note: you should always get the decoding options through this function since the
-// option structure may grow in size in future versions.
-LIBHEIF_API
-struct heif_decoding_options* heif_decoding_options_alloc(void);
-
-LIBHEIF_API
-void heif_decoding_options_free(struct heif_decoding_options*);
-
-// Decode an heif_image_handle into the actual pixel image and also carry out
-// all geometric transformations specified in the HEIF file (rotation, cropping, mirroring).
-//
-// If colorspace or chroma is set to heif_colorspace_undefined or heif_chroma_undefined,
-// respectively, the original colorspace is taken.
-// Decoding options may be NULL. If you want to supply options, always use
-// heif_decoding_options_alloc() to get the structure.
-LIBHEIF_API
-struct heif_error heif_decode_image(const struct heif_image_handle* in_handle,
-                                    struct heif_image** out_img,
-                                    enum heif_colorspace colorspace,
-                                    enum heif_chroma chroma,
-                                    const struct heif_decoding_options* options);
-
-// Get the colorspace format of the image.
-LIBHEIF_API
-enum heif_colorspace heif_image_get_colorspace(const struct heif_image*);
-
-// Get the chroma format of the image.
-LIBHEIF_API
-enum heif_chroma heif_image_get_chroma_format(const struct heif_image*);
-
-/**
- * Get the width of a specified image channel.
- *
- * @param img the image to get the width for
- * @param channel the channel to select
- * @return the width of the channel in pixels, or -1 the channel does not exist in the image
- */
-LIBHEIF_API
-int heif_image_get_width(const struct heif_image* img, enum heif_channel channel);
-
-/**
- * Get the height of a specified image channel.
- *
- * @param img the image to get the height for
- * @param channel the channel to select
- * @return the height of the channel in pixels, or -1 the channel does not exist in the image
- */
-LIBHEIF_API
-int heif_image_get_height(const struct heif_image* img, enum heif_channel channel);
-
-/**
- * Get the width of the main channel.
- *
- * This is the Y channel in YCbCr or mono, or any in RGB.
- *
- * @param img the image to get the primary width for
- * @return the width in pixels
- */
-LIBHEIF_API
-int heif_image_get_primary_width(const struct heif_image* img);
-
-/**
- * Get the height of the main channel.
- *
- * This is the Y channel in YCbCr or mono, or any in RGB.
- *
- * @param img the image to get the primary height for
- * @return the height in pixels
- */
-LIBHEIF_API
-int heif_image_get_primary_height(const struct heif_image* img);
-
-LIBHEIF_API
-struct heif_error heif_image_crop(struct heif_image* img,
-                                  int left, int right, int top, int bottom);
-
-// Get the number of bits per pixel in the given image channel. Returns -1 if
-// a non-existing channel was given.
-// Note that the number of bits per pixel may be different for each color channel.
-// This function returns the number of bits used for storage of each pixel.
-// Especially for HDR images, this is probably not what you want. Have a look at
-// heif_image_get_bits_per_pixel_range() instead.
-LIBHEIF_API
-int heif_image_get_bits_per_pixel(const struct heif_image*, enum heif_channel channel);
-
-// Get the number of bits per pixel in the given image channel. This function returns
-// the number of bits used for representing the pixel value, which might be smaller
-// than the number of bits used in memory.
-// For example, in 12bit HDR images, this function returns '12', while still 16 bits
-// are reserved for storage. For interleaved RGBA with 12 bit, this function also returns
-// '12', not '48' or '64' (heif_image_get_bits_per_pixel returns 64 in this case).
-LIBHEIF_API
-int heif_image_get_bits_per_pixel_range(const struct heif_image*, enum heif_channel channel);
-
-LIBHEIF_API
-int heif_image_has_channel(const struct heif_image*, enum heif_channel channel);
-
-// Get a pointer to the actual pixel data.
-// The 'out_stride' is returned as "bytes per line".
-// When out_stride is NULL, no value will be written.
-// Returns NULL if a non-existing channel was given.
-// TODO: it would be better if the 'stride' parameter would be size_t to prevent integer overflows when this value is multiplicated with large y coordinates.
-LIBHEIF_API
-const uint8_t* heif_image_get_plane_readonly(const struct heif_image*,
-                                             enum heif_channel channel,
-                                             int* out_stride);
-
-LIBHEIF_API
-uint8_t* heif_image_get_plane(struct heif_image*,
-                              enum heif_channel channel,
-                              int* out_stride);
-
-
-
-struct heif_scaling_options;
-
-// Currently, heif_scaling_options is not defined yet. Pass a NULL pointer.
-LIBHEIF_API
-struct heif_error heif_image_scale_image(const struct heif_image* input,
-                                         struct heif_image** output,
-                                         int width, int height,
-                                         const struct heif_scaling_options* options);
-
-// Extends the image size to match the given size by extending the right and bottom borders.
-// The border areas are filled with zero.
-LIBHEIF_API
-struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image,
-                                                           uint32_t width, uint32_t height);
-
-// The color profile is not attached to the image handle because we might need it
-// for color space transform and encoding.
-LIBHEIF_API
-struct heif_error heif_image_set_raw_color_profile(struct heif_image* image,
-                                                   const char* profile_type_fourcc_string,
-                                                   const void* profile_data,
-                                                   const size_t profile_size);
-
-LIBHEIF_API
-struct heif_error heif_image_set_nclx_color_profile(struct heif_image* image,
-                                                    const struct heif_color_profile_nclx* color_profile);
-
-
-// TODO: this function does not make any sense yet, since we currently cannot modify existing HEIF files.
-//LIBHEIF_API
-//void heif_image_remove_color_profile(struct heif_image* image);
-
-// Fills the image decoding warnings into the provided 'out_warnings' array.
-// The size of the array has to be provided in max_output_buffer_entries.
-// If max_output_buffer_entries==0, the number of decoder warnings is returned.
-// The function fills the warnings into the provided buffer, starting with 'first_warning_idx'.
-// It returns the number of warnings filled into the buffer.
-// Note: you can iterate through all warnings by using 'max_output_buffer_entries=1' and iterate 'first_warning_idx'.
-LIBHEIF_API
-int heif_image_get_decoding_warnings(struct heif_image* image,
-                                     int first_warning_idx,
-                                     struct heif_error* out_warnings,
-                                     int max_output_buffer_entries);
-
-// This function is only for decoder plugin implementors.
-LIBHEIF_API
-void heif_image_add_decoding_warning(struct heif_image* image,
-                                     struct heif_error err);
-
-// Release heif_image.
-LIBHEIF_API
-void heif_image_release(const struct heif_image*);
-
-
-// Note: a value of 0 for any of these values indicates that the value is undefined.
-// The unit of these values is Candelas per square meter.
-struct heif_content_light_level
-{
-  uint16_t max_content_light_level;
-  uint16_t max_pic_average_light_level;
-};
-
-LIBHEIF_API
-int heif_image_has_content_light_level(const struct heif_image*);
-
-LIBHEIF_API
-void heif_image_get_content_light_level(const struct heif_image*, struct heif_content_light_level* out);
-
-// Returns whether the image has 'content light level' information. If 0 is returned, the output is not filled.
-LIBHEIF_API
-int heif_image_handle_get_content_light_level(const struct heif_image_handle*, struct heif_content_light_level* out);
-
-LIBHEIF_API
-void heif_image_set_content_light_level(const struct heif_image*, const struct heif_content_light_level* in);
-
-
-// Note: color coordinates are defined according to the CIE 1931 definition of x as specified in ISO 11664-1 (see also ISO 11664-3 and CIE 15).
-struct heif_mastering_display_colour_volume
-{
-  uint16_t display_primaries_x[3];
-  uint16_t display_primaries_y[3];
-  uint16_t white_point_x;
-  uint16_t white_point_y;
-  uint32_t max_display_mastering_luminance;
-  uint32_t min_display_mastering_luminance;
-};
-
-// The units for max_display_mastering_luminance and min_display_mastering_luminance is Candelas per square meter.
-struct heif_decoded_mastering_display_colour_volume
-{
-  float display_primaries_x[3];
-  float display_primaries_y[3];
-  float white_point_x;
-  float white_point_y;
-  double max_display_mastering_luminance;
-  double min_display_mastering_luminance;
-};
-
-struct heif_ambient_viewing_environment
-{
-  uint32_t ambient_illumination;
-  uint16_t ambient_light_x;
-  uint16_t ambient_light_y;
-};
-
-LIBHEIF_API
-int heif_image_has_mastering_display_colour_volume(const struct heif_image*);
-
-LIBHEIF_API
-void heif_image_get_mastering_display_colour_volume(const struct heif_image*, struct heif_mastering_display_colour_volume* out);
-
-// Returns whether the image has 'mastering display colour volume' information. If 0 is returned, the output is not filled.
-LIBHEIF_API
-int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle*, struct heif_mastering_display_colour_volume* out);
-
-LIBHEIF_API
-void heif_image_set_mastering_display_colour_volume(const struct heif_image*, const struct heif_mastering_display_colour_volume* in);
-
-
-// Converts the internal numeric representation of heif_mastering_display_colour_volume to the
-// normalized values, collected in heif_decoded_mastering_display_colour_volume.
-// Values that are out-of-range are decoded to 0, indicating an undefined value (as specified in ISO/IEC 23008-2).
-LIBHEIF_API
-struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_mastering_display_colour_volume* in,
-                                                              struct heif_decoded_mastering_display_colour_volume* out);
-
-LIBHEIF_API
-void heif_image_get_pixel_aspect_ratio(const struct heif_image*, uint32_t* aspect_h, uint32_t* aspect_v);
-
-// Returns whether the image has 'pixel aspect ratio information' information. If 0 is returned, the output is filled with the 1:1 default.
-LIBHEIF_API
-int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle*, uint32_t* aspect_h, uint32_t* aspect_v);
-
-LIBHEIF_API
-void heif_image_set_pixel_aspect_ratio(struct heif_image*, uint32_t aspect_h, uint32_t aspect_v);
-
-// ====================================================================================================
-//  Encoding API
-
-LIBHEIF_API
-struct heif_error heif_context_write_to_file(struct heif_context*,
-                                             const char* filename);
-
-struct heif_writer
-{
-  // API version supported by this writer
-  int writer_api_version;
-
-  // --- version 1 functions ---
-
-  // On success, the returned heif_error may have a NULL message. It will automatically be replaced with a "Success" string.
-  struct heif_error (* write)(struct heif_context* ctx, // TODO: why do we need this parameter?
-                              const void* data,
-                              size_t size,
-                              void* userdata);
-};
-
-LIBHEIF_API
-struct heif_error heif_context_write(struct heif_context*,
-                                     struct heif_writer* writer,
-                                     void* userdata);
-
-// Add a compatible brand that is now added automatically by libheif when encoding images (e.g. some application brands like 'geo1').
-LIBHEIF_API
-void heif_context_add_compatible_brand(struct heif_context* ctx,
-                                       heif_brand2 compatible_brand);
-
-// ----- encoder -----
-
-// The encoder used for actually encoding an image.
-struct heif_encoder;
-
-// A description of the encoder's capabilities and name.
-struct heif_encoder_descriptor;
-
-// A configuration parameter of the encoder. Each encoder implementation may have a different
-// set of parameters. For the most common settings (e.q. quality), special functions to set
-// the parameters are provided.
-struct heif_encoder_parameter;
-
-struct heif_decoder_descriptor;
-
-// Get a list of available decoders. You can filter the encoders by compression format.
-// Use format_filter==heif_compression_undefined to get all available decoders.
-// The returned list of decoders is sorted by their priority (which is a plugin property).
-// The number of decoders is returned, which are not more than 'count' if (out_decoders != nullptr).
-// By setting out_decoders==nullptr, you can query the number of decoders, 'count' is ignored.
-LIBHEIF_API
-int heif_get_decoder_descriptors(enum heif_compression_format format_filter,
-                                 const struct heif_decoder_descriptor** out_decoders,
-                                 int count);
-
-// Return a long, descriptive name of the decoder (including version information).
-LIBHEIF_API
-const char* heif_decoder_descriptor_get_name(const struct heif_decoder_descriptor*);
-
-// Return a short, symbolic name for identifying the decoder.
-// This name should stay constant over different decoder versions.
-// Note: the returned ID may be NULL for old plugins that don't support this yet.
-LIBHEIF_API
-const char* heif_decoder_descriptor_get_id_name(const struct heif_decoder_descriptor*);
-
-// DEPRECATED: use heif_get_encoder_descriptors() instead.
-// Get a list of available encoders. You can filter the encoders by compression format and name.
-// Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards.
-// The returned list of encoders is sorted by their priority (which is a plugin property).
-// The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr).
-// By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored.
-// Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder().
-LIBHEIF_API
-int heif_context_get_encoder_descriptors(struct heif_context*, // TODO: why do we need this parameter?
-                                         enum heif_compression_format format_filter,
-                                         const char* name_filter,
-                                         const struct heif_encoder_descriptor** out_encoders,
-                                         int count);
-
-// Get a list of available encoders. You can filter the encoders by compression format and name.
-// Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards.
-// The returned list of encoders is sorted by their priority (which is a plugin property).
-// The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr).
-// By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored.
-// Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder().
-LIBHEIF_API
-int heif_get_encoder_descriptors(enum heif_compression_format format_filter,
-                                 const char* name_filter,
-                                 const struct heif_encoder_descriptor** out_encoders,
-                                 int count);
-
-// Return a long, descriptive name of the encoder (including version information).
-LIBHEIF_API
-const char* heif_encoder_descriptor_get_name(const struct heif_encoder_descriptor*);
-
-// Return a short, symbolic name for identifying the encoder.
-// This name should stay constant over different encoder versions.
-LIBHEIF_API
-const char* heif_encoder_descriptor_get_id_name(const struct heif_encoder_descriptor*);
-
-LIBHEIF_API
-enum heif_compression_format
-heif_encoder_descriptor_get_compression_format(const struct heif_encoder_descriptor*);
-
-LIBHEIF_API
-int heif_encoder_descriptor_supports_lossy_compression(const struct heif_encoder_descriptor*);
-
-LIBHEIF_API
-int heif_encoder_descriptor_supports_lossless_compression(const struct heif_encoder_descriptor*);
-
-
-// Get an encoder instance that can be used to actually encode images from a descriptor.
-LIBHEIF_API
-struct heif_error heif_context_get_encoder(struct heif_context* context,
-                                           const struct heif_encoder_descriptor*,
-                                           struct heif_encoder** out_encoder);
-
-// Quick check whether there is a decoder available for the given format.
-// Note that the decoder still may not be able to decode all variants of that format.
-// You will have to query that further (todo) or just try to decode and check the returned error.
-LIBHEIF_API
-int heif_have_decoder_for_format(enum heif_compression_format format);
-
-// Quick check whether there is an enoder available for the given format.
-// Note that the encoder may be limited to a certain subset of features (e.g. only 8 bit, only lossy).
-// You will have to query the specific capabilities further.
-LIBHEIF_API
-int heif_have_encoder_for_format(enum heif_compression_format format);
-
-// Get an encoder for the given compression format. If there are several encoder plugins
-// for this format, the encoder with the highest plugin priority will be returned.
-LIBHEIF_API
-struct heif_error heif_context_get_encoder_for_format(struct heif_context* context,
-                                                      enum heif_compression_format format,
-                                                      struct heif_encoder**);
-
-// You have to release the encoder after use.
-LIBHEIF_API
-void heif_encoder_release(struct heif_encoder*);
-
-// Get the encoder name from the encoder itself.
-LIBHEIF_API
-const char* heif_encoder_get_name(const struct heif_encoder*);
-
-
-// --- Encoder Parameters ---
-
-// Libheif supports settings parameters through specialized functions and through
-// generic functions by parameter name. Sometimes, the same parameter can be set
-// in both ways.
-// We consider it best practice to use the generic parameter functions only in
-// dynamically generated user interfaces, as no guarantees are made that some specific
-// parameter names are supported by all plugins.
-
-
-// Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is
-// encoder dependent.
-LIBHEIF_API
-struct heif_error heif_encoder_set_lossy_quality(struct heif_encoder*, int quality);
-
-LIBHEIF_API
-struct heif_error heif_encoder_set_lossless(struct heif_encoder*, int enable);
-
-// level should be between 0 (= none) to 4 (= full)
-LIBHEIF_API
-struct heif_error heif_encoder_set_logging_level(struct heif_encoder*, int level);
-
-// Get a generic list of encoder parameters.
-// Each encoder may define its own, additional set of parameters.
-// You do not have to free the returned list.
-LIBHEIF_API
-const struct heif_encoder_parameter* const* heif_encoder_list_parameters(struct heif_encoder*);
-
-// Return the parameter name.
-LIBHEIF_API
-const char* heif_encoder_parameter_get_name(const struct heif_encoder_parameter*);
-
-
-enum heif_encoder_parameter_type
-{
-  heif_encoder_parameter_type_integer = 1,
-  heif_encoder_parameter_type_boolean = 2,
-  heif_encoder_parameter_type_string = 3
-};
-
-// Return the parameter type.
-LIBHEIF_API
-enum heif_encoder_parameter_type heif_encoder_parameter_get_type(const struct heif_encoder_parameter*);
-
-// DEPRECATED. Use heif_encoder_parameter_get_valid_integer_values() instead.
-LIBHEIF_API
-struct heif_error heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_parameter*,
-                                                                 int* have_minimum_maximum,
-                                                                 int* minimum, int* maximum);
-
-// If integer is limited by a range, have_minimum and/or have_maximum will be != 0 and *minimum, *maximum is set.
-// If integer is limited by a fixed set of values, *num_valid_values will be >0 and *out_integer_array is set.
-LIBHEIF_API
-struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter*,
-                                                                  int* have_minimum, int* have_maximum,
-                                                                  int* minimum, int* maximum,
-                                                                  int* num_valid_values,
-                                                                  const int** out_integer_array);
-
-LIBHEIF_API
-struct heif_error heif_encoder_parameter_get_valid_string_values(const struct heif_encoder_parameter*,
-                                                                 const char* const** out_stringarray);
-
-
-LIBHEIF_API
-struct heif_error heif_encoder_set_parameter_integer(struct heif_encoder*,
-                                                     const char* parameter_name,
-                                                     int value);
-
-LIBHEIF_API
-struct heif_error heif_encoder_get_parameter_integer(struct heif_encoder*,
-                                                     const char* parameter_name,
-                                                     int* value);
-
-// TODO: name should be changed to heif_encoder_get_valid_integer_parameter_range
-LIBHEIF_API // DEPRECATED.
-struct heif_error heif_encoder_parameter_integer_valid_range(struct heif_encoder*,
-                                                             const char* parameter_name,
-                                                             int* have_minimum_maximum,
-                                                             int* minimum, int* maximum);
-
-LIBHEIF_API
-struct heif_error heif_encoder_set_parameter_boolean(struct heif_encoder*,
-                                                     const char* parameter_name,
-                                                     int value);
-
-LIBHEIF_API
-struct heif_error heif_encoder_get_parameter_boolean(struct heif_encoder*,
-                                                     const char* parameter_name,
-                                                     int* value);
-
-LIBHEIF_API
-struct heif_error heif_encoder_set_parameter_string(struct heif_encoder*,
-                                                    const char* parameter_name,
-                                                    const char* value);
-
-LIBHEIF_API
-struct heif_error heif_encoder_get_parameter_string(struct heif_encoder*,
-                                                    const char* parameter_name,
-                                                    char* value, int value_size);
-
-// returns a NULL-terminated list of valid strings or NULL if all values are allowed
-LIBHEIF_API
-struct heif_error heif_encoder_parameter_string_valid_values(struct heif_encoder*,
-                                                             const char* parameter_name,
-                                                             const char* const** out_stringarray);
-
-LIBHEIF_API
-struct heif_error heif_encoder_parameter_integer_valid_values(struct heif_encoder*,
-                                                              const char* parameter_name,
-                                                              int* have_minimum, int* have_maximum,
-                                                              int* minimum, int* maximum,
-                                                              int* num_valid_values,
-                                                              const int** out_integer_array);
-
-// Set a parameter of any type to the string value.
-// Integer values are parsed from the string.
-// Boolean values can be "true"/"false"/"1"/"0"
-//
-// x265 encoder specific note:
-// When using the x265 encoder, you may pass any of its parameters by
-// prefixing the parameter name with 'x265:'. Hence, to set the 'ctu' parameter,
-// you will have to set 'x265:ctu' in libheif.
-// Note that there is no checking for valid parameters when using the prefix.
-LIBHEIF_API
-struct heif_error heif_encoder_set_parameter(struct heif_encoder*,
-                                             const char* parameter_name,
-                                             const char* value);
-
-// Get the current value of a parameter of any type as a human readable string.
-// The returned string is compatible with heif_encoder_set_parameter().
-LIBHEIF_API
-struct heif_error heif_encoder_get_parameter(struct heif_encoder*,
-                                             const char* parameter_name,
-                                             char* value_ptr, int value_size);
-
-// Query whether a specific parameter has a default value.
-LIBHEIF_API
-int heif_encoder_has_default(struct heif_encoder*,
-                             const char* parameter_name);
-
-
-// The orientation values are defined equal to the EXIF Orientation tag.
-enum heif_orientation
-{
-  heif_orientation_normal = 1,
-  heif_orientation_flip_horizontally = 2,
-  heif_orientation_rotate_180 = 3,
-  heif_orientation_flip_vertically = 4,
-  heif_orientation_rotate_90_cw_then_flip_horizontally = 5,
-  heif_orientation_rotate_90_cw = 6,
-  heif_orientation_rotate_90_cw_then_flip_vertically = 7,
-  heif_orientation_rotate_270_cw = 8
-};
-
-
-struct heif_encoding_options
-{
-  uint8_t version;
-
-  // version 1 options
-
-  uint8_t save_alpha_channel; // default: true
-
-  // version 2 options
-
-  // DEPRECATED. This option is not required anymore. Its value will be ignored.
-  uint8_t macOS_compatibility_workaround;
-
-  // version 3 options
-
-  uint8_t save_two_colr_boxes_when_ICC_and_nclx_available; // default: false
-
-  // version 4 options
-
-  // Set this to the NCLX parameters to be used in the output image or set to NULL
-  // when the same parameters as in the input image should be used.
-  struct heif_color_profile_nclx* output_nclx_profile;
-
-  uint8_t macOS_compatibility_workaround_no_nclx_profile;
-
-  // version 5 options
-
-  // libheif will generate irot/imir boxes to match these orientations
-  enum heif_orientation image_orientation;
-
-  // version 6 options
-
-  struct heif_color_conversion_options color_conversion_options;
-
-  // version 7 options
-
-  // Set this to true to use compressed form of uncC where possible.
-  uint8_t prefer_uncC_short_form;
-
-  // TODO: we should add a flag to force MIAF compatible outputs. E.g. this will put restrictions on grid tile sizes and
-  //       might add a clap box when the grid output size does not match the color subsampling factors.
-  //       Since some of these constraints have to be known before actually encoding the image, "forcing MIAF compatibility"
-  //       could also be a flag in the heif_context.
-};
-
-LIBHEIF_API
-struct heif_encoding_options* heif_encoding_options_alloc(void);
-
-LIBHEIF_API
-void heif_encoding_options_free(struct heif_encoding_options*);
-
-
-// Compress the input image.
-// Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL.
-// 'options' should be NULL for now.
-// The first image added to the context is also automatically set the primary image, but
-// you can change the primary image later with heif_context_set_primary_image().
-LIBHEIF_API
-struct heif_error heif_context_encode_image(struct heif_context*,
-                                            const struct heif_image* image,
-                                            struct heif_encoder* encoder,
-                                            const struct heif_encoding_options* options,
-                                            struct heif_image_handle** out_image_handle);
-
-/**
- * @brief Encodes an array of images into a grid.
- * 
- * @param ctx The file context
- * @param tiles User allocated array of images that will form the grid.
- * @param rows The number of rows in the grid.
- * @param columns The number of columns in the grid.
- * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format()
- * @param input_options Optional, may be nullptr.
- * @param out_image_handle Returns a handle to the grid. The caller is responsible for freeing it.
- * @return Returns an error if ctx, tiles, or encoder is nullptr. If rows or columns is 0. 
- */
-LIBHEIF_API
-struct heif_error heif_context_encode_grid(struct heif_context* ctx,
-                                           struct heif_image** tiles,
-                                           uint16_t rows,
-                                           uint16_t columns,
-                                           struct heif_encoder* encoder,
-                                           const struct heif_encoding_options* input_options,
-                                           struct heif_image_handle** out_image_handle);
-
-LIBHEIF_API
-struct heif_error heif_context_add_grid_image(struct heif_context* ctx,
-                                              uint32_t image_width,
-                                              uint32_t image_height,
-                                              uint32_t tile_columns,
-                                              uint32_t tile_rows,
-                                              const struct heif_encoding_options* encoding_options,
-                                              struct heif_image_handle** out_grid_image_handle);
-
-LIBHEIF_API
-struct heif_error heif_context_add_image_tile(struct heif_context* ctx,
-                                              struct heif_image_handle* tiled_image,
-                                              uint32_t tile_x, uint32_t tile_y,
-                                              const struct heif_image* image,
-                                              struct heif_encoder* encoder);
-
-// offsets[] should either be NULL (all offsets==0) or an array of size 2*nImages with x;y offset pairs.
-// If background_rgba is NULL, the background is transparent.
-LIBHEIF_API
-struct heif_error heif_context_add_overlay_image(struct heif_context* ctx,
-                                                 uint32_t image_width,
-                                                 uint32_t image_height,
-                                                 uint16_t nImages,
-                                                 const heif_item_id* image_ids,
-                                                 int32_t* offsets,
-                                                 const uint16_t background_rgba[4],
-                                                 struct heif_image_handle** out_iovl_image_handle);
-
-LIBHEIF_API
-struct heif_error heif_context_set_primary_image(struct heif_context*,
-                                                 struct heif_image_handle* image_handle);
-
-// Encode the 'image' as a scaled down thumbnail image.
-// The image is scaled down to fit into a square area of width 'bbox_size'.
-// If the input image is already so small that it fits into this bounding box, no thumbnail
-// image is encoded and NULL is returned in 'out_thumb_image_handle'.
-// No error is returned in this case.
-// The encoded thumbnail is automatically assigned to the 'master_image_handle'. Hence, you
-// do not have to call heif_context_assign_thumbnail().
-LIBHEIF_API
-struct heif_error heif_context_encode_thumbnail(struct heif_context*,
-                                                const struct heif_image* image,
-                                                const struct heif_image_handle* master_image_handle,
-                                                struct heif_encoder* encoder,
-                                                const struct heif_encoding_options* options,
-                                                int bbox_size,
-                                                struct heif_image_handle** out_thumb_image_handle);
-
-// Assign 'thumbnail_image' as the thumbnail image of 'master_image'.
-LIBHEIF_API
-struct heif_error heif_context_assign_thumbnail(struct heif_context*,
-                                                const struct heif_image_handle* master_image,
-                                                const struct heif_image_handle* thumbnail_image);
-
-// Add EXIF metadata to an image.
-LIBHEIF_API
-struct heif_error heif_context_add_exif_metadata(struct heif_context*,
-                                                 const struct heif_image_handle* image_handle,
-                                                 const void* data, int size);
-
-// Add XMP metadata to an image.
-LIBHEIF_API
-struct heif_error heif_context_add_XMP_metadata(struct heif_context*,
-                                                const struct heif_image_handle* image_handle,
-                                                const void* data, int size);
-
-// New version of heif_context_add_XMP_metadata() with data compression (experimental).
-LIBHEIF_API
-struct heif_error heif_context_add_XMP_metadata2(struct heif_context*,
-                                                 const struct heif_image_handle* image_handle,
-                                                 const void* data, int size,
-                                                 enum heif_metadata_compression compression);
-
-// Add generic, proprietary metadata to an image. You have to specify an 'item_type' that will
-// identify your metadata. 'content_type' can be an additional type, or it can be NULL.
-// For example, this function can be used to add IPTC metadata (IIM stream, not XMP) to an image.
-// Although not standard, we propose to store IPTC data with item type="iptc", content_type=NULL.
-LIBHEIF_API
-struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx,
-                                                    const struct heif_image_handle* image_handle,
-                                                    const void* data, int size,
-                                                    const char* item_type, const char* content_type);
-
-// Add generic metadata with item_type "uri ". Items with this type do not have a content_type, but
-// an item_uri_type and they have no content_encoding (they are always stored uncompressed).
-LIBHEIF_API
-struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx,
-                                                    const struct heif_image_handle* image_handle,
-                                                    const void* data, int size,
-                                                    const char* item_uri_type,
-                                                    heif_item_id* out_item_id);
-
-// --- heif_image allocation
-
-/**
- * Create a new image of the specified resolution and colorspace.
- *
- * <p>This does not allocate memory for the image data. Use {@link heif_image_add_plane} to
- * add the corresponding planes to match the specified {@code colorspace} and {@code chroma}.
- *
- * @param width the width of the image in pixels
- * @param height the height of the image in pixels
- * @param colorspace the colorspace of the image
- * @param chroma the chroma of the image
- * @param out_image pointer to pointer of the resulting image
- * @return whether the creation succeeded or there was an error
-*/
-LIBHEIF_API
-struct heif_error heif_image_create(int width, int height,
-                                    enum heif_colorspace colorspace,
-                                    enum heif_chroma chroma,
-                                    struct heif_image** out_image);
-
-/**
- * Add an image plane to the image.
- *
- * <p>The image plane needs to match the colorspace and chroma of the image. Note
- * that this does not need to be a single "planar" format - interleaved pixel channels
- * can also be used if the chroma is interleaved.
- *
- * <p>The indicated bit_depth corresponds to the bit depth per channel. For example,
- * with an interleaved format like RRGGBB where each color is represented by 10 bits,
- * the {@code bit_depth} would be {@code 10} rather than {@code 30}.
- *
- * <p>For backward compatibility, one can also specify 24bits for RGB and 32bits for RGBA,
- * instead of the preferred 8 bits. However, this use is deprecated.
- *
- * @param image the parent image to add the channel plane to
- * @param channel the channel of the plane to add
- * @param width the width of the plane
- * @param height the height of the plane
- * @param bit_depth the bit depth per color channel
- * @return whether the addition succeeded or there was an error
- *
- * @note The width and height are usually the same as the parent image, but can be
- * less for subsampling.
- *
- * @note The specified width can differ from the row stride of the resulting image plane.
- * Always use the result of {@link heif_image_get_plane} or {@link heif_image_get_plane_readonly}
- * to determine row stride.
- */
-LIBHEIF_API
-struct heif_error heif_image_add_plane(struct heif_image* image,
-                                       enum heif_channel channel,
-                                       int width, int height, int bit_depth);
-
-// Signal that the image is premultiplied by the alpha pixel values.
-LIBHEIF_API
-void heif_image_set_premultiplied_alpha(struct heif_image* image,
-                                        int is_premultiplied_alpha);
-
-LIBHEIF_API
-int heif_image_is_premultiplied_alpha(struct heif_image* image);
-
-// This function extends the padding of the image so that it has at least the given physical size.
-// The padding border is filled with the pixels along the right/bottom border.
-// This function may be useful if you want to process the image, but have some external padding requirements.
-// The image size will not be modified if it is already larger/equal than the given physical size.
-// I.e. you cannot assume that after calling this function, the stride will be equal to min_physical_width.
-LIBHEIF_API
-struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height);
-
-
-
-// --- register plugins
-
-struct heif_decoder_plugin;
-struct heif_encoder_plugin;
-
-// DEPRECATED. Use heif_register_decoder_plugin(const struct heif_decoder_plugin*) instead.
-LIBHEIF_API
-struct heif_error heif_register_decoder(struct heif_context* heif, const struct heif_decoder_plugin*);
-
-LIBHEIF_API
-struct heif_error heif_register_decoder_plugin(const struct heif_decoder_plugin*);
-
-LIBHEIF_API
-struct heif_error heif_register_encoder_plugin(const struct heif_encoder_plugin*);
-
-// DEPRECATED, typo in function name
-LIBHEIF_API
-int heif_encoder_descriptor_supportes_lossy_compression(const struct heif_encoder_descriptor*);
-
-// DEPRECATED, typo in function name
-LIBHEIF_API
-int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor*);
-
-
-#ifdef __cplusplus
-}
-#endif
+#include <libheif/heif_image.h>
+#include <libheif/heif_color.h>
+#include <libheif/heif_error.h>
+#include <libheif/heif_brands.h>
+
+#include <libheif/heif_metadata.h>
+#include <libheif/heif_aux_images.h>
+#include <libheif/heif_entity_groups.h>
+#include <libheif/heif_security.h>
+#include <libheif/heif_encoding.h>
+#include <libheif/heif_decoding.h>
+#include <libheif/heif_context.h>
+#include <libheif/heif_image_handle.h>
+#include <libheif/heif_tiling.h>
 
 #endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_aux_images.cc 1.20.1-1/libheif/api/libheif/heif_aux_images.cc
--- 1.19.8-1/libheif/api/libheif/heif_aux_images.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_aux_images.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,341 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_aux_images.h"
+#include "api_structs.h"
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <algorithm>
+
+
+// ------------------------- depth images -------------------------
+
+
+int heif_image_handle_has_depth_image(const struct heif_image_handle* handle)
+{
+  return handle->image->get_depth_channel() != nullptr;
+}
+
+
+int heif_image_handle_get_number_of_depth_images(const struct heif_image_handle* handle)
+{
+  auto depth_image = handle->image->get_depth_channel();
+
+  if (depth_image) {
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+
+int heif_image_handle_get_list_of_depth_image_IDs(const struct heif_image_handle* handle,
+                                                  heif_item_id* ids, int count)
+{
+  auto depth_image = handle->image->get_depth_channel();
+
+  if (count == 0) {
+    return 0;
+  }
+
+  if (depth_image) {
+    ids[0] = depth_image->get_id();
+    return 1;
+  }
+  else {
+    return 0;
+  }
+}
+
+
+struct heif_error heif_image_handle_get_depth_image_handle(const struct heif_image_handle* handle,
+                                                           heif_item_id depth_id,
+                                                           struct heif_image_handle** out_depth_handle)
+{
+  if (out_depth_handle == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "NULL out_depth_handle passed to heif_image_handle_get_depth_image_handle()"};
+  }
+
+  auto depth_image = handle->image->get_depth_channel();
+
+  if (depth_image->get_id() != depth_id) {
+    *out_depth_handle = nullptr;
+
+    Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
+    return err.error_struct(handle->image.get());
+  }
+
+  *out_depth_handle = new heif_image_handle();
+  (*out_depth_handle)->image = depth_image;
+  (*out_depth_handle)->context = handle->context;
+
+  return Error::Ok.error_struct(handle->image.get());
+}
+
+
+void heif_depth_representation_info_free(const struct heif_depth_representation_info* info)
+{
+  delete info;
+}
+
+
+int heif_image_handle_get_depth_image_representation_info(const struct heif_image_handle* handle,
+                                                          heif_item_id depth_image_id,
+                                                          const struct heif_depth_representation_info** out)
+{
+  std::shared_ptr<ImageItem> depth_image;
+
+  if (out) {
+    if (handle->image->is_depth_channel()) {
+      // Because of an API bug before v1.11.0, the input handle may be the depth image (#422).
+      depth_image = handle->image;
+    }
+    else {
+      depth_image = handle->image->get_depth_channel();
+    }
+
+    if (depth_image->has_depth_representation_info()) {
+      auto info = new heif_depth_representation_info;
+      *info = depth_image->get_depth_representation_info();
+      *out = info;
+      return true;
+    }
+    else {
+      *out = nullptr;
+    }
+  }
+
+  return false;
+}
+
+
+// ------------------------- thumbnails -------------------------
+
+
+int heif_image_handle_get_number_of_thumbnails(const struct heif_image_handle* handle)
+{
+  return (int) handle->image->get_thumbnails().size();
+}
+
+
+int heif_image_handle_get_list_of_thumbnail_IDs(const struct heif_image_handle* handle,
+                                                heif_item_id* ids, int count)
+{
+  if (ids == nullptr) {
+    return 0;
+  }
+
+  auto thumbnails = handle->image->get_thumbnails();
+  int n = (int) std::min(count, (int) thumbnails.size());
+
+  for (int i = 0; i < n; i++) {
+    ids[i] = thumbnails[i]->get_id();
+  }
+
+  return n;
+}
+
+
+heif_error heif_image_handle_get_thumbnail(const struct heif_image_handle* handle,
+                                           heif_item_id thumbnail_id,
+                                           struct heif_image_handle** out_thumbnail_handle)
+{
+  if (!out_thumbnail_handle) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(handle->image.get());
+  }
+
+  auto thumbnails = handle->image->get_thumbnails();
+  for (const auto& thumb : thumbnails) {
+    if (thumb->get_id() == thumbnail_id) {
+      *out_thumbnail_handle = new heif_image_handle();
+      (*out_thumbnail_handle)->image = thumb;
+      (*out_thumbnail_handle)->context = handle->context;
+
+      return Error::Ok.error_struct(handle->image.get());
+    }
+  }
+
+  Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
+  return err.error_struct(handle->image.get());
+}
+
+
+struct heif_error heif_context_encode_thumbnail(struct heif_context* ctx,
+                                                const struct heif_image* image,
+                                                const struct heif_image_handle* image_handle,
+                                                struct heif_encoder* encoder,
+                                                const struct heif_encoding_options* input_options,
+                                                int bbox_size,
+                                                struct heif_image_handle** out_image_handle)
+{
+  heif_encoding_options* options = heif_encoding_options_alloc();
+  heif_encoding_options_copy(options, input_options);
+
+  auto encodingResult = ctx->context->encode_thumbnail(image->image,
+                                                       encoder,
+                                                       *options,
+                                                       bbox_size);
+  heif_encoding_options_free(options);
+
+  if (encodingResult.error != Error::Ok) {
+    return encodingResult.error.error_struct(ctx->context.get());
+  }
+
+  std::shared_ptr<ImageItem> thumbnail_image = *encodingResult;
+
+  if (!thumbnail_image) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Invalid_parameter_value,
+              "Thumbnail images must be smaller than the original image.");
+    return err.error_struct(ctx->context.get());
+  }
+
+  Error error = ctx->context->assign_thumbnail(image_handle->image, thumbnail_image);
+  if (error != Error::Ok) {
+    return error.error_struct(ctx->context.get());
+  }
+
+
+  if (out_image_handle) {
+    *out_image_handle = new heif_image_handle;
+    (*out_image_handle)->image = thumbnail_image;
+    (*out_image_handle)->context = ctx->context;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx,
+                                                const struct heif_image_handle* master_image,
+                                                const struct heif_image_handle* thumbnail_image)
+{
+  Error error = ctx->context->assign_thumbnail(thumbnail_image->image, master_image->image);
+  return error.error_struct(ctx->context.get());
+}
+
+
+// ------------------------- auxiliary images -------------------------
+
+
+int heif_image_handle_get_number_of_auxiliary_images(const struct heif_image_handle* handle,
+                                                     int include_alpha_image)
+{
+  return (int) handle->image->get_aux_images(include_alpha_image).size();
+}
+
+
+int heif_image_handle_get_list_of_auxiliary_image_IDs(const struct heif_image_handle* handle,
+                                                      int include_alpha_image,
+                                                      heif_item_id* ids, int count)
+{
+  if (ids == nullptr) {
+    return 0;
+  }
+
+  auto auxImages = handle->image->get_aux_images(include_alpha_image);
+  int n = (int) std::min(count, (int) auxImages.size());
+
+  for (int i = 0; i < n; i++) {
+    ids[i] = auxImages[i]->get_id();
+  }
+
+  return n;
+}
+
+
+struct heif_error heif_image_handle_get_auxiliary_type(const struct heif_image_handle* handle,
+                                                       const char** out_type)
+{
+  if (out_type == nullptr) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(handle->image.get());
+  }
+
+  *out_type = nullptr;
+
+  const auto& auxType = handle->image->get_aux_type();
+
+  char* buf = (char*) malloc(auxType.length() + 1);
+
+  if (buf == nullptr) {
+    return Error(heif_error_Memory_allocation_error,
+                 heif_suberror_Unspecified,
+                 "Failed to allocate memory for the type string").error_struct(handle->image.get());
+  }
+
+  strcpy(buf, auxType.c_str());
+  *out_type = buf;
+
+  return heif_error_success;
+}
+
+
+void heif_image_handle_release_auxiliary_type(const struct heif_image_handle* handle,
+                                              const char** out_type)
+{
+  if (out_type && *out_type) {
+    free((void*) *out_type);
+    *out_type = nullptr;
+  }
+}
+
+
+struct heif_error heif_image_handle_get_auxiliary_image_handle(const struct heif_image_handle* main_image_handle,
+                                                               heif_item_id auxiliary_id,
+                                                               struct heif_image_handle** out_auxiliary_handle)
+{
+  if (!out_auxiliary_handle) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(main_image_handle->image.get());
+  }
+
+  *out_auxiliary_handle = nullptr;
+
+  auto auxImages = main_image_handle->image->get_aux_images();
+  for (const auto& aux : auxImages) {
+    if (aux->get_id() == auxiliary_id) {
+      *out_auxiliary_handle = new heif_image_handle();
+      (*out_auxiliary_handle)->image = aux;
+      (*out_auxiliary_handle)->context = main_image_handle->context;
+
+      return Error::Ok.error_struct(main_image_handle->image.get());
+    }
+  }
+
+  Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced);
+  return err.error_struct(main_image_handle->image.get());
+}
+
+
+// ===================== DEPRECATED =====================
+
+// DEPRECATED (typo)
+void heif_image_handle_free_auxiliary_types(const struct heif_image_handle* handle,
+                                            const char** out_type)
+{
+  heif_image_handle_release_auxiliary_type(handle, out_type);
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_aux_images.h 1.20.1-1/libheif/api/libheif/heif_aux_images.h
--- 1.19.8-1/libheif/api/libheif/heif_aux_images.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_aux_images.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,182 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_AUX_IMAGES_H
+#define LIBHEIF_HEIF_AUX_IMAGES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_image.h>
+#include <libheif/heif_error.h>
+
+typedef struct heif_encoder heif_encoder;
+typedef struct heif_encoding_options heif_encoding_options;
+
+
+// ------------------------- depth images -------------------------
+
+LIBHEIF_API
+int heif_image_handle_has_depth_image(const heif_image_handle*);
+
+LIBHEIF_API
+int heif_image_handle_get_number_of_depth_images(const heif_image_handle* handle);
+
+LIBHEIF_API
+int heif_image_handle_get_list_of_depth_image_IDs(const heif_image_handle* handle,
+                                                  heif_item_id* ids, int count);
+
+LIBHEIF_API
+heif_error heif_image_handle_get_depth_image_handle(const heif_image_handle* handle,
+                                                    heif_item_id depth_image_id,
+                                                    heif_image_handle** out_depth_handle);
+
+
+enum heif_depth_representation_type {
+  heif_depth_representation_type_uniform_inverse_Z = 0,
+  heif_depth_representation_type_uniform_disparity = 1,
+  heif_depth_representation_type_uniform_Z = 2,
+  heif_depth_representation_type_nonuniform_disparity = 3
+};
+
+typedef struct heif_depth_representation_info {
+  uint8_t version;
+
+  // version 1 fields
+
+  uint8_t has_z_near;
+  uint8_t has_z_far;
+  uint8_t has_d_min;
+  uint8_t has_d_max;
+
+  double z_near;
+  double z_far;
+  double d_min;
+  double d_max;
+
+  enum heif_depth_representation_type depth_representation_type;
+  uint32_t disparity_reference_view;
+
+  uint32_t depth_nonlinear_representation_model_size;
+  uint8_t* depth_nonlinear_representation_model;
+
+  // version 2 fields below
+} heif_depth_representation_info;
+
+
+LIBHEIF_API
+void heif_depth_representation_info_free(const heif_depth_representation_info* info);
+
+// Returns true when there is depth_representation_info available
+// Note 1: depth_image_id is currently unused because we support only one depth channel per image, but
+// you should still provide the correct ID for future compatibility.
+// Note 2: Because of an API bug before v1.11.0, the function also works when 'handle' is the handle of the depth image.
+// However, you should pass the handle of the main image. Please adapt your code if needed.
+LIBHEIF_API
+int heif_image_handle_get_depth_image_representation_info(const heif_image_handle* handle,
+                                                          heif_item_id depth_image_id,
+                                                          const heif_depth_representation_info** out);
+
+
+
+// ------------------------- thumbnails -------------------------
+
+// List the number of thumbnails assigned to this image handle. Usually 0 or 1.
+LIBHEIF_API
+int heif_image_handle_get_number_of_thumbnails(const heif_image_handle* handle);
+
+LIBHEIF_API
+int heif_image_handle_get_list_of_thumbnail_IDs(const heif_image_handle* handle,
+                                                heif_item_id* ids, int count);
+
+// Get the image handle of a thumbnail image.
+LIBHEIF_API
+heif_error heif_image_handle_get_thumbnail(const heif_image_handle* main_image_handle,
+                                           heif_item_id thumbnail_id,
+                                           heif_image_handle** out_thumbnail_handle);
+
+
+// Encode the 'image' as a scaled down thumbnail image.
+// The image is scaled down to fit into a square area of width 'bbox_size'.
+// If the input image is already so small that it fits into this bounding box, no thumbnail
+// image is encoded and NULL is returned in 'out_thumb_image_handle'.
+// No error is returned in this case.
+// The encoded thumbnail is automatically assigned to the 'master_image_handle'. Hence, you
+// do not have to call heif_context_assign_thumbnail().
+LIBHEIF_API
+heif_error heif_context_encode_thumbnail(heif_context*,
+                                         const heif_image* image,
+                                         const heif_image_handle* master_image_handle,
+                                         heif_encoder* encoder,
+                                         const heif_encoding_options* options,
+                                         int bbox_size,
+                                         heif_image_handle** out_thumb_image_handle);
+
+// Assign 'thumbnail_image' as the thumbnail image of 'master_image'.
+LIBHEIF_API
+heif_error heif_context_assign_thumbnail(struct heif_context*,
+                                         const heif_image_handle* master_image,
+                                         const heif_image_handle* thumbnail_image);
+
+
+// ------------------------- auxiliary images -------------------------
+
+#define LIBHEIF_AUX_IMAGE_FILTER_OMIT_ALPHA (1UL<<1)
+#define LIBHEIF_AUX_IMAGE_FILTER_OMIT_DEPTH (2UL<<1)
+
+// List the number of auxiliary images assigned to this image handle.
+LIBHEIF_API
+int heif_image_handle_get_number_of_auxiliary_images(const heif_image_handle* handle,
+                                                     int aux_filter);
+
+LIBHEIF_API
+int heif_image_handle_get_list_of_auxiliary_image_IDs(const heif_image_handle* handle,
+                                                      int aux_filter,
+                                                      heif_item_id* ids, int count);
+
+// You are responsible to deallocate the returned buffer with heif_image_handle_release_auxiliary_type().
+LIBHEIF_API
+heif_error heif_image_handle_get_auxiliary_type(const heif_image_handle* handle,
+                                                const char** out_type);
+
+LIBHEIF_API
+void heif_image_handle_release_auxiliary_type(const heif_image_handle* handle,
+                                              const char** out_type);
+
+// Get the image handle of an auxiliary image.
+LIBHEIF_API
+heif_error heif_image_handle_get_auxiliary_image_handle(const heif_image_handle* main_image_handle,
+                                                        heif_item_id auxiliary_id,
+                                                        heif_image_handle** out_auxiliary_handle);
+
+// ===================== DEPRECATED =====================
+
+// DEPRECATED (because typo in function name). Use heif_image_handle_release_auxiliary_type() instead.
+LIBHEIF_API
+void heif_image_handle_free_auxiliary_types(const heif_image_handle* handle,
+                                            const char** out_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_brands.cc 1.20.1-1/libheif/api/libheif/heif_brands.cc
--- 1.19.8-1/libheif/api/libheif/heif_brands.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_brands.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,431 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_brands.h"
+#include "heif_error.h"
+#include "error.h"
+#include "common_utils.h"
+#include "bitstream.h"
+#include "box.h"
+
+#include <cstring>
+#include <set>
+#include <memory>
+
+
+
+heif_brand2 heif_read_main_brand(const uint8_t* data, int len)
+{
+  if (len < 12) {
+    return heif_unknown_brand;
+  }
+
+  return heif_fourcc_to_brand((char*) (data + 8));
+}
+
+
+heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len)
+{
+  if (len < 16) {
+    return heif_unknown_brand;
+  }
+  return heif_fourcc_to_brand((char*) (data + 12));
+}
+
+
+heif_brand2 heif_fourcc_to_brand(const char* fourcc_string)
+{
+  if (fourcc_string == nullptr || !fourcc_string[0] || !fourcc_string[1] || !fourcc_string[2] || !fourcc_string[3]) {
+    return 0;
+  }
+
+  return fourcc(fourcc_string);
+}
+
+void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc)
+{
+  if (out_fourcc) {
+    out_fourcc[0] = (char) ((brand >> 24) & 0xFF);
+    out_fourcc[1] = (char) ((brand >> 16) & 0xFF);
+    out_fourcc[2] = (char) ((brand >> 8) & 0xFF);
+    out_fourcc[3] = (char) ((brand >> 0) & 0xFF);
+  }
+}
+
+
+int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc)
+{
+  if (data == nullptr || len <= 0 || brand_fourcc == nullptr || !brand_fourcc[0] || !brand_fourcc[1] || !brand_fourcc[2] || !brand_fourcc[3]) {
+    return -1;
+  }
+
+  auto stream = std::make_shared<StreamReader_memory>(data, len, false);
+  BitstreamRange range(stream, len);
+
+  std::shared_ptr<Box> box;
+  Error err = Box::read(range, &box, heif_get_global_security_limits());
+  if (err) {
+    if (err.sub_error_code == heif_suberror_End_of_data) {
+      return -1;
+    }
+
+    return -2;
+  }
+
+  auto ftyp = std::dynamic_pointer_cast<Box_ftyp>(box);
+  if (!ftyp) {
+    return -2;
+  }
+
+  return ftyp->has_compatible_brand(fourcc(brand_fourcc)) ? 1 : 0;
+}
+
+
+struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size)
+{
+  if (data == nullptr || out_brands == nullptr || out_size == nullptr) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument"};
+  }
+
+  if (len <= 0) {
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "data length must be positive"};
+  }
+
+  auto stream = std::make_shared<StreamReader_memory>(data, len, false);
+  BitstreamRange range(stream, len);
+
+  std::shared_ptr<Box> box;
+  Error err = Box::read(range, &box, heif_get_global_security_limits());
+  if (err) {
+    if (err.sub_error_code == heif_suberror_End_of_data) {
+      return {err.error_code, err.sub_error_code, "insufficient input data"};
+    }
+
+    return {err.error_code, err.sub_error_code, "error reading ftyp box"};
+  }
+
+  auto ftyp = std::dynamic_pointer_cast<Box_ftyp>(box);
+  if (!ftyp) {
+    return {heif_error_Invalid_input, heif_suberror_No_ftyp_box, "input is not a ftyp box"};
+  }
+
+  auto brands = ftyp->list_brands();
+  size_t nBrands = brands.size();
+  *out_brands = (heif_brand2*) malloc(sizeof(heif_brand2) * nBrands);
+  *out_size = (int)nBrands;
+
+  for (size_t i = 0; i < nBrands; i++) {
+    (*out_brands)[i] = brands[i];
+  }
+
+  return heif_error_success;
+}
+
+
+void heif_free_list_of_compatible_brands(heif_brand2* brands_list)
+{
+  if (brands_list) {
+    free(brands_list);
+  }
+}
+
+
+enum class TriBool
+{
+  No, Yes, Unknown
+};
+
+static TriBool is_jpeg(const uint8_t* data, int len)
+{
+  if (len < 12) {
+    return TriBool::Unknown;
+  }
+
+  if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE0 &&
+      data[4] == 0x00 && data[5] == 0x10 && data[6] == 0x4A && data[7] == 0x46 &&
+      data[8] == 0x49 && data[9] == 0x46 && data[10] == 0x00 && data[11] == 0x01) {
+    return TriBool::Yes;
+  }
+  if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF && data[3] == 0xE1 &&
+      data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66 &&
+      data[10] == 0x00 && data[11] == 0x00) {
+    return TriBool::Yes;
+  }
+  else {
+    return TriBool::No;
+  }
+}
+
+
+static TriBool is_png(const uint8_t* data, int len)
+{
+  if (len < 8) {
+    return TriBool::Unknown;
+  }
+
+  if (data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47 &&
+      data[4] == 0x0D && data[5] == 0x0A && data[6] == 0x1A && data[7] == 0x0A) {
+    return TriBool::Yes;
+  }
+  else {
+    return TriBool::No;
+  }
+}
+
+
+const char* heif_get_file_mime_type(const uint8_t* data, int len)
+{
+  heif_brand mainBrand = heif_main_brand(data, len);
+
+  if (mainBrand == heif_heic ||
+      mainBrand == heif_heix ||
+      mainBrand == heif_heim ||
+      mainBrand == heif_heis) {
+    return "image/heic";
+  }
+  else if (mainBrand == heif_mif1) {
+    return "image/heif";
+  }
+  else if (mainBrand == heif_hevc ||
+           mainBrand == heif_hevx ||
+           mainBrand == heif_hevm ||
+           mainBrand == heif_hevs) {
+    return "image/heic-sequence";
+  }
+  else if (mainBrand == heif_msf1) {
+    return "image/heif-sequence";
+  }
+  else if (mainBrand == heif_avif) {
+    return "image/avif";
+  }
+  else if (mainBrand == heif_avis) {
+    return "image/avif-sequence";
+  }
+#if ENABLE_EXPERIMENTAL_MINI_FORMAT
+  else if (mainBrand == heif_brand2_mif3) {
+    heif_brand2 minorBrand = heif_read_minor_version_brand(data, len);
+    if (minorBrand == heif_brand2_avif) {
+      return "image/avif";
+    }
+    if (minorBrand == heif_brand2_heic ||
+        minorBrand == heif_brand2_heix ||
+        minorBrand == heif_brand2_heim ||
+        minorBrand == heif_brand2_heis) {
+      return "image/heic";
+    }
+    // There could be other options in here, like VVC or J2K
+    return "image/heif";
+  }
+#endif
+  else if (mainBrand == heif_j2ki) {
+    return "image/hej2k";
+  }
+  else if (mainBrand == heif_j2is) {
+    return "image/j2is";
+  }
+  else if (is_jpeg(data, len) == TriBool::Yes) {
+    return "image/jpeg";
+  }
+  else if (is_png(data, len) == TriBool::Yes) {
+    return "image/png";
+  }
+  else {
+    return "";
+  }
+}
+
+heif_filetype_result heif_check_filetype(const uint8_t* data, int len)
+{
+  if (len < 8) {
+    return heif_filetype_maybe;
+  }
+
+  if (data[4] != 'f' ||
+      data[5] != 't' ||
+      data[6] != 'y' ||
+      data[7] != 'p') {
+    return heif_filetype_no;
+  }
+
+  if (len >= 12) {
+    heif_brand2 brand = heif_read_main_brand(data, len);
+
+    if (brand == heif_brand2_heic) {
+      return heif_filetype_yes_supported;
+    }
+    else if (brand == heif_brand2_heix) {
+      return heif_filetype_yes_supported;
+    }
+    else if (brand == heif_brand2_avif) {
+      return heif_filetype_yes_supported;
+    }
+    else if (brand == heif_brand2_jpeg) {
+      return heif_filetype_yes_supported;
+    }
+    else if (brand == heif_brand2_j2ki) {
+      return heif_filetype_yes_supported;
+    }
+    else if (brand == heif_brand2_mif1) {
+      return heif_filetype_maybe;
+    }
+    else if (brand == heif_brand2_mif2) {
+      return heif_filetype_maybe;
+    }
+    else {
+      return heif_filetype_yes_unsupported;
+    }
+  }
+
+  return heif_filetype_maybe;
+}
+
+
+heif_error heif_has_compatible_filetype(const uint8_t* data, int len)
+{
+  // Get compatible brands first, because that does validity checks
+  heif_brand2* compatible_brands = nullptr;
+  int nBrands = 0;
+  struct heif_error err = heif_list_compatible_brands(data, len, &compatible_brands, &nBrands);
+  if (err.code) {
+    assert(compatible_brands == nullptr); // NOLINT(clang-analyzer-unix.Malloc)
+    return err;
+  }
+
+  heif_brand2 main_brand = heif_read_main_brand(data, len);
+
+  std::set<heif_brand2> supported_brands{
+      heif_brand2_avif,
+      heif_brand2_heic,
+      heif_brand2_heix,
+      heif_brand2_j2ki,
+      heif_brand2_jpeg,
+      heif_brand2_miaf,
+      heif_brand2_mif1,
+      heif_brand2_mif2
+#if ENABLE_EXPERIMENTAL_MINI_FORMAT
+      , heif_brand2_mif3
+#endif
+      ,heif_brand2_msf1
+  };
+
+  auto it = supported_brands.find(main_brand);
+  if (it != supported_brands.end()) {
+    heif_free_list_of_compatible_brands(compatible_brands);
+    return heif_error_success;
+  }
+
+  for (int i = 0; i < nBrands; i++) {
+    heif_brand2 compatible_brand = compatible_brands[i];
+    it = supported_brands.find(compatible_brand);
+    if (it != supported_brands.end()) {
+      heif_free_list_of_compatible_brands(compatible_brands);
+      return heif_error_success;
+    }
+  }
+  heif_free_list_of_compatible_brands(compatible_brands);
+  return {heif_error_Invalid_input, heif_suberror_Unsupported_image_type, "No supported brands found."};;
+}
+
+
+int heif_check_jpeg_filetype(const uint8_t* data, int len)
+{
+  if (len < 4 || data == nullptr) {
+    return -1;
+  }
+
+  return (data[0] == 0xFF &&
+	  data[1] == 0xD8 &&
+	  data[2] == 0xFF &&
+	  (data[3] & 0xF0) == 0xE0);
+}
+
+
+static heif_brand heif_fourcc_to_brand_enum(const char* fourcc)
+{
+  if (fourcc == nullptr || !fourcc[0] || !fourcc[1] || !fourcc[2] || !fourcc[3]) {
+    return heif_unknown_brand;
+  }
+
+  char brand[5];
+  brand[0] = fourcc[0];
+  brand[1] = fourcc[1];
+  brand[2] = fourcc[2];
+  brand[3] = fourcc[3];
+  brand[4] = 0;
+
+  if (strcmp(brand, "heic") == 0) {
+    return heif_heic;
+  }
+  else if (strcmp(brand, "heix") == 0) {
+    return heif_heix;
+  }
+  else if (strcmp(brand, "hevc") == 0) {
+    return heif_hevc;
+  }
+  else if (strcmp(brand, "hevx") == 0) {
+    return heif_hevx;
+  }
+  else if (strcmp(brand, "heim") == 0) {
+    return heif_heim;
+  }
+  else if (strcmp(brand, "heis") == 0) {
+    return heif_heis;
+  }
+  else if (strcmp(brand, "hevm") == 0) {
+    return heif_hevm;
+  }
+  else if (strcmp(brand, "hevs") == 0) {
+    return heif_hevs;
+  }
+  else if (strcmp(brand, "mif1") == 0) {
+    return heif_mif1;
+  }
+  else if (strcmp(brand, "msf1") == 0) {
+    return heif_msf1;
+  }
+  else if (strcmp(brand, "avif") == 0) {
+    return heif_avif;
+  }
+  else if (strcmp(brand, "avis") == 0) {
+    return heif_avis;
+  }
+  else if (strcmp(brand, "vvic") == 0) {
+    return heif_vvic;
+  }
+  else if (strcmp(brand, "j2ki") == 0) {
+    return heif_j2ki;
+  }
+  else if (strcmp(brand, "j2is") == 0) {
+    return heif_j2is;
+  }
+  else {
+    return heif_unknown_brand;
+  }
+}
+
+
+enum heif_brand heif_main_brand(const uint8_t* data, int len)
+{
+  if (len < 12) {
+    return heif_unknown_brand;
+  }
+
+  return heif_fourcc_to_brand_enum((char*) (data + 8));
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_brands.h 1.20.1-1/libheif/api/libheif/heif_brands.h
--- 1.19.8-1/libheif/api/libheif/heif_brands.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_brands.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,373 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_BRANDS_H
+#define LIBHEIF_HEIF_BRANDS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <libheif/heif_library.h>
+
+
+
+typedef uint32_t heif_brand2;
+
+/**
+ * HEVC image (`heic`) brand.
+ *
+ * Image conforms to HEVC (H.265) Main or Main Still profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.1.
+ */
+#define heif_brand2_heic   heif_fourcc('h','e','i','c')
+
+/**
+ * HEVC image (`heix`) brand.
+ *
+ * Image conforms to HEVC (H.265) Main 10 profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.1.
+ */
+#define heif_brand2_heix   heif_fourcc('h','e','i','x')
+
+/**
+ * HEVC image sequence (`hevc`) brand.
+ *
+ * Image sequence conforms to HEVC (H.265) Main profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.2.
+ */
+#define heif_brand2_hevc   heif_fourcc('h','e','v','c')
+
+/**
+ * HEVC image sequence (`hevx`) brand.
+ *
+ * Image sequence conforms to HEVC (H.265) Main 10 profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.2.
+ */
+#define heif_brand2_hevx   heif_fourcc('h','e','v','x')
+
+/**
+ * HEVC layered image (`heim`) brand.
+ *
+ * Image layers conform to HEVC (H.265) Main or Multiview Main profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.3.
+ */
+#define heif_brand2_heim   heif_fourcc('h','e','i','m')
+
+/**
+ * HEVC layered image (`heis`) brand.
+ *
+ * Image layers conform to HEVC (H.265) Main, Main 10, Scalable Main
+ * or Scalable Main 10 profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.3.
+ */
+#define heif_brand2_heis   heif_fourcc('h','e','i','s')
+
+/**
+ * HEVC layered image sequence (`hevm`) brand.
+ *
+ * Image sequence layers conform to HEVC (H.265) Main or Multiview Main profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.4.
+ */
+#define heif_brand2_hevm   heif_fourcc('h','e','v','m')
+
+/**
+ * HEVC layered image sequence (`hevs`) brand.
+ *
+ * Image sequence layers conform to HEVC (H.265) Main, Main 10, Scalable Main
+ * or Scalable Main 10 profile.
+ *
+ * See ISO/IEC 23008-12:2022 Section B.4.4.
+ */
+#define heif_brand2_hevs   heif_fourcc('h','e','v','s')
+
+/**
+ * AV1 image (`avif`) brand.
+ *
+ * See https://aomediacodec.github.io/av1-avif/#image-and-image-collection-brand
+ */
+#define heif_brand2_avif   heif_fourcc('a','v','i','f')
+
+/**
+ * AV1 image sequence (`avis`) brand.
+ *
+ * See https://aomediacodec.github.io/av1-avif/#image-sequence-brand
+ */
+#define heif_brand2_avis   heif_fourcc('a','v','i','s') // AVIF sequence
+
+/**
+ * HEIF image structural brand (`mif1`).
+ *
+ * This does not imply a specific coding algorithm.
+ *
+ * See ISO/IEC 23008-12:2022 Section 10.2.2.
+ */
+#define heif_brand2_mif1   heif_fourcc('m','i','f','1')
+
+/**
+ * HEIF image structural brand (`mif2`).
+ *
+ * This does not imply a specific coding algorithm. `mif2` extends
+ * the requirements of `mif1` to include the `rref` and `iscl` item
+ * properties.
+ *
+ * See ISO/IEC 23008-12:2022 Section 10.2.3.
+ */
+#define heif_brand2_mif2   heif_fourcc('m','i','f','2')
+
+/**
+ * HEIF image structural brand (`mif3`).
+ *
+ * This indicates the low-overhead (ftyp+mini) structure.
+ */
+#define heif_brand2_mif3   heif_fourcc('m','i','f','3')
+
+/**
+ * HEIF image sequence structural brand (`msf1`).
+ *
+ * This does not imply a specific coding algorithm.
+ *
+ * See ISO/IEC 23008-12:2022 Section 10.3.1.
+ */
+#define heif_brand2_msf1   heif_fourcc('m','s','f','1')
+
+/**
+ * VVC image (`vvic`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section L.4.1.
+ */
+#define heif_brand2_vvic   heif_fourcc('v','v','i','c')
+
+/**
+ * VVC image sequence (`vvis`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section L.4.2.
+ */
+#define heif_brand2_vvis   heif_fourcc('v','v','i','s')
+
+/**
+ * EVC baseline image (`evbi`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section M.4.1.
+ */
+#define heif_brand2_evbi   heif_fourcc('e','v','b','i')
+
+/**
+ * EVC main profile image (`evmi`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section M.4.2.
+ */
+#define heif_brand2_evmi   heif_fourcc('e','v','m','i')
+
+/**
+ * EVC baseline image sequence (`evbs`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section M.4.3.
+ */
+#define heif_brand2_evbs   heif_fourcc('e','v','b','s')
+
+/**
+ * EVC main profile image sequence (`evms`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Section M.4.4.
+ */
+#define heif_brand2_evms   heif_fourcc('e','v','m','s')
+
+/**
+ * JPEG image (`jpeg`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Annex H.4
+ */
+#define heif_brand2_jpeg   heif_fourcc('j','p','e','g')
+
+/**
+ * JPEG image sequence (`jpgs`) brand.
+ *
+ * See ISO/IEC 23008-12:2022 Annex H.5
+ */
+#define heif_brand2_jpgs   heif_fourcc('j','p','g','s')
+
+/**
+ * JPEG 2000 image (`j2ki`) brand.
+ *
+ * See ISO/IEC 15444-16:2021 Section 6.5 
+ */
+#define heif_brand2_j2ki   heif_fourcc('j','2','k','i')
+
+/**
+ * JPEG 2000 image sequence (`j2is`) brand.
+ *
+ * See ISO/IEC 15444-16:2021 Section 7.6
+ */
+#define heif_brand2_j2is   heif_fourcc('j','2','i','s')
+
+/**
+ * Multi-image application format (MIAF) brand.
+ *
+ * This is HEIF with additional constraints for interoperability.
+ *
+ * See ISO/IEC 23000-22.
+ */
+#define heif_brand2_miaf   heif_fourcc('m','i','a','f')
+
+/**
+ * Single picture file brand.
+ *
+ * This is a compatible brand indicating the file contains a single intra-coded picture.
+ *
+ * See ISO/IEC 23008-12:2022 Section 10.2.5.
+*/
+#define heif_brand2_1pic   heif_fourcc('1','p','i','c')
+
+// H.264
+#define heif_brand2_avci   heif_fourcc('a','v','c','i')
+#define heif_brand2_avcs   heif_fourcc('a','v','c','s')
+
+#define heif_brand2_iso8   heif_fourcc('i','s','o','8')
+
+// input data should be at least 12 bytes
+LIBHEIF_API
+heif_brand2 heif_read_main_brand(const uint8_t* data, int len);
+
+// input data should be at least 16 bytes
+LIBHEIF_API
+heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len);
+
+// 'brand_fourcc' must be 4 character long, but need not be 0-terminated
+LIBHEIF_API
+heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc);
+
+// the output buffer must be at least 4 bytes long
+LIBHEIF_API
+void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc);
+
+// 'brand_fourcc' must be 4 character long, but need not be 0-terminated
+// returns 1 if file includes the brand, and 0 if it does not
+// returns -1 if the provided data is not sufficient
+//            (you should input at least as many bytes as indicated in the first 4 bytes of the file, usually ~50 bytes will do)
+// returns -2 on other errors
+LIBHEIF_API
+int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fourcc);
+
+// Returns an array of compatible brands. The array is allocated by this function and has to be freed with 'heif_free_list_of_compatible_brands()'.
+// The number of entries is returned in out_size.
+LIBHEIF_API
+struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif_brand2** out_brands, int* out_size);
+
+LIBHEIF_API
+void heif_free_list_of_compatible_brands(heif_brand2* brands_list);
+
+
+
+// Returns one of these MIME types:
+// - image/heic           HEIF file using h265 compression
+// - image/heif           HEIF file using any other compression
+// - image/heic-sequence  HEIF image sequence using h265 compression
+// - image/heif-sequence  HEIF image sequence using any other compression
+// - image/avif           AVIF image
+// - image/avif-sequence  AVIF sequence
+// - image/jpeg    JPEG image
+// - image/png     PNG image
+// If the format could not be detected, an empty string is returned.
+//
+// Provide at least 12 bytes of input. With less input, its format might not
+// be detected. You may also provide more input to increase detection accuracy.
+//
+// Note that JPEG and PNG images cannot be decoded by libheif even though the
+// formats are detected by this function.
+LIBHEIF_API
+const char* heif_get_file_mime_type(const uint8_t* data, int len);
+
+
+// ========================= file type check ======================
+
+enum heif_filetype_result
+{
+  heif_filetype_no,
+  heif_filetype_yes_supported,   // it is heif and can be read by libheif
+  heif_filetype_yes_unsupported, // it is heif, but cannot be read by libheif
+  heif_filetype_maybe // not sure whether it is an heif, try detection with more input data
+};
+
+// input data should be at least 12 bytes
+LIBHEIF_API
+enum heif_filetype_result heif_check_filetype(const uint8_t* data, int len);
+
+/**
+ * Check the filetype box content for a supported file type.
+ *
+ * <p>The data is assumed to start from the start of the `ftyp` box.
+ *
+ * <p>This function checks the compatible brands.
+ *
+ * @returns heif_error_ok if a supported brand is found, or other error if not.
+ */
+LIBHEIF_API
+heif_error heif_has_compatible_filetype(const uint8_t* data, int len);
+
+LIBHEIF_API
+int heif_check_jpeg_filetype(const uint8_t* data, int len);
+
+
+// ===================== DEPRECATED =====================
+
+// DEPRECATED, use heif_brand2 and the heif_brand2_* constants instead
+enum heif_brand
+{
+  heif_unknown_brand,
+  heif_heic, // HEIF image with h265
+  heif_heix, // 10bit images, or anything that uses h265 with range extension
+  heif_hevc, heif_hevx, // brands for image sequences
+  heif_heim, // multiview
+  heif_heis, // scalable
+  heif_hevm, // multiview sequence
+  heif_hevs, // scalable sequence
+  heif_mif1, // image, any coding algorithm
+  heif_msf1, // sequence, any coding algorithm
+  heif_avif, // HEIF image with AV1
+  heif_avis,
+  heif_vvic, // VVC image
+  heif_vvis, // VVC sequence
+  heif_evbi, // EVC image
+  heif_evbs, // EVC sequence
+  heif_j2ki, // JPEG2000 image
+  heif_j2is, // JPEG2000 image sequence
+};
+
+// input data should be at least 12 bytes
+// DEPRECATED, use heif_read_main_brand() instead
+LIBHEIF_API
+enum heif_brand heif_main_brand(const uint8_t* data, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_color.cc 1.20.1-1/libheif/api/libheif/heif_color.cc
--- 1.19.8-1/libheif/api/libheif/heif_color.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_color.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,559 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libheif/heif_color.h>
+
+#include "common_utils.h"
+#include <cstdint>
+#include "heif.h"
+#include "pixelimage.h"
+#include "api_structs.h"
+#include "error.h"
+#include <set>
+#include <limits>
+
+#include <algorithm>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include <cstring>
+#include <array>
+
+
+static struct heif_error error_null_parameter = {heif_error_Usage_error,
+                                                 heif_suberror_Null_pointer_argument,
+                                                 "NULL passed"};
+
+
+void heif_color_conversion_options_set_defaults(struct heif_color_conversion_options* options)
+{
+  options->version = 1;
+#if HAVE_LIBSHARPYUV
+  options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_sharp_yuv;
+#else
+  options->preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
+#endif
+
+  options->preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
+  options->only_use_preferred_chroma_algorithm = true;
+}
+
+
+static void fill_default_color_conversion_options_ext(heif_color_conversion_options_ext& options)
+{
+  options.version = 1;
+  options.alpha_composition_mode = heif_alpha_composition_mode_none;
+  options.background_red = options.background_green = options.background_blue = 0xFFFF;
+  options.secondary_background_red = options.secondary_background_green = options.secondary_background_blue = 0xCCCC;
+  options.checkerboard_square_size = 16;
+}
+
+
+struct heif_color_conversion_options_ext* heif_color_conversion_options_ext_alloc()
+{
+  auto options = new heif_color_conversion_options_ext;
+
+  fill_default_color_conversion_options_ext(*options);
+
+  return options;
+}
+
+
+void heif_color_conversion_options_ext_copy(struct heif_color_conversion_options_ext* dst,
+                                            const struct heif_color_conversion_options_ext* src)
+{
+  if (src == nullptr) {
+    return;
+  }
+
+  int min_version = std::min(dst->version, src->version);
+
+  switch (min_version) {
+    case 1:
+      dst->alpha_composition_mode = src->alpha_composition_mode;
+      dst->background_red = src->background_red;
+      dst->background_green = src->background_green;
+      dst->background_blue = src->background_blue;
+      dst->secondary_background_red = src->secondary_background_red;
+      dst->secondary_background_green = src->secondary_background_green;
+      dst->secondary_background_blue = src->secondary_background_blue;
+      dst->checkerboard_square_size = src->checkerboard_square_size;
+  }
+}
+
+
+void heif_color_conversion_options_ext_free(struct heif_color_conversion_options_ext* options)
+{
+  delete options;
+}
+
+
+heif_color_profile_type heif_image_handle_get_color_profile_type(const struct heif_image_handle* handle)
+{
+  auto profile_icc = handle->image->get_color_profile_icc();
+  if (profile_icc) {
+    return (heif_color_profile_type) profile_icc->get_type();
+  }
+
+  auto profile_nclx = handle->image->get_color_profile_nclx();
+  if (profile_nclx) {
+    return (heif_color_profile_type) profile_nclx->get_type();
+  }
+  else {
+    return heif_color_profile_type_not_present;
+  }
+}
+
+
+size_t heif_image_handle_get_raw_color_profile_size(const struct heif_image_handle* handle)
+{
+  auto profile_icc = handle->image->get_color_profile_icc();
+  if (profile_icc) {
+    return profile_icc->get_data().size();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+struct heif_error heif_image_handle_get_raw_color_profile(const struct heif_image_handle* handle,
+                                                          void* out_data)
+{
+  if (out_data == nullptr) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(handle->image.get());
+  }
+
+  auto raw_profile = handle->image->get_color_profile_icc();
+  if (raw_profile) {
+    memcpy(out_data,
+           raw_profile->get_data().data(),
+           raw_profile->get_data().size());
+  }
+  else {
+    Error err(heif_error_Color_profile_does_not_exist,
+              heif_suberror_Unspecified);
+    return err.error_struct(handle->image.get());
+  }
+
+  return Error::Ok.error_struct(handle->image.get());
+}
+
+
+static const std::set<typename std::underlying_type<heif_color_primaries>::type> known_color_primaries{
+    heif_color_primaries_ITU_R_BT_709_5,
+    heif_color_primaries_unspecified,
+    heif_color_primaries_ITU_R_BT_470_6_System_M,
+    heif_color_primaries_ITU_R_BT_470_6_System_B_G,
+    heif_color_primaries_ITU_R_BT_601_6,
+    heif_color_primaries_SMPTE_240M,
+    heif_color_primaries_generic_film,
+    heif_color_primaries_ITU_R_BT_2020_2_and_2100_0,
+    heif_color_primaries_SMPTE_ST_428_1,
+    heif_color_primaries_SMPTE_RP_431_2,
+    heif_color_primaries_SMPTE_EG_432_1,
+    heif_color_primaries_EBU_Tech_3213_E,
+};
+
+
+struct heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp)
+{
+  if (static_cast<std::underlying_type<heif_color_primaries>::type>(cp) > std::numeric_limits<std::underlying_type<heif_color_primaries>::type>::max()) {
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr);
+  }
+
+  auto n = static_cast<typename std::underlying_type<heif_color_primaries>::type>(cp);
+  if (known_color_primaries.find(n) != known_color_primaries.end()) {
+    nclx->color_primaries = static_cast<heif_color_primaries>(n);
+  }
+  else {
+    nclx->color_primaries = heif_color_primaries_unspecified;
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr);
+  }
+
+  return Error::Ok.error_struct(nullptr);
+}
+
+
+static const std::set<typename std::underlying_type<heif_transfer_characteristics>::type> known_transfer_characteristics{
+    heif_transfer_characteristic_ITU_R_BT_709_5,
+    heif_transfer_characteristic_unspecified,
+    heif_transfer_characteristic_ITU_R_BT_470_6_System_M,
+    heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G,
+    heif_transfer_characteristic_ITU_R_BT_601_6,
+    heif_transfer_characteristic_SMPTE_240M,
+    heif_transfer_characteristic_linear,
+    heif_transfer_characteristic_logarithmic_100,
+    heif_transfer_characteristic_logarithmic_100_sqrt10,
+    heif_transfer_characteristic_IEC_61966_2_4,
+    heif_transfer_characteristic_ITU_R_BT_1361,
+    heif_transfer_characteristic_IEC_61966_2_1,
+    heif_transfer_characteristic_ITU_R_BT_2020_2_10bit,
+    heif_transfer_characteristic_ITU_R_BT_2020_2_12bit,
+    heif_transfer_characteristic_ITU_R_BT_2100_0_PQ,
+    heif_transfer_characteristic_SMPTE_ST_428_1,
+    heif_transfer_characteristic_ITU_R_BT_2100_0_HLG
+};
+
+
+struct heif_error heif_nclx_color_profile_set_transfer_characteristics(struct heif_color_profile_nclx* nclx, uint16_t tc)
+{
+  if (static_cast<std::underlying_type<heif_color_primaries>::type>(tc) > std::numeric_limits<std::underlying_type<heif_transfer_characteristics>::type>::max()) {
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr);
+  }
+
+  auto n = static_cast<typename std::underlying_type<heif_transfer_characteristics>::type>(tc);
+  if (known_transfer_characteristics.find(n) != known_transfer_characteristics.end()) {
+    nclx->transfer_characteristics = static_cast<heif_transfer_characteristics>(n);
+  }
+  else {
+    nclx->transfer_characteristics = heif_transfer_characteristic_unspecified;
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr);
+  }
+
+  return Error::Ok.error_struct(nullptr);
+}
+
+
+static const std::set<typename std::underlying_type<heif_matrix_coefficients>::type> known_matrix_coefficients{
+    heif_matrix_coefficients_RGB_GBR,
+    heif_matrix_coefficients_ITU_R_BT_709_5,
+    heif_matrix_coefficients_unspecified,
+    heif_matrix_coefficients_US_FCC_T47,
+    heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G,
+    heif_matrix_coefficients_ITU_R_BT_601_6,
+    heif_matrix_coefficients_SMPTE_240M,
+    heif_matrix_coefficients_YCgCo,
+    heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance,
+    heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance,
+    heif_matrix_coefficients_SMPTE_ST_2085,
+    heif_matrix_coefficients_chromaticity_derived_non_constant_luminance,
+    heif_matrix_coefficients_chromaticity_derived_constant_luminance,
+    heif_matrix_coefficients_ICtCp
+};
+
+
+struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t mc)
+{
+  if (static_cast<std::underlying_type<heif_color_primaries>::type>(mc) > std::numeric_limits<std::underlying_type<heif_matrix_coefficients>::type>::max()) {
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr);
+  }
+
+  auto n = static_cast<typename std::underlying_type<heif_matrix_coefficients>::type>(mc);
+  if (known_matrix_coefficients.find(n) != known_matrix_coefficients.end()) {
+    nclx->matrix_coefficients = static_cast<heif_matrix_coefficients>(n);;
+  }
+  else {
+    nclx->matrix_coefficients = heif_matrix_coefficients_unspecified;
+    return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr);
+  }
+
+  return Error::Ok.error_struct(nullptr);
+}
+
+
+struct heif_error heif_image_handle_get_nclx_color_profile(const struct heif_image_handle* handle,
+                                                           struct heif_color_profile_nclx** out_data)
+{
+  if (!out_data) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(handle->image.get());
+  }
+
+  auto nclx_profile = handle->image->get_color_profile_nclx();
+  if (!nclx_profile) {
+    Error err(heif_error_Color_profile_does_not_exist,
+              heif_suberror_Unspecified);
+    return err.error_struct(handle->image.get());
+  }
+
+  Error err = nclx_profile->get_nclx_color_profile(out_data);
+
+  return err.error_struct(handle->image.get());
+}
+
+
+struct heif_color_profile_nclx* heif_nclx_color_profile_alloc()
+{
+  return color_profile_nclx::alloc_nclx_color_profile();
+}
+
+
+void heif_nclx_color_profile_free(struct heif_color_profile_nclx* nclx_profile)
+{
+  color_profile_nclx::free_nclx_color_profile(nclx_profile);
+}
+
+
+enum heif_color_profile_type heif_image_get_color_profile_type(const struct heif_image* image)
+{
+  std::shared_ptr<const color_profile> profile;
+
+  profile = image->image->get_color_profile_icc();
+  if (!profile) {
+    profile = image->image->get_color_profile_nclx();
+  }
+
+  if (!profile) {
+    return heif_color_profile_type_not_present;
+  }
+  else {
+    return (heif_color_profile_type) profile->get_type();
+  }
+}
+
+
+size_t heif_image_get_raw_color_profile_size(const struct heif_image* image)
+{
+  auto raw_profile = image->image->get_color_profile_icc();
+  if (raw_profile) {
+    return raw_profile->get_data().size();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+struct heif_error heif_image_get_raw_color_profile(const struct heif_image* image,
+                                                   void* out_data)
+{
+  if (out_data == nullptr) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(image->image.get());
+  }
+
+  auto raw_profile = image->image->get_color_profile_icc();
+  if (raw_profile) {
+    memcpy(out_data,
+           raw_profile->get_data().data(),
+           raw_profile->get_data().size());
+  }
+  else {
+    Error err(heif_error_Color_profile_does_not_exist,
+              heif_suberror_Unspecified);
+    return err.error_struct(image->image.get());
+  }
+
+  return Error::Ok.error_struct(image->image.get());
+}
+
+
+struct heif_error heif_image_get_nclx_color_profile(const struct heif_image* image,
+                                                    struct heif_color_profile_nclx** out_data)
+{
+  if (!out_data) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(image->image.get());
+  }
+
+  auto nclx_profile = image->image->get_color_profile_nclx();
+
+  if (!nclx_profile) {
+    Error err(heif_error_Color_profile_does_not_exist,
+              heif_suberror_Unspecified);
+    return err.error_struct(image->image.get());
+  }
+
+  Error err = nclx_profile->get_nclx_color_profile(out_data);
+
+  return err.error_struct(image->image.get());
+}
+
+
+struct heif_error heif_image_set_raw_color_profile(struct heif_image* image,
+                                                   const char* color_profile_type_fourcc,
+                                                   const void* profile_data,
+                                                   const size_t profile_size)
+{
+  if (strlen(color_profile_type_fourcc) != 4) {
+    heif_error err = {heif_error_Usage_error,
+                      heif_suberror_Unspecified,
+                      "Invalid color_profile_type (must be 4 characters)"};
+    return err;
+  }
+
+  uint32_t color_profile_type = fourcc(color_profile_type_fourcc);
+
+  std::vector<uint8_t> data;
+  data.insert(data.end(),
+              (const uint8_t*) profile_data,
+              (const uint8_t*) profile_data + profile_size);
+
+  auto color_profile = std::make_shared<color_profile_raw>(color_profile_type, data);
+
+  image->image->set_color_profile_icc(color_profile);
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_image_set_nclx_color_profile(struct heif_image* image,
+                                                    const struct heif_color_profile_nclx* color_profile)
+{
+  auto nclx = std::make_shared<color_profile_nclx>();
+
+  nclx->set_colour_primaries(color_profile->color_primaries);
+  nclx->set_transfer_characteristics(color_profile->transfer_characteristics);
+  nclx->set_matrix_coefficients(color_profile->matrix_coefficients);
+  nclx->set_full_range_flag(color_profile->full_range_flag);
+
+  image->image->set_color_profile_nclx(nclx);
+
+  return heif_error_success;
+}
+
+
+// --- content light level ---
+
+int heif_image_has_content_light_level(const struct heif_image* image)
+{
+  return image->image->has_clli();
+}
+
+
+void heif_image_get_content_light_level(const struct heif_image* image, struct heif_content_light_level* out)
+{
+  if (out) {
+    *out = image->image->get_clli();
+  }
+}
+
+
+int heif_image_handle_get_content_light_level(const struct heif_image_handle* handle, struct heif_content_light_level* out)
+{
+  auto clli = handle->image->get_property<Box_clli>();
+  if (out && clli) {
+    *out = clli->clli;
+  }
+
+  return clli ? 1 : 0;
+}
+
+
+void heif_image_set_content_light_level(const struct heif_image* image, const struct heif_content_light_level* in)
+{
+  if (in == nullptr) {
+    return;
+  }
+
+  image->image->set_clli(*in);
+}
+
+
+// --- mastering display colour volume ---
+
+
+int heif_image_has_mastering_display_colour_volume(const struct heif_image* image)
+{
+  return image->image->has_mdcv();
+}
+
+
+void heif_image_get_mastering_display_colour_volume(const struct heif_image* image, struct heif_mastering_display_colour_volume* out)
+{
+  *out = image->image->get_mdcv();
+}
+
+
+int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle* handle, struct heif_mastering_display_colour_volume* out)
+{
+  auto mdcv = handle->image->get_property<Box_mdcv>();
+  if (out && mdcv) {
+    *out = mdcv->mdcv;
+  }
+
+  return mdcv ? 1 : 0;
+}
+
+
+void heif_image_set_mastering_display_colour_volume(const struct heif_image* image, const struct heif_mastering_display_colour_volume* in)
+{
+  if (in == nullptr) {
+    return;
+  }
+
+  image->image->set_mdcv(*in);
+}
+
+
+float mdcv_coord_decode_x(uint16_t coord)
+{
+  // check for unspecified value
+  if (coord < 5 || coord > 37000) {
+    return 0.0f;
+  }
+
+  return (float) (coord * 0.00002);
+}
+
+
+float mdcv_coord_decode_y(uint16_t coord)
+{
+  // check for unspecified value
+  if (coord < 5 || coord > 42000) {
+    return 0.0f;
+  }
+
+  return (float) (coord * 0.00002);
+}
+
+
+struct heif_error heif_mastering_display_colour_volume_decode(const struct heif_mastering_display_colour_volume* in,
+                                                              struct heif_decoded_mastering_display_colour_volume* out)
+{
+  if (in == nullptr || out == nullptr) {
+    return error_null_parameter;
+  }
+
+  for (int c = 0; c < 3; c++) {
+    out->display_primaries_x[c] = mdcv_coord_decode_x(in->display_primaries_x[c]);
+    out->display_primaries_y[c] = mdcv_coord_decode_y(in->display_primaries_y[c]);
+  }
+
+  out->white_point_x = mdcv_coord_decode_x(in->white_point_x);
+  out->white_point_y = mdcv_coord_decode_y(in->white_point_y);
+
+  if (in->max_display_mastering_luminance < 50000 || in->max_display_mastering_luminance > 100000000) {
+    out->max_display_mastering_luminance = 0;
+  }
+  else {
+    out->max_display_mastering_luminance = in->max_display_mastering_luminance * 0.0001;
+  }
+
+  if (in->min_display_mastering_luminance < 1 || in->min_display_mastering_luminance > 50000) {
+    out->min_display_mastering_luminance = 0;
+  }
+  else {
+    out->min_display_mastering_luminance = in->min_display_mastering_luminance * 0.0001;
+  }
+
+  return heif_error_success;
+}
+
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_color.h 1.20.1-1/libheif/api/libheif/heif_color.h
--- 1.19.8-1/libheif/api/libheif/heif_color.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_color.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,357 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_COLOR_H
+#define LIBHEIF_HEIF_COLOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+
+// forward declaration from other header files
+typedef struct heif_image heif_image;
+
+
+enum heif_chroma_downsampling_algorithm
+{
+  heif_chroma_downsampling_nearest_neighbor = 1,
+  heif_chroma_downsampling_average = 2,
+
+  // Combine with 'heif_chroma_upsampling_bilinear' for best quality.
+  // Makes edges look sharper when using YUV 420 with bilinear chroma upsampling.
+  heif_chroma_downsampling_sharp_yuv = 3
+};
+
+enum heif_chroma_upsampling_algorithm
+{
+  heif_chroma_upsampling_nearest_neighbor = 1,
+  heif_chroma_upsampling_bilinear = 2
+};
+
+
+typedef struct heif_color_conversion_options
+{
+  // 'version' must be 1.
+  uint8_t version;
+
+  // --- version 1 options
+
+  enum heif_chroma_downsampling_algorithm preferred_chroma_downsampling_algorithm;
+  enum heif_chroma_upsampling_algorithm preferred_chroma_upsampling_algorithm;
+
+  // When set to 'false' libheif may also use a different algorithm if the preferred one is not available
+  // or using a different algorithm is computationally less complex. Note that currently (v1.17.0) this
+  // means that for RGB input it will usually choose nearest-neighbor sampling because this is computationally
+  // the simplest.
+  // Set this field to 'true' if you want to make sure that the specified algorithm is used even
+  // at the cost of slightly higher computation times.
+  uint8_t only_use_preferred_chroma_algorithm;
+
+  // --- Note that we cannot extend this struct because it is embedded in
+  //     other structs (heif_decoding_options and heif_encoding_options).
+} heif_color_conversion_options;
+
+
+enum heif_alpha_composition_mode
+{
+  heif_alpha_composition_mode_none,
+  heif_alpha_composition_mode_solid_color,
+  heif_alpha_composition_mode_checkerboard,
+};
+
+
+typedef struct heif_color_conversion_options_ext
+{
+  uint8_t version;
+
+  // --- version 1 options
+
+  enum heif_alpha_composition_mode alpha_composition_mode;
+
+  // color values should be specified in the range [0, 65535]
+  uint16_t background_red, background_green, background_blue;
+  uint16_t secondary_background_red, secondary_background_green, secondary_background_blue;
+  uint16_t checkerboard_square_size;
+} heif_color_conversion_options_ext;
+
+
+// Assumes that it is a version=1 struct.
+LIBHEIF_API
+void heif_color_conversion_options_set_defaults(heif_color_conversion_options*);
+
+LIBHEIF_API
+heif_color_conversion_options_ext* heif_color_conversion_options_ext_alloc(void);
+
+LIBHEIF_API
+void heif_color_conversion_options_ext_copy(heif_color_conversion_options_ext* dst,
+                                            const heif_color_conversion_options_ext* src);
+
+LIBHEIF_API
+void heif_color_conversion_options_ext_free(heif_color_conversion_options_ext*);
+
+
+// ------------------------- color profiles -------------------------
+
+enum heif_color_profile_type
+{
+  heif_color_profile_type_not_present = 0,
+  heif_color_profile_type_nclx = heif_fourcc('n', 'c', 'l', 'x'),
+  heif_color_profile_type_rICC = heif_fourcc('r', 'I', 'C', 'C'),
+  heif_color_profile_type_prof = heif_fourcc('p', 'r', 'o', 'f')
+};
+
+
+// Returns 'heif_color_profile_type_not_present' if there is no color profile.
+// If there is an ICC profile and an NCLX profile, the ICC profile is returned.
+// TODO: we need a new API for this function as images can contain both NCLX and ICC at the same time.
+//       However, you can still use heif_image_handle_get_raw_color_profile() and
+//       heif_image_handle_get_nclx_color_profile() to access both profiles.
+LIBHEIF_API
+enum heif_color_profile_type heif_image_handle_get_color_profile_type(const heif_image_handle* handle);
+
+LIBHEIF_API
+size_t heif_image_handle_get_raw_color_profile_size(const heif_image_handle* handle);
+
+// Returns 'heif_error_Color_profile_does_not_exist' when there is no ICC profile.
+LIBHEIF_API
+struct heif_error heif_image_handle_get_raw_color_profile(const heif_image_handle* handle,
+                                                          void* out_data);
+
+
+enum heif_color_primaries
+{
+  heif_color_primaries_ITU_R_BT_709_5 = 1, // g=0.3;0.6, b=0.15;0.06, r=0.64;0.33, w=0.3127,0.3290
+  heif_color_primaries_unspecified = 2,
+  heif_color_primaries_ITU_R_BT_470_6_System_M = 4,
+  heif_color_primaries_ITU_R_BT_470_6_System_B_G = 5,
+  heif_color_primaries_ITU_R_BT_601_6 = 6,
+  heif_color_primaries_SMPTE_240M = 7,
+  heif_color_primaries_generic_film = 8,
+  heif_color_primaries_ITU_R_BT_2020_2_and_2100_0 = 9,
+  heif_color_primaries_SMPTE_ST_428_1 = 10,
+  heif_color_primaries_SMPTE_RP_431_2 = 11,
+  heif_color_primaries_SMPTE_EG_432_1 = 12,
+  heif_color_primaries_EBU_Tech_3213_E = 22
+};
+
+enum heif_transfer_characteristics
+{
+  heif_transfer_characteristic_ITU_R_BT_709_5 = 1,
+  heif_transfer_characteristic_unspecified = 2,
+  heif_transfer_characteristic_ITU_R_BT_470_6_System_M = 4,
+  heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G = 5,
+  heif_transfer_characteristic_ITU_R_BT_601_6 = 6,
+  heif_transfer_characteristic_SMPTE_240M = 7,
+  heif_transfer_characteristic_linear = 8,
+  heif_transfer_characteristic_logarithmic_100 = 9,
+  heif_transfer_characteristic_logarithmic_100_sqrt10 = 10,
+  heif_transfer_characteristic_IEC_61966_2_4 = 11,
+  heif_transfer_characteristic_ITU_R_BT_1361 = 12,
+  heif_transfer_characteristic_IEC_61966_2_1 = 13,
+  heif_transfer_characteristic_ITU_R_BT_2020_2_10bit = 14,
+  heif_transfer_characteristic_ITU_R_BT_2020_2_12bit = 15,
+  heif_transfer_characteristic_ITU_R_BT_2100_0_PQ = 16,
+  heif_transfer_characteristic_SMPTE_ST_428_1 = 17,
+  heif_transfer_characteristic_ITU_R_BT_2100_0_HLG = 18
+};
+
+enum heif_matrix_coefficients
+{
+  heif_matrix_coefficients_RGB_GBR = 0,
+  heif_matrix_coefficients_ITU_R_BT_709_5 = 1,  // TODO: or 709-6 according to h.273
+  heif_matrix_coefficients_unspecified = 2,
+  heif_matrix_coefficients_US_FCC_T47 = 4,
+  heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G = 5,
+  heif_matrix_coefficients_ITU_R_BT_601_6 = 6,  // TODO: or 601-7 according to h.273
+  heif_matrix_coefficients_SMPTE_240M = 7,
+  heif_matrix_coefficients_YCgCo = 8,
+  heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance = 9,
+  heif_matrix_coefficients_ITU_R_BT_2020_2_constant_luminance = 10,
+  heif_matrix_coefficients_SMPTE_ST_2085 = 11,
+  heif_matrix_coefficients_chromaticity_derived_non_constant_luminance = 12,
+  heif_matrix_coefficients_chromaticity_derived_constant_luminance = 13,
+  heif_matrix_coefficients_ICtCp = 14
+};
+
+typedef struct heif_color_profile_nclx
+{
+  // === version 1 fields
+
+  uint8_t version;
+
+  enum heif_color_primaries color_primaries;
+  enum heif_transfer_characteristics transfer_characteristics;
+  enum heif_matrix_coefficients matrix_coefficients;
+  uint8_t full_range_flag;
+
+  // --- decoded values (not used when saving nclx)
+
+  float color_primary_red_x, color_primary_red_y;
+  float color_primary_green_x, color_primary_green_y;
+  float color_primary_blue_x, color_primary_blue_y;
+  float color_primary_white_x, color_primary_white_y;
+} heif_color_profile_nclx;
+
+LIBHEIF_API
+heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp);
+
+LIBHEIF_API
+heif_error heif_nclx_color_profile_set_transfer_characteristics(heif_color_profile_nclx* nclx, uint16_t transfer_characteristics);
+
+LIBHEIF_API
+heif_error heif_nclx_color_profile_set_matrix_coefficients(heif_color_profile_nclx* nclx, uint16_t matrix_coefficients);
+
+// Returns 'heif_error_Color_profile_does_not_exist' when there is no NCLX profile.
+// TODO: This function does currently not return an NCLX profile if it is stored in the image bitstream.
+//       Only NCLX profiles stored as colr boxes are returned. This may change in the future.
+LIBHEIF_API
+heif_error heif_image_handle_get_nclx_color_profile(const heif_image_handle* handle,
+                                                    heif_color_profile_nclx** out_data);
+
+// Returned color profile has 'version' field set to the maximum allowed.
+// Do not fill values for higher versions as these might be outside the allocated structure size.
+// May return NULL.
+LIBHEIF_API
+heif_color_profile_nclx* heif_nclx_color_profile_alloc(void);
+
+LIBHEIF_API
+void heif_nclx_color_profile_free(heif_color_profile_nclx* nclx_profile);
+
+// Note: in early versions of HEIF, there could only be one color profile per image. However, this has been changed.
+// This function will now return ICC if one is present and NCLX only if there is no ICC.
+// You may better avoid this function and simply query for NCLX and ICC directly.
+LIBHEIF_API
+enum heif_color_profile_type heif_image_get_color_profile_type(const heif_image* image);
+
+// Returns the size of the ICC profile if one is assigned to the image. Otherwise, it returns 0.
+LIBHEIF_API
+size_t heif_image_get_raw_color_profile_size(const heif_image* image);
+
+// Returns the ICC profile if one is assigned to the image. Otherwise, it returns an error.
+LIBHEIF_API
+heif_error heif_image_get_raw_color_profile(const heif_image* image,
+                                            void* out_data);
+
+LIBHEIF_API
+heif_error heif_image_get_nclx_color_profile(const heif_image* image,
+                                             heif_color_profile_nclx** out_data);
+
+
+// The color profile is not attached to the image handle because we might need it
+// for color space transform and encoding.
+LIBHEIF_API
+heif_error heif_image_set_raw_color_profile(heif_image* image,
+                                            const char* profile_type_fourcc_string,
+                                            const void* profile_data,
+                                            const size_t profile_size);
+
+LIBHEIF_API
+heif_error heif_image_set_nclx_color_profile(heif_image* image,
+                                             const heif_color_profile_nclx* color_profile);
+
+
+// TODO: this function does not make any sense yet, since we currently cannot modify existing HEIF files.
+//LIBHEIF_API
+//void heif_image_remove_color_profile(struct heif_image* image);
+
+
+// --- content light level ---
+
+// Note: a value of 0 for any of these values indicates that the value is undefined.
+// The unit of these values is Candelas per square meter.
+typedef struct heif_content_light_level
+{
+  uint16_t max_content_light_level;
+  uint16_t max_pic_average_light_level;
+} heif_content_light_level;
+
+LIBHEIF_API
+int heif_image_has_content_light_level(const heif_image*);
+
+LIBHEIF_API
+void heif_image_get_content_light_level(const heif_image*, heif_content_light_level* out);
+
+// Returns whether the image has 'content light level' information. If 0 is returned, the output is not filled.
+LIBHEIF_API
+int heif_image_handle_get_content_light_level(const heif_image_handle*, heif_content_light_level* out);
+
+LIBHEIF_API
+void heif_image_set_content_light_level(const heif_image*, const heif_content_light_level* in);
+
+
+// --- mastering display colour volume ---
+
+// Note: color coordinates are defined according to the CIE 1931 definition of x as specified in ISO 11664-1 (see also ISO 11664-3 and CIE 15).
+typedef struct heif_mastering_display_colour_volume
+{
+  uint16_t display_primaries_x[3];
+  uint16_t display_primaries_y[3];
+  uint16_t white_point_x;
+  uint16_t white_point_y;
+  uint32_t max_display_mastering_luminance;
+  uint32_t min_display_mastering_luminance;
+} heif_mastering_display_colour_volume;
+
+// The units for max_display_mastering_luminance and min_display_mastering_luminance is Candelas per square meter.
+typedef struct heif_decoded_mastering_display_colour_volume
+{
+  float display_primaries_x[3];
+  float display_primaries_y[3];
+  float white_point_x;
+  float white_point_y;
+  double max_display_mastering_luminance;
+  double min_display_mastering_luminance;
+} heif_decoded_mastering_display_colour_volume;
+
+typedef struct heif_ambient_viewing_environment
+{
+  uint32_t ambient_illumination;
+  uint16_t ambient_light_x;
+  uint16_t ambient_light_y;
+} heif_ambient_viewing_environment;
+
+LIBHEIF_API
+int heif_image_has_mastering_display_colour_volume(const heif_image*);
+
+LIBHEIF_API
+void heif_image_get_mastering_display_colour_volume(const heif_image*, heif_mastering_display_colour_volume* out);
+
+// Returns whether the image has 'mastering display colour volume' information. If 0 is returned, the output is not filled.
+LIBHEIF_API
+int heif_image_handle_get_mastering_display_colour_volume(const heif_image_handle*, heif_mastering_display_colour_volume* out);
+
+LIBHEIF_API
+void heif_image_set_mastering_display_colour_volume(const heif_image*, const heif_mastering_display_colour_volume* in);
+
+
+// Converts the internal numeric representation of heif_mastering_display_colour_volume to the
+// normalized values, collected in heif_decoded_mastering_display_colour_volume.
+// Values that are out-of-range are decoded to 0, indicating an undefined value (as specified in ISO/IEC 23008-2).
+LIBHEIF_API
+struct heif_error heif_mastering_display_colour_volume_decode(const heif_mastering_display_colour_volume* in,
+                                                              heif_decoded_mastering_display_colour_volume* out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_context.cc 1.20.1-1/libheif/api/libheif/heif_context.cc
--- 1.19.8-1/libheif/api/libheif/heif_context.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_context.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,290 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_context.h"
+#include "api_structs.h"
+#include "context.h"
+#include "init.h"
+#include "file.h"
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include <algorithm>
+
+#ifdef _WIN32
+// for _write
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+
+heif_context* heif_context_alloc()
+{
+  load_plugins_if_not_initialized_yet();
+
+  struct heif_context* ctx = new heif_context;
+  ctx->context = std::make_shared<HeifContext>();
+
+  return ctx;
+}
+
+void heif_context_free(heif_context* ctx)
+{
+  delete ctx;
+}
+
+heif_error heif_context_read_from_file(heif_context* ctx, const char* filename,
+                                       const struct heif_reading_options*)
+{
+  Error err = ctx->context->read_from_file(filename);
+  return err.error_struct(ctx->context.get());
+}
+
+heif_error heif_context_read_from_memory(heif_context* ctx, const void* mem, size_t size,
+                                         const struct heif_reading_options*)
+{
+  Error err = ctx->context->read_from_memory(mem, size, true);
+  return err.error_struct(ctx->context.get());
+}
+
+heif_error heif_context_read_from_memory_without_copy(heif_context* ctx, const void* mem, size_t size,
+                                                      const struct heif_reading_options*)
+{
+  Error err = ctx->context->read_from_memory(mem, size, false);
+  return err.error_struct(ctx->context.get());
+}
+
+heif_error heif_context_read_from_reader(struct heif_context* ctx,
+                                         const struct heif_reader* reader_func_table,
+                                         void* userdata,
+                                         const struct heif_reading_options*)
+{
+  auto reader = std::make_shared<StreamReader_CApi>(reader_func_table, userdata);
+
+  Error err = ctx->context->read(reader);
+  return err.error_struct(ctx->context.get());
+}
+
+// TODO: heif_error heif_context_read_from_file_descriptor(heif_context*, int fd);
+
+int heif_context_get_number_of_top_level_images(heif_context* ctx)
+{
+  return (int) ctx->context->get_top_level_images(true).size();
+}
+
+
+int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id)
+{
+  const std::vector<std::shared_ptr<ImageItem>> images = ctx->context->get_top_level_images(true);
+
+  for (const auto& img : images) {
+    if (img->get_id() == id) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+
+int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx,
+                                                 heif_item_id* ID_array,
+                                                 int count)
+{
+  if (ID_array == nullptr || count == 0 || ctx == nullptr) {
+    return 0;
+  }
+
+
+  // fill in ID values into output array
+
+  const std::vector<std::shared_ptr<ImageItem>> imgs = ctx->context->get_top_level_images(true);
+  int n = (int) std::min(count, (int) imgs.size());
+  for (int i = 0; i < n; i++) {
+    ID_array[i] = imgs[i]->get_id();
+  }
+
+  return n;
+}
+
+
+struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id)
+{
+  if (!id) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
+  }
+
+  std::shared_ptr<ImageItem> primary = ctx->context->get_primary_image(true);
+  if (!primary) {
+    return Error(heif_error_Invalid_input,
+                 heif_suberror_No_or_invalid_primary_item).error_struct(ctx->context.get());
+  }
+
+  *id = primary->get_id();
+
+  return Error::Ok.error_struct(ctx->context.get());
+}
+
+
+heif_error heif_context_get_primary_image_handle(heif_context* ctx, heif_image_handle** img)
+{
+  if (!img) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(ctx->context.get());
+  }
+
+  std::shared_ptr<ImageItem> primary_image = ctx->context->get_primary_image(true);
+
+  // It is a requirement of an HEIF file there is always a primary image.
+  // If there is none, an error is generated when loading the file.
+  if (!primary_image) {
+    Error err(heif_error_Invalid_input,
+              heif_suberror_No_or_invalid_primary_item);
+    return err.error_struct(ctx->context.get());
+  }
+
+  if (auto errImage = std::dynamic_pointer_cast<ImageItem_Error>(primary_image)) {
+    Error error = errImage->get_item_error();
+    return error.error_struct(ctx->context.get());
+  }
+
+  *img = new heif_image_handle();
+  (*img)->image = std::move(primary_image);
+  (*img)->context = ctx->context;
+
+  return Error::Ok.error_struct(ctx->context.get());
+}
+
+
+struct heif_error heif_context_get_image_handle(struct heif_context* ctx,
+                                                heif_item_id id,
+                                                struct heif_image_handle** imgHdl)
+{
+  if (!imgHdl) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, ""};
+  }
+
+  auto image = ctx->context->get_image(id, true);
+
+  if (auto errImage = std::dynamic_pointer_cast<ImageItem_Error>(image)) {
+    Error error = errImage->get_item_error();
+    return error.error_struct(ctx->context.get());
+  }
+
+  if (!image) {
+    *imgHdl = nullptr;
+
+    return {heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced, ""};
+  }
+
+  *imgHdl = new heif_image_handle();
+  (*imgHdl)->image = std::move(image);
+  (*imgHdl)->context = ctx->context;
+
+  return heif_error_success;
+}
+
+
+void heif_context_debug_dump_boxes_to_file(struct heif_context* ctx, int fd)
+{
+  if (!ctx) {
+    return;
+  }
+
+  std::string dump = ctx->context->debug_dump_boxes();
+  // TODO(fancycode): Should we return an error if writing fails?
+#ifdef _WIN32
+  auto written = _write(fd, dump.c_str(), static_cast<unsigned int>(dump.size()));
+#else
+  auto written = write(fd, dump.c_str(), dump.size());
+#endif
+  (void) written;
+}
+
+
+// ====================================================================================================
+//   Write the heif_context to a HEIF file
+
+
+static struct heif_error heif_file_writer_write(struct heif_context* ctx,
+                                                const void* data, size_t size, void* userdata)
+{
+  const char* filename = static_cast<const char*>(userdata);
+
+#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
+  std::ofstream ostr(HeifFile::convert_utf8_path_to_utf16(filename).c_str(), std::ios_base::binary);
+#else
+  std::ofstream ostr(filename, std::ios_base::binary);
+#endif
+  ostr.write(static_cast<const char*>(data), size);
+  // TODO: handle write errors
+  return Error::Ok.error_struct(ctx->context.get());
+}
+
+
+struct heif_error heif_context_write_to_file(struct heif_context* ctx,
+                                             const char* filename)
+{
+  heif_writer writer;
+  writer.writer_api_version = 1;
+  writer.write = heif_file_writer_write;
+  return heif_context_write(ctx, &writer, (void*) filename);
+}
+
+
+struct heif_error heif_context_write(struct heif_context* ctx,
+                                     struct heif_writer* writer,
+                                     void* userdata)
+{
+  if (!writer) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
+  }
+  else if (writer->writer_api_version != 1) {
+    Error err(heif_error_Usage_error, heif_suberror_Unsupported_writer_version);
+    return err.error_struct(ctx->context.get());
+  }
+
+  StreamWriter swriter;
+  ctx->context->write(swriter);
+
+  const auto& data = swriter.get_data();
+  heif_error writer_error = writer->write(ctx, data.data(), data.size(), userdata);
+  if (!writer_error.message) {
+    // It is now allowed to return a NULL error message on success. It will be replaced by "Success". An error message is still required when there is an error.
+    if (writer_error.code == heif_error_Ok) {
+      writer_error.message = Error::kSuccess;
+      return writer_error;
+    }
+    else {
+      return heif_error{heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_writer callback returned a null error text"};
+    }
+  }
+  else {
+    return writer_error;
+  }
+}
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_context.h 1.20.1-1/libheif/api/libheif/heif_context.h
--- 1.19.8-1/libheif/api/libheif/heif_context.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_context.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,329 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_CONTEXT_H
+#define LIBHEIF_HEIF_CONTEXT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_error.h>
+
+
+/**
+ * libheif known compression formats.
+ */
+enum heif_compression_format
+{
+  /**
+   * Unspecified / undefined compression format.
+   *
+   * This is used to mean "no match" or "any decoder" for some parts of the
+   * API. It does not indicate a specific compression format.
+   */
+  heif_compression_undefined = 0,
+  /**
+   * HEVC compression, used for HEIC images.
+   *
+   * This is equivalent to H.265.
+  */
+  heif_compression_HEVC = 1,
+  /**
+   * AVC compression. (Currently unused in libheif.)
+   *
+   * The compression is defined in ISO/IEC 14496-10. This is equivalent to H.264.
+   *
+   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex E.
+   */
+  heif_compression_AVC = 2,
+  /**
+   * JPEG compression.
+   *
+   * The compression format is defined in ISO/IEC 10918-1. The encapsulation
+   * of JPEG is specified in ISO/IEC 23008-12:2022 Annex H.
+  */
+  heif_compression_JPEG = 3,
+  /**
+   * AV1 compression, used for AVIF images.
+   *
+   * The compression format is provided at https://aomediacodec.github.io/av1-spec/
+   *
+   * The encapsulation is defined in https://aomediacodec.github.io/av1-avif/
+   */
+  heif_compression_AV1 = 4,
+  /**
+   * VVC compression.
+   *
+   * The compression format is defined in ISO/IEC 23090-3. This is equivalent to H.266.
+   *
+   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex L.
+   */
+  heif_compression_VVC = 5,
+  /**
+   * EVC compression. (Currently unused in libheif.)
+   *
+   * The compression format is defined in ISO/IEC 23094-1.
+   *
+   * The encapsulation is defined in ISO/IEC 23008-12:2022 Annex M.
+   */
+  heif_compression_EVC = 6,
+  /**
+   * JPEG 2000 compression.
+   *
+   * The encapsulation of JPEG 2000 is specified in ISO/IEC 15444-16:2021.
+   * The core encoding is defined in ISO/IEC 15444-1, or ITU-T T.800.
+  */
+  heif_compression_JPEG2000 = 7,
+  /**
+   * Uncompressed encoding.
+   *
+   * This is defined in ISO/IEC 23001-17:2024.
+  */
+  heif_compression_uncompressed = 8,
+  /**
+   * Mask image encoding.
+   *
+   * See ISO/IEC 23008-12:2022 Section 6.10.2
+   */
+  heif_compression_mask = 9,
+  /**
+   * High Throughput JPEG 2000 (HT-J2K) compression.
+   *
+   * The encapsulation of HT-J2K is specified in ISO/IEC 15444-16:2021.
+   * The core encoding is defined in ISO/IEC 15444-15, or ITU-T T.814.
+  */
+  heif_compression_HTJ2K = 10
+};
+
+
+// ========================= heif_context =========================
+// A heif_context represents a HEIF file that has been read.
+// In the future, you will also be able to add pictures to a heif_context
+// and write it into a file again.
+
+
+// Allocate a new context for reading HEIF files.
+// Has to be freed again with heif_context_free().
+LIBHEIF_API
+heif_context* heif_context_alloc(void);
+
+// Free a previously allocated HEIF context. You should not free a context twice.
+LIBHEIF_API
+void heif_context_free(heif_context*);
+
+
+typedef struct heif_reading_options heif_reading_options;
+
+enum heif_reader_grow_status
+{
+  heif_reader_grow_status_size_reached,    // requested size has been reached, we can read until this point
+  heif_reader_grow_status_timeout,         // size has not been reached yet, but it may still grow further (deprecated)
+  heif_reader_grow_status_size_beyond_eof, // size has not been reached and never will. The file has grown to its full size
+  heif_reader_grow_status_error            // an error has occurred
+};
+
+
+typedef struct heif_reader_range_request_result
+{
+  enum heif_reader_grow_status status; // should not return 'heif_reader_grow_status_timeout'
+
+  // Indicates up to what position the file has been read.
+  // If we cannot read the whole file range (status == 'heif_reader_grow_status_size_beyond_eof'), this is the actual end position.
+  // On the other hand, it may be that the reader was reading more data than requested. In that case, it should indicate the full size here
+  // and libheif may decide to make use of the additional data (e.g. for filling 'tili' offset tables).
+  uint64_t range_end;
+
+  // for status == 'heif_reader_grow_status_error'
+  int reader_error_code;        // a reader specific error code
+  const char* reader_error_msg; // libheif will call heif_reader.release_error_msg on this if it is not NULL
+} heif_reader_range_request_result;
+
+
+typedef struct heif_reader
+{
+  // API version supported by this reader
+  int reader_api_version;
+
+  // --- version 1 functions ---
+  int64_t (* get_position)(void* userdata);
+
+  // The functions read(), and seek() return 0 on success.
+  // Generally, libheif will make sure that we do not read past the file size.
+  int (* read)(void* data,
+               size_t size,
+               void* userdata);
+
+  int (* seek)(int64_t position,
+               void* userdata);
+
+  // When calling this function, libheif wants to make sure that it can read the file
+  // up to 'target_size'. This is useful when the file is currently downloaded and may
+  // grow with time. You may, for example, extract the image sizes even before the actual
+  // compressed image data has been completely downloaded.
+  //
+  // Even if your input files will not grow, you will have to implement at least
+  // detection whether the target_size is above the (fixed) file length
+  // (in this case, return 'size_beyond_eof').
+  enum heif_reader_grow_status (* wait_for_file_size)(int64_t target_size, void* userdata);
+
+  // --- version 2 functions ---
+
+  // These two functions are for applications that want to stream HEIF files on demand.
+  // For example, a large HEIF file that is served over HTTPS and we only want to download
+  // it partially to decode individual tiles.
+  // If you do not have this use case, you do not have to implement these functions and
+  // you can set them to NULL. For simple linear loading, you may use the 'wait_for_file_size'
+  // function above instead.
+
+  // If this function is defined, libheif will often request a file range before accessing it.
+  // The purpose of this function is that libheif will usually read very small chunks of data with the
+  // read() callback above. However, it is inefficient to request such a small chunk of data over a network
+  // and the network delay will significantly increase the decoding time.
+  // Thus, libheif will call request_range() with a larger block of data that should be preloaded and the
+  // subsequent read() calls will work within the requested ranges.
+  //
+  // Note: `end_pos` is one byte after the last position to be read.
+  // You should return
+  // - 'heif_reader_grow_status_size_reached' if the requested range is available, or
+  // - 'heif_reader_grow_status_size_beyond_eof' if the requested range exceeds the file size
+  //   (the valid part of the range has been read).
+  heif_reader_range_request_result (* request_range)(uint64_t start_pos, uint64_t end_pos, void* userdata);
+
+  // libheif might issue hints when it assumes that a file range might be needed in the future.
+  // This may happen, for example, when your are doing selective tile accesses and libheif proposes
+  // to preload offset pointer tables.
+  // Another difference to request_file_range() is that this call should be non-blocking.
+  // If you preload any data, do this in a background thread.
+  void (* preload_range_hint)(uint64_t start_pos, uint64_t end_pos, void* userdata);
+
+  // If libheif does not need access to a file range anymore, it may call this function to
+  // give a hint to the reader that it may release the range from a cache.
+  // If you do not maintain a file cache that wants to reduce its size dynamically, you do not
+  // need to implement this function.
+  void (* release_file_range)(uint64_t start_pos, uint64_t end_pos, void* userdata);
+
+  // Release an error message that was returned by heif_reader in an earlier call.
+  // If this function is NULL, the error message string will not be released.
+  // This is a viable option if you are only returning static strings.
+  void (* release_error_msg)(const char* msg);
+} heif_reader;
+
+
+// Read a HEIF file from a named disk file.
+// The heif_reading_options should currently be set to NULL.
+LIBHEIF_API
+heif_error heif_context_read_from_file(heif_context*, const char* filename,
+                                       const heif_reading_options*);
+
+// Read a HEIF file stored completely in memory.
+// The heif_reading_options should currently be set to NULL.
+// DEPRECATED: use heif_context_read_from_memory_without_copy() instead.
+LIBHEIF_API
+heif_error heif_context_read_from_memory(heif_context*,
+                                         const void* mem, size_t size,
+                                         const heif_reading_options*);
+
+// Same as heif_context_read_from_memory() except that the provided memory is not copied.
+// That means, you will have to keep the memory area alive as long as you use the heif_context.
+LIBHEIF_API
+heif_error heif_context_read_from_memory_without_copy(heif_context*,
+                                                      const void* mem, size_t size,
+                                                      const heif_reading_options*);
+
+LIBHEIF_API
+heif_error heif_context_read_from_reader(heif_context*,
+                                         const heif_reader* reader,
+                                         void* userdata,
+                                         const heif_reading_options*);
+
+// Number of top-level images in the HEIF file. This does not include the thumbnails or the
+// tile images that are composed to an image grid. You can get access to the thumbnails via
+// the main image handle.
+LIBHEIF_API
+int heif_context_get_number_of_top_level_images(heif_context* ctx);
+
+LIBHEIF_API
+int heif_context_is_top_level_image_ID(heif_context* ctx, heif_item_id id);
+
+// Fills in image IDs into the user-supplied int-array 'ID_array', preallocated with 'count' entries.
+// Function returns the total number of IDs filled into the array.
+LIBHEIF_API
+int heif_context_get_list_of_top_level_image_IDs(heif_context* ctx,
+                                                 heif_item_id* ID_array,
+                                                 int count);
+
+LIBHEIF_API
+heif_error heif_context_get_primary_image_ID(heif_context* ctx, heif_item_id* id);
+
+// Get a handle to the primary image of the HEIF file.
+// This is the image that should be displayed primarily when there are several images in the file.
+LIBHEIF_API
+heif_error heif_context_get_primary_image_handle(heif_context* ctx,
+                                                 heif_image_handle**);
+
+// Get the image handle for a known image ID.
+LIBHEIF_API
+heif_error heif_context_get_image_handle(heif_context* ctx,
+                                         heif_item_id id,
+                                         heif_image_handle**);
+
+// Print information about the boxes of a HEIF file to file descriptor.
+// This is for debugging and informational purposes only. You should not rely on
+// the output having a specific format. At best, you should not use this at all.
+LIBHEIF_API
+void heif_context_debug_dump_boxes_to_file(heif_context* ctx, int fd);
+
+// ====================================================================================================
+//   Write the heif_context to a HEIF file
+
+LIBHEIF_API
+heif_error heif_context_write_to_file(heif_context*,
+                                      const char* filename);
+
+typedef struct heif_writer
+{
+  // API version supported by this writer
+  int writer_api_version;
+
+  // --- version 1 functions ---
+
+  // On success, the returned heif_error may have a NULL message. It will automatically be replaced with a "Success" string.
+  heif_error (* write)(heif_context* ctx, // TODO: why do we need this parameter?
+                       const void* data,
+                       size_t size,
+                       void* userdata);
+} heif_writer;
+
+
+LIBHEIF_API
+heif_error heif_context_write(heif_context*,
+                              heif_writer* writer,
+                              void* userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_cxx.h 1.20.1-1/libheif/api/libheif/heif_cxx.h
--- 1.19.8-1/libheif/api/libheif/heif_cxx.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_cxx.h	2025-07-02 13:05:31.000000000 +0000
@@ -345,9 +345,9 @@ namespace heif {
 
     bool has_channel(enum heif_channel channel) const noexcept;
 
-    const uint8_t* get_plane(enum heif_channel channel, int* out_stride) const noexcept;
+    const uint8_t* get_plane(enum heif_channel channel, size_t* out_stride) const noexcept;
 
-    uint8_t* get_plane(enum heif_channel channel, int* out_stride) noexcept;
+    uint8_t* get_plane(enum heif_channel channel, size_t* out_stride) noexcept;
 
     // throws Error
     void set_nclx_color_profile(const ColorProfile_nclx&);
@@ -906,14 +906,14 @@ namespace heif {
     return heif_image_has_channel(m_image.get(), channel);
   }
 
-  inline const uint8_t* Image::get_plane(enum heif_channel channel, int* out_stride) const noexcept
+  inline const uint8_t* Image::get_plane(enum heif_channel channel, size_t* out_stride) const noexcept
   {
-    return heif_image_get_plane_readonly(m_image.get(), channel, out_stride);
+    return heif_image_get_plane_readonly2(m_image.get(), channel, out_stride);
   }
 
-  inline uint8_t* Image::get_plane(enum heif_channel channel, int* out_stride) noexcept
+  inline uint8_t* Image::get_plane(enum heif_channel channel, size_t* out_stride) noexcept
   {
-    return heif_image_get_plane(m_image.get(), channel, out_stride);
+    return heif_image_get_plane2(m_image.get(), channel, out_stride);
   }
 
   inline void Image::set_nclx_color_profile(const ColorProfile_nclx& nclx)
diff -pruN 1.19.8-1/libheif/api/libheif/heif_decoding.cc 1.20.1-1/libheif/api/libheif/heif_decoding.cc
--- 1.19.8-1/libheif/api/libheif/heif_decoding.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_decoding.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,248 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_decoding.h"
+#include "api_structs.h"
+#include "plugin_registry.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+
+void heif_context_set_max_decoding_threads(struct heif_context* ctx, int max_threads)
+{
+  ctx->context->set_max_decoding_threads(max_threads);
+}
+
+
+int heif_have_decoder_for_format(enum heif_compression_format format)
+{
+  auto plugin = get_decoder(format, nullptr);
+  return plugin != nullptr;
+}
+
+
+static void fill_default_decoding_options(heif_decoding_options& options)
+{
+  options.version = 7;
+
+  options.ignore_transformations = false;
+
+  options.start_progress = nullptr;
+  options.on_progress = nullptr;
+  options.end_progress = nullptr;
+  options.progress_user_data = nullptr;
+
+  // version 2
+
+  options.convert_hdr_to_8bit = false;
+
+  // version 3
+
+  options.strict_decoding = false;
+
+  // version 4
+
+  options.decoder_id = nullptr;
+
+  // version 5
+
+  options.color_conversion_options.version = 1;
+  options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
+  options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
+  options.color_conversion_options.only_use_preferred_chroma_algorithm = false;
+
+  // version 6
+
+  options.cancel_decoding = nullptr;
+
+  // version 7
+
+  options.color_conversion_options_ext = nullptr;
+}
+
+
+heif_decoding_options* heif_decoding_options_alloc()
+{
+  auto options = new heif_decoding_options;
+
+  fill_default_decoding_options(*options);
+
+  return options;
+}
+
+
+// overwrite the (possibly lower version) input options over the default options
+void heif_decoding_options_copy(struct heif_decoding_options* dst,
+                                const struct heif_decoding_options* src)
+{
+  if (src == nullptr) {
+    return;
+  }
+
+  int min_version = std::min(dst->version, src->version);
+
+  switch (min_version) {
+    case 7:
+      dst->color_conversion_options_ext = src->color_conversion_options_ext;
+      [[fallthrough]];
+    case 6:
+      dst->cancel_decoding = src->cancel_decoding;
+      [[fallthrough]];
+    case 5:
+      dst->color_conversion_options = src->color_conversion_options;
+      [[fallthrough]];
+    case 4:
+      dst->decoder_id = src->decoder_id;
+      [[fallthrough]];
+    case 3:
+      dst->strict_decoding = src->strict_decoding;
+      [[fallthrough]];
+    case 2:
+      dst->convert_hdr_to_8bit = src->convert_hdr_to_8bit;
+      [[fallthrough]];
+    case 1:
+      dst->ignore_transformations = src->ignore_transformations;
+      dst->start_progress = src->start_progress;
+      dst->on_progress = src->on_progress;
+      dst->end_progress = src->end_progress;
+      dst->progress_user_data = src->progress_user_data;
+  }
+}
+
+
+void heif_decoding_options_free(heif_decoding_options* options)
+{
+  delete options;
+}
+
+
+int heif_get_decoder_descriptors(enum heif_compression_format format_filter,
+                                 const struct heif_decoder_descriptor** out_decoders,
+                                 int count)
+{
+  struct decoder_with_priority
+  {
+    const heif_decoder_plugin* plugin;
+    int priority;
+  };
+
+  std::vector<decoder_with_priority> plugins;
+  std::vector<heif_compression_format> formats;
+  if (format_filter == heif_compression_undefined) {
+    formats = {heif_compression_HEVC, heif_compression_AV1, heif_compression_JPEG, heif_compression_JPEG2000, heif_compression_HTJ2K, heif_compression_VVC};
+  }
+  else {
+    formats.emplace_back(format_filter);
+  }
+
+  for (const auto* plugin : get_decoder_plugins()) {
+    for (auto& format : formats) {
+      int priority = plugin->does_support_format(format);
+      if (priority) {
+        plugins.push_back({plugin, priority});
+        break;
+      }
+    }
+  }
+
+  if (out_decoders == nullptr) {
+    return (int) plugins.size();
+  }
+
+  std::sort(plugins.begin(), plugins.end(), [](const decoder_with_priority& a, const decoder_with_priority& b) {
+    return a.priority > b.priority;
+  });
+
+  int nDecodersReturned = std::min(count, (int) plugins.size());
+
+  for (int i = 0; i < nDecodersReturned; i++) {
+    out_decoders[i] = (heif_decoder_descriptor*) (plugins[i].plugin);
+  }
+
+  return nDecodersReturned;
+}
+
+
+const char* heif_decoder_descriptor_get_name(const struct heif_decoder_descriptor* descriptor)
+{
+  auto decoder = (heif_decoder_plugin*) descriptor;
+  return decoder->get_plugin_name();
+}
+
+
+const char* heif_decoder_descriptor_get_id_name(const struct heif_decoder_descriptor* descriptor)
+{
+  auto decoder = (heif_decoder_plugin*) descriptor;
+  if (decoder->plugin_api_version < 3) {
+    return nullptr;
+  }
+  else {
+    return decoder->id_name;
+  }
+}
+
+
+struct heif_error heif_decode_image(const struct heif_image_handle* in_handle,
+                                    struct heif_image** out_img,
+                                    heif_colorspace colorspace,
+                                    heif_chroma chroma,
+                                    const struct heif_decoding_options* input_options)
+{
+  if (out_img == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "NULL out_img passed to heif_decode_image()"};
+  }
+
+  if (in_handle == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "NULL heif_image_handle passed to heif_decode_image()"};
+  }
+
+  *out_img = nullptr;
+  heif_item_id id = in_handle->image->get_id();
+
+  heif_decoding_options dec_options;
+  fill_default_decoding_options(dec_options);
+  heif_decoding_options_copy(&dec_options, input_options);
+
+  Result<std::shared_ptr<HeifPixelImage>> decodingResult = in_handle->context->decode_image(id,
+                                                                                            colorspace,
+                                                                                            chroma,
+                                                                                            dec_options,
+                                                                                            false, 0, 0);
+  if (decodingResult.error.error_code != heif_error_Ok) {
+    return decodingResult.error.error_struct(in_handle->image.get());
+  }
+
+  std::shared_ptr<HeifPixelImage> img = decodingResult.value;
+
+  *out_img = new heif_image();
+  (*out_img)->image = std::move(img);
+
+  return Error::Ok.error_struct(in_handle->image.get());
+}
+
+
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_decoding.h 1.20.1-1/libheif/api/libheif/heif_decoding.h
--- 1.19.8-1/libheif/api/libheif/heif_decoding.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_decoding.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,162 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_DECODING_H
+#define LIBHEIF_HEIF_DECODING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_image.h>
+#include <libheif/heif_context.h>
+#include <libheif/heif_color.h>
+
+
+// If the maximum threads number is set to 0, the image tiles are decoded in the main thread.
+// This is different from setting it to 1, which will generate a single background thread to decode the tiles.
+// Note that this setting only affects libheif itself. The codecs itself may still use multi-threaded decoding.
+// You can use it, for example, in cases where you are decoding several images in parallel anyway you thus want
+// to minimize parallelism in each decoder.
+LIBHEIF_API
+void heif_context_set_max_decoding_threads(heif_context* ctx, int max_threads);
+
+// Quick check whether there is a decoder available for the given format.
+// Note that the decoder still may not be able to decode all variants of that format.
+// You will have to query that further (todo) or just try to decode and check the returned error.
+LIBHEIF_API
+int heif_have_decoder_for_format(enum heif_compression_format format);
+
+
+enum heif_progress_step
+{
+  heif_progress_step_total = 0,
+  heif_progress_step_load_tile = 1
+};
+
+
+typedef struct heif_decoding_options
+{
+  uint8_t version;
+
+  // version 1 options
+
+  // Ignore geometric transformations like cropping, rotation, mirroring.
+  // Default: false (do not ignore).
+  uint8_t ignore_transformations;
+
+  // Any of the progress functions may be called from background threads.
+  void (* start_progress)(enum heif_progress_step step, int max_progress, void* progress_user_data);
+
+  void (* on_progress)(enum heif_progress_step step, int progress, void* progress_user_data);
+
+  void (* end_progress)(enum heif_progress_step step, void* progress_user_data);
+
+  void* progress_user_data;
+
+  // version 2 options
+
+  uint8_t convert_hdr_to_8bit;
+
+  // version 3 options
+
+  // When enabled, an error is returned for invalid input. Otherwise, it will try its best and
+  // add decoding warnings to the decoded heif_image. Default is non-strict.
+  uint8_t strict_decoding;
+
+  // version 4 options
+
+  // name_id of the decoder to use for the decoding.
+  // If set to NULL (default), the highest priority decoder is chosen.
+  // The priority is defined in the plugin.
+  const char* decoder_id;
+
+  // version 5 options
+
+  heif_color_conversion_options color_conversion_options;
+
+  // version 6 options
+
+  int (* cancel_decoding)(void* progress_user_data);
+
+  // version 7 options
+
+  // When set to NULL, default options will be used
+  heif_color_conversion_options_ext* color_conversion_options_ext;
+} heif_decoding_options;
+
+
+// Allocate decoding options and fill with default values.
+// Note: you should always get the decoding options through this function since the
+// option structure may grow in size in future versions.
+LIBHEIF_API
+heif_decoding_options* heif_decoding_options_alloc(void);
+
+LIBHEIF_API
+void heif_decoding_options_copy(heif_decoding_options* dst,
+                                const heif_decoding_options* src);
+
+LIBHEIF_API
+void heif_decoding_options_free(heif_decoding_options*);
+
+
+typedef struct heif_decoder_descriptor heif_decoder_descriptor;
+
+// Get a list of available decoders. You can filter the encoders by compression format.
+// Use format_filter==heif_compression_undefined to get all available decoders.
+// The returned list of decoders is sorted by their priority (which is a plugin property).
+// The number of decoders is returned, which are not more than 'count' if (out_decoders != nullptr).
+// By setting out_decoders==nullptr, you can query the number of decoders, 'count' is ignored.
+LIBHEIF_API
+int heif_get_decoder_descriptors(enum heif_compression_format format_filter,
+                                 const heif_decoder_descriptor** out_decoders,
+                                 int count);
+
+// Return a long, descriptive name of the decoder (including version information).
+LIBHEIF_API
+const char* heif_decoder_descriptor_get_name(const heif_decoder_descriptor*);
+
+// Return a short, symbolic name for identifying the decoder.
+// This name should stay constant over different decoder versions.
+// Note: the returned ID may be NULL for old plugins that don't support this yet.
+LIBHEIF_API
+const char* heif_decoder_descriptor_get_id_name(const heif_decoder_descriptor*);
+
+
+// Decode an heif_image_handle into the actual pixel image and also carry out
+// all geometric transformations specified in the HEIF file (rotation, cropping, mirroring).
+//
+// If colorspace or chroma is set to heif_colorspace_undefined or heif_chroma_undefined,
+// respectively, the original colorspace is taken.
+// Decoding options may be NULL. If you want to supply options, always use
+// heif_decoding_options_alloc() to get the structure.
+LIBHEIF_API
+heif_error heif_decode_image(const heif_image_handle* in_handle,
+                             heif_image** out_img,
+                             enum heif_colorspace colorspace,
+                             enum heif_chroma chroma,
+                             const heif_decoding_options* options);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_emscripten.h 1.20.1-1/libheif/api/libheif/heif_emscripten.h
--- 1.19.8-1/libheif/api/libheif/heif_emscripten.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_emscripten.h	2025-07-02 13:05:31.000000000 +0000
@@ -139,20 +139,20 @@ static emscripten::val heif_js_decode_im
   result.set("width", width);
   int height = heif_image_handle_get_height(handle);
   result.set("height", height);
-  std::basic_string<unsigned char> data;
+  std::vector<unsigned char> data;
   result.set("chroma", heif_image_get_chroma_format(image));
   result.set("colorspace", heif_image_get_colorspace(image));
   switch (heif_image_get_colorspace(image)) {
     case heif_colorspace_YCbCr: {
-      int stride_y;
-      const uint8_t* plane_y = heif_image_get_plane_readonly(image,
-                                                             heif_channel_Y, &stride_y);
-      int stride_u;
-      const uint8_t* plane_u = heif_image_get_plane_readonly(image,
-                                                             heif_channel_Cb, &stride_u);
-      int stride_v;
-      const uint8_t* plane_v = heif_image_get_plane_readonly(image,
-                                                             heif_channel_Cr, &stride_v);
+      size_t stride_y;
+      const uint8_t* plane_y = heif_image_get_plane_readonly2(image,
+                                                              heif_channel_Y, &stride_y);
+      size_t stride_u;
+      const uint8_t* plane_u = heif_image_get_plane_readonly2(image,
+                                                              heif_channel_Cb, &stride_u);
+      size_t stride_v;
+      const uint8_t* plane_v = heif_image_get_plane_readonly2(image,
+                                                              heif_channel_Cr, &stride_v);
       data.resize((width * height) + (2 * round_odd(width) * round_odd(height)));
       unsigned char* dest = const_cast<unsigned char*>(data.data());
       strided_copy(dest, plane_y, width, height, stride_y);
@@ -164,17 +164,17 @@ static emscripten::val heif_js_decode_im
       break;
     case heif_colorspace_RGB: {
       if(heif_image_get_chroma_format(image) == heif_chroma_interleaved_RGB) {
-        int stride_rgb;
-        const uint8_t* plane_rgb = heif_image_get_plane_readonly(image,
-                                                                 heif_channel_interleaved, &stride_rgb);
+        size_t stride_rgb;
+        const uint8_t* plane_rgb = heif_image_get_plane_readonly2(image,
+                                                                  heif_channel_interleaved, &stride_rgb);
         data.resize(width * height * 3);
         unsigned char* dest = const_cast<unsigned char*>(data.data());
         strided_copy(dest, plane_rgb, width * 3, height, stride_rgb);
       }
       else if (heif_image_get_chroma_format(image) == heif_chroma_interleaved_RGBA) {
-        int stride_rgba;
-        const uint8_t* plane_rgba = heif_image_get_plane_readonly(image,
-                                                                 heif_channel_interleaved, &stride_rgba);
+        size_t stride_rgba;
+        const uint8_t* plane_rgba = heif_image_get_plane_readonly2(image,
+                                                                   heif_channel_interleaved, &stride_rgba);
         data.resize(width * height * 4);
         unsigned char* dest = const_cast<unsigned char*>(data.data());
         strided_copy(dest, plane_rgba, width * 4, height, stride_rgba);
@@ -187,9 +187,9 @@ static emscripten::val heif_js_decode_im
     case heif_colorspace_monochrome: {
       assert(heif_image_get_chroma_format(image) ==
              heif_chroma_monochrome);
-      int stride_grey;
-      const uint8_t* plane_grey = heif_image_get_plane_readonly(image,
-                                                                heif_channel_Y, &stride_grey);
+      size_t stride_grey;
+      const uint8_t* plane_grey = heif_image_get_plane_readonly2(image,
+                                                                 heif_channel_Y, &stride_grey);
       data.resize(width * height);
       unsigned char* dest = const_cast<unsigned char*>(data.data());
       strided_copy(dest, plane_grey, width, height, stride_grey);
@@ -202,10 +202,9 @@ static emscripten::val heif_js_decode_im
   result.set("data", std::move(data));
 
   if (heif_image_has_channel(image, heif_channel_Alpha)) {
-    std::basic_string<unsigned char> alpha;
-    int stride_alpha;
-    const uint8_t* plane_alpha = heif_image_get_plane_readonly(image,
-							       heif_channel_Alpha, &stride_alpha);
+    std::vector<unsigned char> alpha;
+    size_t stride_alpha;
+    const uint8_t* plane_alpha = heif_image_get_plane_readonly2(image, heif_channel_Alpha, &stride_alpha);
     alpha.resize(width * height);
     unsigned char* dest = const_cast<unsigned char*>(alpha.data());
     strided_copy(dest, plane_alpha, width, height, stride_alpha);
@@ -265,8 +264,8 @@ static emscripten::val heif_js_decode_im
       emscripten::val val_channel_info = emscripten::val::object();
       val_channel_info.set("id", channel);
 
-      int stride;
-      const uint8_t* plane = heif_image_get_plane_readonly(image, channel, &stride);
+      size_t stride;
+      const uint8_t* plane = heif_image_get_plane_readonly2(image, channel, &stride);
 
       val_channel_info.set("stride", stride);
       val_channel_info.set("data", emscripten::val(emscripten::typed_memory_view(stride * height, plane)));
@@ -329,6 +328,7 @@ EMSCRIPTEN_BINDINGS(libheif) {
     .value("heif_error_Decoder_plugin_error", heif_error_Decoder_plugin_error)
     .value("heif_error_Encoder_plugin_error", heif_error_Encoder_plugin_error)
     .value("heif_error_Encoding_error", heif_error_Encoding_error)
+    .value("heif_error_End_of_sequence", heif_error_End_of_sequence)
     .value("heif_error_Color_profile_does_not_exist", heif_error_Color_profile_does_not_exist)
     .value("heif_error_Canceled", heif_error_Canceled);
     emscripten::enum_<heif_suberror_code>("heif_suberror_code")
@@ -345,6 +345,7 @@ EMSCRIPTEN_BINDINGS(libheif) {
     .value("heif_suberror_No_ftyp_box", heif_suberror_No_ftyp_box)
     .value("heif_suberror_No_idat_box", heif_suberror_No_idat_box)
     .value("heif_suberror_No_meta_box", heif_suberror_No_meta_box)
+    .value("heif_suberror_No_moov_box", heif_suberror_No_moov_box)
     .value("heif_suberror_No_hdlr_box", heif_suberror_No_hdlr_box)
     .value("heif_suberror_No_hvcC_box", heif_suberror_No_hvcC_box)
     .value("heif_suberror_No_vvcC_box", heif_suberror_No_vvcC_box)
diff -pruN 1.19.8-1/libheif/api/libheif/heif_encoding.cc 1.20.1-1/libheif/api/libheif/heif_encoding.cc
--- 1.19.8-1/libheif/api/libheif/heif_encoding.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_encoding.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,810 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_encoding.h"
+#include "api_structs.h"
+#include "context.h"
+#include "init.h"
+#include "plugin_registry.h"
+#include "image-items/overlay.h"
+#include "image-items/tiled.h"
+#include "image-items/grid.h"
+
+#include <cstring>
+
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+
+static struct heif_error error_unsupported_parameter = {heif_error_Usage_error,
+                                                        heif_suberror_Unsupported_parameter,
+                                                        "Unsupported encoder parameter"};
+static struct heif_error error_invalid_parameter_value = {heif_error_Usage_error,
+                                                          heif_suberror_Invalid_parameter_value,
+                                                          "Invalid parameter value"};
+
+
+
+int heif_have_encoder_for_format(enum heif_compression_format format)
+{
+  auto plugin = get_encoder(format);
+  return plugin != nullptr;
+}
+
+
+int heif_get_encoder_descriptors(enum heif_compression_format format,
+                                 const char* name,
+                                 const struct heif_encoder_descriptor** out_encoder_descriptors,
+                                 int count)
+{
+  if (out_encoder_descriptors != nullptr && count <= 0) {
+    return 0;
+  }
+
+  std::vector<const struct heif_encoder_descriptor*> descriptors;
+  descriptors = get_filtered_encoder_descriptors(format, name);
+
+  if (out_encoder_descriptors == nullptr) {
+    return static_cast<int>(descriptors.size());
+  }
+
+  int i;
+  for (i = 0; i < count && static_cast<size_t>(i) < descriptors.size(); i++) {
+    out_encoder_descriptors[i] = descriptors[i];
+  }
+
+  return i;
+}
+
+
+const char* heif_encoder_descriptor_get_name(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->get_plugin_name();
+}
+
+
+const char* heif_encoder_descriptor_get_id_name(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->id_name;
+}
+
+
+
+enum heif_compression_format
+heif_encoder_descriptor_get_compression_format(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->compression_format;
+}
+
+
+int heif_encoder_descriptor_supports_lossy_compression(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->supports_lossy_compression;
+}
+
+
+int heif_encoder_descriptor_supports_lossless_compression(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->supports_lossless_compression;
+}
+
+
+struct heif_error heif_context_get_encoder(struct heif_context* context,
+                                           const struct heif_encoder_descriptor* descriptor,
+                                           struct heif_encoder** encoder)
+{
+  // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation.
+
+  if (!descriptor || !encoder) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(context ? context->context.get() : nullptr);
+  }
+
+  *encoder = new struct heif_encoder(descriptor->plugin);
+  return (*encoder)->alloc();
+}
+
+
+struct heif_error heif_context_get_encoder_for_format(struct heif_context* context,
+                                                      enum heif_compression_format format,
+                                                      struct heif_encoder** encoder)
+{
+  // Note: be aware that context may be NULL as we explicitly allowed that in an earlier documentation.
+
+  if (!encoder) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Null_pointer_argument);
+    return err.error_struct(context ? context->context.get() : nullptr);
+  }
+
+  std::vector<const struct heif_encoder_descriptor*> descriptors;
+  descriptors = get_filtered_encoder_descriptors(format, nullptr);
+
+  if (descriptors.size() > 0) {
+    *encoder = new struct heif_encoder(descriptors[0]->plugin);
+    return (*encoder)->alloc();
+  }
+  else {
+    *encoder = nullptr;
+    Error err(heif_error_Unsupported_filetype, // TODO: is this the right error code?
+              heif_suberror_Unspecified);
+    return err.error_struct(context ? context->context.get() : nullptr);
+  }
+}
+
+
+void heif_encoder_release(struct heif_encoder* encoder)
+{
+  if (encoder) {
+    delete encoder;
+  }
+}
+
+
+const char* heif_encoder_get_name(const struct heif_encoder* encoder)
+{
+  return encoder->plugin->get_plugin_name();
+}
+
+
+// Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is
+// encoder dependent.
+struct heif_error heif_encoder_set_lossy_quality(struct heif_encoder* encoder,
+                                                 int quality)
+{
+  if (!encoder) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
+  }
+
+  return encoder->plugin->set_parameter_quality(encoder->encoder, quality);
+}
+
+
+struct heif_error heif_encoder_set_lossless(struct heif_encoder* encoder, int enable)
+{
+  if (!encoder) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
+  }
+
+  return encoder->plugin->set_parameter_lossless(encoder->encoder, enable);
+}
+
+
+struct heif_error heif_encoder_set_logging_level(struct heif_encoder* encoder, int level)
+{
+  if (!encoder) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(nullptr);
+  }
+
+  if (encoder->plugin->set_parameter_logging_level) {
+    return encoder->plugin->set_parameter_logging_level(encoder->encoder, level);
+  }
+
+  return heif_error_success;
+}
+
+
+const struct heif_encoder_parameter* const* heif_encoder_list_parameters(struct heif_encoder* encoder)
+{
+  return encoder->plugin->list_parameters(encoder->encoder);
+}
+
+
+const char* heif_encoder_parameter_get_name(const struct heif_encoder_parameter* param)
+{
+  return param->name;
+}
+
+enum heif_encoder_parameter_type
+heif_encoder_parameter_get_type(const struct heif_encoder_parameter* param)
+{
+  return param->type;
+}
+
+
+struct heif_error
+heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_parameter* param,
+                                               int* have_minimum_maximum,
+                                               int* minimum, int* maximum)
+{
+  if (param->type != heif_encoder_parameter_type_integer) {
+    return error_unsupported_parameter; // TODO: correct error ?
+  }
+
+  if (param->integer.have_minimum_maximum) {
+    if (minimum) {
+      *minimum = param->integer.minimum;
+    }
+
+    if (maximum) {
+      *maximum = param->integer.maximum;
+    }
+  }
+
+  if (have_minimum_maximum) {
+    *have_minimum_maximum = param->integer.have_minimum_maximum;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter* param,
+                                                                  int* have_minimum, int* have_maximum,
+                                                                  int* minimum, int* maximum,
+                                                                  int* num_valid_values,
+                                                                  const int** out_integer_array)
+{
+  if (param->type != heif_encoder_parameter_type_integer) {
+    return error_unsupported_parameter; // TODO: correct error ?
+  }
+
+
+  // --- range of values
+
+  if (param->integer.have_minimum_maximum) {
+    if (minimum) {
+      *minimum = param->integer.minimum;
+    }
+
+    if (maximum) {
+      *maximum = param->integer.maximum;
+    }
+  }
+
+  if (have_minimum) {
+    *have_minimum = param->integer.have_minimum_maximum;
+  }
+
+  if (have_maximum) {
+    *have_maximum = param->integer.have_minimum_maximum;
+  }
+
+
+  // --- set of valid values
+
+  if (param->integer.num_valid_values > 0) {
+    if (out_integer_array) {
+      *out_integer_array = param->integer.valid_values;
+    }
+  }
+
+  if (num_valid_values) {
+    *num_valid_values = param->integer.num_valid_values;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error
+heif_encoder_parameter_get_valid_string_values(const struct heif_encoder_parameter* param,
+                                               const char* const** out_stringarray)
+{
+  if (param->type != heif_encoder_parameter_type_string) {
+    return error_unsupported_parameter; // TODO: correct error ?
+  }
+
+  if (out_stringarray) {
+    *out_stringarray = param->string.valid_values;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_encoder_set_parameter_integer(struct heif_encoder* encoder,
+                                                     const char* parameter_name,
+                                                     int value)
+{
+  // --- check if parameter is valid
+
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+
+      int have_minimum = 0, have_maximum = 0, minimum = 0, maximum = 0, num_valid_values = 0;
+      const int* valid_values = nullptr;
+      heif_error err = heif_encoder_parameter_get_valid_integer_values((*params), &have_minimum, &have_maximum,
+                                                                       &minimum, &maximum,
+                                                                       &num_valid_values,
+                                                                       &valid_values);
+      if (err.code) {
+        return err;
+      }
+
+      if ((have_minimum && value < minimum) ||
+          (have_maximum && value > maximum)) {
+        return error_invalid_parameter_value;
+      }
+
+      if (num_valid_values > 0) {
+        bool found = false;
+        for (int i = 0; i < num_valid_values; i++) {
+          if (valid_values[i] == value) {
+            found = true;
+            break;
+          }
+        }
+
+        if (!found) {
+          return error_invalid_parameter_value;
+        }
+      }
+    }
+  }
+
+
+  // --- parameter is ok, pass it to the encoder plugin
+
+  return encoder->plugin->set_parameter_integer(encoder->encoder, parameter_name, value);
+}
+
+struct heif_error heif_encoder_get_parameter_integer(struct heif_encoder* encoder,
+                                                     const char* parameter_name,
+                                                     int* value_ptr)
+{
+  return encoder->plugin->get_parameter_integer(encoder->encoder, parameter_name, value_ptr);
+}
+
+
+struct heif_error heif_encoder_parameter_integer_valid_range(struct heif_encoder* encoder,
+                                                             const char* parameter_name,
+                                                             int* have_minimum_maximum,
+                                                             int* minimum, int* maximum)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+      return heif_encoder_parameter_get_valid_integer_range(*params, have_minimum_maximum,
+                                                            minimum, maximum);
+    }
+  }
+
+  return error_unsupported_parameter;
+}
+
+struct heif_error heif_encoder_set_parameter_boolean(struct heif_encoder* encoder,
+                                                     const char* parameter_name,
+                                                     int value)
+{
+  return encoder->plugin->set_parameter_boolean(encoder->encoder, parameter_name, value);
+}
+
+struct heif_error heif_encoder_get_parameter_boolean(struct heif_encoder* encoder,
+                                                     const char* parameter_name,
+                                                     int* value_ptr)
+{
+  return encoder->plugin->get_parameter_boolean(encoder->encoder, parameter_name, value_ptr);
+}
+
+struct heif_error heif_encoder_set_parameter_string(struct heif_encoder* encoder,
+                                                    const char* parameter_name,
+                                                    const char* value)
+{
+  return encoder->plugin->set_parameter_string(encoder->encoder, parameter_name, value);
+}
+
+struct heif_error heif_encoder_get_parameter_string(struct heif_encoder* encoder,
+                                                    const char* parameter_name,
+                                                    char* value_ptr, int value_size)
+{
+  return encoder->plugin->get_parameter_string(encoder->encoder, parameter_name,
+                                               value_ptr, value_size);
+}
+
+struct heif_error heif_encoder_parameter_string_valid_values(struct heif_encoder* encoder,
+                                                             const char* parameter_name,
+                                                             const char* const** out_stringarray)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+      return heif_encoder_parameter_get_valid_string_values(*params, out_stringarray);
+    }
+  }
+
+  return error_unsupported_parameter;
+}
+
+struct heif_error heif_encoder_parameter_integer_valid_values(struct heif_encoder* encoder,
+                                                              const char* parameter_name,
+                                                              int* have_minimum, int* have_maximum,
+                                                              int* minimum, int* maximum,
+                                                              int* num_valid_values,
+                                                              const int** out_integer_array)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+      return heif_encoder_parameter_get_valid_integer_values(*params, have_minimum, have_maximum, minimum, maximum,
+                                                             num_valid_values, out_integer_array);
+    }
+  }
+
+  return error_unsupported_parameter;
+}
+
+
+static bool parse_boolean(const char* value)
+{
+  if (strcmp(value, "true") == 0) {
+    return true;
+  }
+  else if (strcmp(value, "false") == 0) {
+    return false;
+  }
+  else if (strcmp(value, "1") == 0) {
+    return true;
+  }
+  else if (strcmp(value, "0") == 0) {
+    return false;
+  }
+
+  return false;
+}
+
+
+struct heif_error heif_encoder_set_parameter(struct heif_encoder* encoder,
+                                             const char* parameter_name,
+                                             const char* value)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+      switch ((*params)->type) {
+        case heif_encoder_parameter_type_integer:
+          return heif_encoder_set_parameter_integer(encoder, parameter_name, atoi(value));
+
+        case heif_encoder_parameter_type_boolean:
+          return heif_encoder_set_parameter_boolean(encoder, parameter_name, parse_boolean(value));
+
+        case heif_encoder_parameter_type_string:
+          return heif_encoder_set_parameter_string(encoder, parameter_name, value);
+          break;
+      }
+
+      return heif_error_success;
+    }
+  }
+
+  return heif_encoder_set_parameter_string(encoder, parameter_name, value);
+
+  //return error_unsupported_parameter;
+}
+
+
+struct heif_error heif_encoder_get_parameter(struct heif_encoder* encoder,
+                                             const char* parameter_name,
+                                             char* value_ptr, int value_size)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+      switch ((*params)->type) {
+        case heif_encoder_parameter_type_integer: {
+          int value;
+          struct heif_error error = heif_encoder_get_parameter_integer(encoder, parameter_name, &value);
+          if (error.code) {
+            return error;
+          }
+          else {
+            snprintf(value_ptr, value_size, "%d", value);
+          }
+        }
+          break;
+
+        case heif_encoder_parameter_type_boolean: {
+          int value;
+          struct heif_error error = heif_encoder_get_parameter_boolean(encoder, parameter_name, &value);
+          if (error.code) {
+            return error;
+          }
+          else {
+            snprintf(value_ptr, value_size, "%d", value);
+          }
+        }
+          break;
+
+        case heif_encoder_parameter_type_string: {
+          struct heif_error error = heif_encoder_get_parameter_string(encoder, parameter_name,
+                                                                      value_ptr, value_size);
+          if (error.code) {
+            return error;
+          }
+        }
+          break;
+      }
+
+      return heif_error_success;
+    }
+  }
+
+  return error_unsupported_parameter;
+}
+
+
+int heif_encoder_has_default(struct heif_encoder* encoder,
+                             const char* parameter_name)
+{
+  for (const struct heif_encoder_parameter* const* params = heif_encoder_list_parameters(encoder);
+       *params;
+       params++) {
+    if (strcmp((*params)->name, parameter_name) == 0) {
+
+      if ((*params)->version >= 2) {
+        return (*params)->has_default;
+      }
+      else {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+
+static void set_default_encoding_options(heif_encoding_options& options)
+{
+  options.version = 7;
+
+  options.save_alpha_channel = true;
+  options.macOS_compatibility_workaround = false;
+  options.save_two_colr_boxes_when_ICC_and_nclx_available = false;
+  options.output_nclx_profile = nullptr;
+  options.macOS_compatibility_workaround_no_nclx_profile = false;
+  options.image_orientation = heif_orientation_normal;
+
+  options.color_conversion_options.version = 1;
+  options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
+  options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
+  options.color_conversion_options.only_use_preferred_chroma_algorithm = false;
+
+  options.prefer_uncC_short_form = true;
+}
+
+
+heif_encoding_options* heif_encoding_options_alloc()
+{
+  auto options = new heif_encoding_options;
+
+  set_default_encoding_options(*options);
+
+  return options;
+}
+
+
+void heif_encoding_options_copy(heif_encoding_options* dst, const heif_encoding_options*  src)
+{
+  if (src == nullptr) {
+    return;
+  }
+
+  int min_version = std::min(dst->version, src->version);
+
+  switch (min_version) {
+    case 7:
+      dst->prefer_uncC_short_form = src->prefer_uncC_short_form;
+      [[fallthrough]];
+    case 6:
+      dst->color_conversion_options = src->color_conversion_options;
+      [[fallthrough]];
+    case 5:
+      dst->image_orientation = src->image_orientation;
+      [[fallthrough]];
+    case 4:
+      dst->output_nclx_profile = src->output_nclx_profile;
+      dst->macOS_compatibility_workaround_no_nclx_profile = src->macOS_compatibility_workaround_no_nclx_profile;
+      [[fallthrough]];
+    case 3:
+      dst->save_two_colr_boxes_when_ICC_and_nclx_available = src->save_two_colr_boxes_when_ICC_and_nclx_available;
+      [[fallthrough]];
+    case 2:
+      dst->macOS_compatibility_workaround = src->macOS_compatibility_workaround;
+      [[fallthrough]];
+    case 1:
+      dst->save_alpha_channel = src->save_alpha_channel;
+  }
+}
+
+
+void heif_encoding_options_free(heif_encoding_options* options)
+{
+  delete options;
+}
+
+struct heif_error heif_context_encode_image(struct heif_context* ctx,
+                                            const struct heif_image* input_image,
+                                            struct heif_encoder* encoder,
+                                            const struct heif_encoding_options* input_options,
+                                            struct heif_image_handle** out_image_handle)
+{
+  if (!encoder) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
+  }
+
+  if (out_image_handle) {
+    *out_image_handle = nullptr;
+  }
+
+  heif_encoding_options options;
+  heif_color_profile_nclx nclx;
+  set_default_encoding_options(options);
+  if (input_options) {
+    heif_encoding_options_copy(&options, input_options);
+
+    if (options.output_nclx_profile == nullptr) {
+      auto input_nclx = input_image->image->get_color_profile_nclx();
+      if (input_nclx) {
+        options.output_nclx_profile = &nclx;
+        nclx.version = 1;
+        nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries();
+        nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics();
+        nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients();
+        nclx.full_range_flag = input_nclx->get_full_range_flag();
+      }
+    }
+  }
+
+  auto encodingResult = ctx->context->encode_image(input_image->image,
+                                                   encoder,
+                                                   options,
+                                                   heif_image_input_class_normal);
+  if (encodingResult.error != Error::Ok) {
+    return encodingResult.error.error_struct(ctx->context.get());
+  }
+
+  std::shared_ptr<ImageItem> image = *encodingResult;
+
+  // mark the new image as primary image
+
+  if (ctx->context->is_primary_image_set() == false) {
+    ctx->context->set_primary_image(image);
+  }
+
+  if (out_image_handle) {
+    *out_image_handle = new heif_image_handle;
+    (*out_image_handle)->image = std::move(image);
+    (*out_image_handle)->context = ctx->context;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_context_add_overlay_image(struct heif_context* ctx,
+                                                 uint32_t image_width,
+                                                 uint32_t image_height,
+                                                 uint16_t nImages,
+                                                 const heif_item_id* image_ids,
+                                                 int32_t* offsets,
+                                                 const uint16_t background_rgba[4],
+                                                 struct heif_image_handle** out_iovl_image_handle)
+{
+  if (!image_ids) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
+  }
+  else if (nImages == 0) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
+  }
+
+
+  std::vector<heif_item_id> refs;
+  refs.insert(refs.end(), image_ids, image_ids + nImages);
+
+  ImageOverlay overlay;
+  overlay.set_canvas_size(image_width, image_height);
+
+  if (background_rgba) {
+    overlay.set_background_color(background_rgba);
+  }
+
+  for (uint16_t i=0;i<nImages;i++) {
+    overlay.add_image_on_top(image_ids[i],
+                             offsets ? offsets[2 * i] : 0,
+                             offsets ? offsets[2 * i + 1] : 0);
+  }
+
+  Result<std::shared_ptr<ImageItem_Overlay>> addImageResult = ImageItem_Overlay::add_new_overlay_item(ctx->context.get(), overlay);
+
+  if (addImageResult.error != Error::Ok) {
+    return addImageResult.error.error_struct(ctx->context.get());
+  }
+
+  std::shared_ptr<ImageItem> iovlimage = addImageResult.value;
+
+
+  if (out_iovl_image_handle) {
+    *out_iovl_image_handle = new heif_image_handle;
+    (*out_iovl_image_handle)->image = std::move(iovlimage);
+    (*out_iovl_image_handle)->context = ctx->context;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_context_set_primary_image(struct heif_context* ctx,
+                                                 struct heif_image_handle* image_handle)
+{
+  ctx->context->set_primary_image(image_handle->image);
+
+  return heif_error_success;
+}
+
+
+void heif_context_set_major_brand(struct heif_context* ctx,
+                                  heif_brand2 major_brand)
+{
+  auto ftyp = ctx->context->get_heif_file()->get_ftyp_box();
+  ftyp->set_major_brand(major_brand);
+  ftyp->add_compatible_brand(major_brand);
+}
+
+
+void heif_context_add_compatible_brand(struct heif_context* ctx,
+                                       heif_brand2 compatible_brand)
+{
+  ctx->context->get_heif_file()->get_ftyp_box()->add_compatible_brand(compatible_brand);
+}
+
+
+// === DEPRECATED ===
+
+// DEPRECATED: typo in function name
+int heif_encoder_descriptor_supportes_lossy_compression(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->supports_lossy_compression;
+}
+
+
+// DEPRECATED: typo in function name
+int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor* descriptor)
+{
+  return descriptor->plugin->supports_lossless_compression;
+}
+
+
+// DEPRECATED
+int heif_context_get_encoder_descriptors(struct heif_context* ctx,
+                                         enum heif_compression_format format,
+                                         const char* name,
+                                         const struct heif_encoder_descriptor** out_encoder_descriptors,
+                                         int count)
+{
+  return heif_get_encoder_descriptors(format, name, out_encoder_descriptors, count);
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_encoding.h 1.20.1-1/libheif/api/libheif/heif_encoding.h
--- 1.19.8-1/libheif/api/libheif/heif_encoding.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_encoding.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,391 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_ENCODING_H
+#define LIBHEIF_HEIF_ENCODING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_image.h>
+#include <libheif/heif_context.h>
+#include <libheif/heif_brands.h>
+#include <libheif/heif_color.h>
+
+
+// ----- encoder -----
+
+// The encoder used for actually encoding an image.
+typedef struct heif_encoder heif_encoder;
+
+// A description of the encoder's capabilities and name.
+typedef struct heif_encoder_descriptor heif_encoder_descriptor;
+
+// A configuration parameter of the encoder. Each encoder implementation may have a different
+// set of parameters. For the most common settings (e.q. quality), special functions to set
+// the parameters are provided.
+typedef struct heif_encoder_parameter heif_encoder_parameter;
+
+
+// Quick check whether there is an enoder available for the given format.
+// Note that the encoder may be limited to a certain subset of features (e.g. only 8 bit, only lossy).
+// You will have to query the specific capabilities further.
+LIBHEIF_API
+int heif_have_encoder_for_format(enum heif_compression_format format);
+
+// Get a list of available encoders. You can filter the encoders by compression format and name.
+// Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards.
+// The returned list of encoders is sorted by their priority (which is a plugin property).
+// The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr).
+// By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored.
+// Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder().
+LIBHEIF_API
+int heif_get_encoder_descriptors(enum heif_compression_format format_filter,
+                                 const char* name_filter,
+                                 const heif_encoder_descriptor** out_encoders,
+                                 int count);
+
+// Return a long, descriptive name of the encoder (including version information).
+LIBHEIF_API
+const char* heif_encoder_descriptor_get_name(const heif_encoder_descriptor*);
+
+// Return a short, symbolic name for identifying the encoder.
+// This name should stay constant over different encoder versions.
+LIBHEIF_API
+const char* heif_encoder_descriptor_get_id_name(const heif_encoder_descriptor*);
+
+LIBHEIF_API
+enum heif_compression_format
+heif_encoder_descriptor_get_compression_format(const heif_encoder_descriptor*);
+
+LIBHEIF_API
+int heif_encoder_descriptor_supports_lossy_compression(const heif_encoder_descriptor*);
+
+LIBHEIF_API
+int heif_encoder_descriptor_supports_lossless_compression(const heif_encoder_descriptor*);
+
+
+// Get an encoder instance that can be used to actually encode images from a descriptor.
+LIBHEIF_API
+heif_error heif_context_get_encoder(heif_context* context,
+                                    const heif_encoder_descriptor*,
+                                    heif_encoder** out_encoder);
+
+// Get an encoder for the given compression format. If there are several encoder plugins
+// for this format, the encoder with the highest plugin priority will be returned.
+LIBHEIF_API
+heif_error heif_context_get_encoder_for_format(heif_context* context,
+                                               enum heif_compression_format format,
+                                               heif_encoder**);
+
+// You have to release the encoder after use.
+LIBHEIF_API
+void heif_encoder_release(heif_encoder*);
+
+// Get the encoder name from the encoder itself.
+LIBHEIF_API
+const char* heif_encoder_get_name(const heif_encoder*);
+
+
+// --- Encoder Parameters ---
+
+// Libheif supports settings parameters through specialized functions and through
+// generic functions by parameter name. Sometimes, the same parameter can be set
+// in both ways.
+// We consider it best practice to use the generic parameter functions only in
+// dynamically generated user interfaces, as no guarantees are made that some specific
+// parameter names are supported by all plugins.
+
+
+// Set a 'quality' factor (0-100). How this is mapped to actual encoding parameters is
+// encoder dependent.
+LIBHEIF_API
+heif_error heif_encoder_set_lossy_quality(heif_encoder*, int quality);
+
+LIBHEIF_API
+heif_error heif_encoder_set_lossless(heif_encoder*, int enable);
+
+// level should be between 0 (= none) to 4 (= full)
+LIBHEIF_API
+heif_error heif_encoder_set_logging_level(heif_encoder*, int level);
+
+// Get a generic list of encoder parameters.
+// Each encoder may define its own, additional set of parameters.
+// You do not have to free the returned list.
+LIBHEIF_API
+const heif_encoder_parameter* const* heif_encoder_list_parameters(heif_encoder*);
+
+// Return the parameter name.
+LIBHEIF_API
+const char* heif_encoder_parameter_get_name(const heif_encoder_parameter*);
+
+
+enum heif_encoder_parameter_type
+{
+  heif_encoder_parameter_type_integer = 1,
+  heif_encoder_parameter_type_boolean = 2,
+  heif_encoder_parameter_type_string = 3
+};
+
+// Return the parameter type.
+LIBHEIF_API
+enum heif_encoder_parameter_type heif_encoder_parameter_get_type(const heif_encoder_parameter*);
+
+// DEPRECATED. Use heif_encoder_parameter_get_valid_integer_values() instead.
+LIBHEIF_API
+heif_error heif_encoder_parameter_get_valid_integer_range(const heif_encoder_parameter*,
+                                                          int* have_minimum_maximum,
+                                                          int* minimum, int* maximum);
+
+// If integer is limited by a range, have_minimum and/or have_maximum will be != 0 and *minimum, *maximum is set.
+// If integer is limited by a fixed set of values, *num_valid_values will be >0 and *out_integer_array is set.
+LIBHEIF_API
+heif_error heif_encoder_parameter_get_valid_integer_values(const heif_encoder_parameter*,
+                                                           int* have_minimum, int* have_maximum,
+                                                           int* minimum, int* maximum,
+                                                           int* num_valid_values,
+                                                           const int** out_integer_array);
+
+LIBHEIF_API
+heif_error heif_encoder_parameter_get_valid_string_values(const heif_encoder_parameter*,
+                                                          const char* const** out_stringarray);
+
+
+LIBHEIF_API
+heif_error heif_encoder_set_parameter_integer(heif_encoder*,
+                                              const char* parameter_name,
+                                              int value);
+
+LIBHEIF_API
+heif_error heif_encoder_get_parameter_integer(heif_encoder*,
+                                              const char* parameter_name,
+                                              int* value);
+
+// TODO: name should be changed to heif_encoder_get_valid_integer_parameter_range
+LIBHEIF_API // DEPRECATED.
+heif_error heif_encoder_parameter_integer_valid_range(heif_encoder*,
+                                                      const char* parameter_name,
+                                                      int* have_minimum_maximum,
+                                                      int* minimum, int* maximum);
+
+LIBHEIF_API
+heif_error heif_encoder_set_parameter_boolean(heif_encoder*,
+                                              const char* parameter_name,
+                                              int value);
+
+LIBHEIF_API
+heif_error heif_encoder_get_parameter_boolean(heif_encoder*,
+                                              const char* parameter_name,
+                                              int* value);
+
+LIBHEIF_API
+heif_error heif_encoder_set_parameter_string(heif_encoder*,
+                                             const char* parameter_name,
+                                             const char* value);
+
+LIBHEIF_API
+heif_error heif_encoder_get_parameter_string(heif_encoder*,
+                                             const char* parameter_name,
+                                             char* value, int value_size);
+
+// returns a NULL-terminated list of valid strings or NULL if all values are allowed
+LIBHEIF_API
+heif_error heif_encoder_parameter_string_valid_values(heif_encoder*,
+                                                      const char* parameter_name,
+                                                      const char* const** out_stringarray);
+
+LIBHEIF_API
+heif_error heif_encoder_parameter_integer_valid_values(heif_encoder*,
+                                                       const char* parameter_name,
+                                                       int* have_minimum, int* have_maximum,
+                                                       int* minimum, int* maximum,
+                                                       int* num_valid_values,
+                                                       const int** out_integer_array);
+
+// Set a parameter of any type to the string value.
+// Integer values are parsed from the string.
+// Boolean values can be "true"/"false"/"1"/"0"
+//
+// x265 encoder specific note:
+// When using the x265 encoder, you may pass any of its parameters by
+// prefixing the parameter name with 'x265:'. Hence, to set the 'ctu' parameter,
+// you will have to set 'x265:ctu' in libheif.
+// Note that there is no checking for valid parameters when using the prefix.
+LIBHEIF_API
+heif_error heif_encoder_set_parameter(heif_encoder*,
+                                      const char* parameter_name,
+                                      const char* value);
+
+// Get the current value of a parameter of any type as a human readable string.
+// The returned string is compatible with heif_encoder_set_parameter().
+LIBHEIF_API
+heif_error heif_encoder_get_parameter(heif_encoder*,
+                                      const char* parameter_name,
+                                      char* value_ptr, int value_size);
+
+// Query whether a specific parameter has a default value.
+LIBHEIF_API
+int heif_encoder_has_default(heif_encoder*,
+                             const char* parameter_name);
+
+
+// The orientation values are defined equal to the EXIF Orientation tag.
+enum heif_orientation
+{
+  heif_orientation_normal = 1,
+  heif_orientation_flip_horizontally = 2,
+  heif_orientation_rotate_180 = 3,
+  heif_orientation_flip_vertically = 4,
+  heif_orientation_rotate_90_cw_then_flip_horizontally = 5,
+  heif_orientation_rotate_90_cw = 6,
+  heif_orientation_rotate_90_cw_then_flip_vertically = 7,
+  heif_orientation_rotate_270_cw = 8
+};
+
+
+typedef struct heif_encoding_options
+{
+  uint8_t version;
+
+  // version 1 options
+
+  uint8_t save_alpha_channel; // default: true
+
+  // version 2 options
+
+  // DEPRECATED. This option is not required anymore. Its value will be ignored.
+  uint8_t macOS_compatibility_workaround;
+
+  // version 3 options
+
+  uint8_t save_two_colr_boxes_when_ICC_and_nclx_available; // default: false
+
+  // version 4 options
+
+  // Set this to the NCLX parameters to be used in the output image or set to NULL
+  // when the same parameters as in the input image should be used.
+  heif_color_profile_nclx* output_nclx_profile;
+
+  uint8_t macOS_compatibility_workaround_no_nclx_profile;
+
+  // version 5 options
+
+  // libheif will generate irot/imir boxes to match these orientations
+  enum heif_orientation image_orientation;
+
+  // version 6 options
+
+  heif_color_conversion_options color_conversion_options;
+
+  // version 7 options
+
+  // Set this to true to use compressed form of uncC where possible.
+  uint8_t prefer_uncC_short_form;
+
+  // TODO: we should add a flag to force MIAF compatible outputs. E.g. this will put restrictions on grid tile sizes and
+  //       might add a clap box when the grid output size does not match the color subsampling factors.
+  //       Since some of these constraints have to be known before actually encoding the image, "forcing MIAF compatibility"
+  //       could also be a flag in the heif_context.
+} heif_encoding_options;
+
+LIBHEIF_API
+heif_encoding_options* heif_encoding_options_alloc(void);
+
+LIBHEIF_API
+void heif_encoding_options_copy(heif_encoding_options* dst, const heif_encoding_options* src);
+
+LIBHEIF_API
+void heif_encoding_options_free(heif_encoding_options*);
+
+
+// Compress the input image.
+// Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL.
+// 'options' should be NULL for now.
+// The first image added to the context is also automatically set the primary image, but
+// you can change the primary image later with heif_context_set_primary_image().
+LIBHEIF_API
+heif_error heif_context_encode_image(heif_context*,
+                                     const heif_image* image,
+                                     heif_encoder* encoder,
+                                     const heif_encoding_options* options,
+                                     heif_image_handle** out_image_handle);
+
+// offsets[] should either be NULL (all offsets==0) or an array of size 2*nImages with x;y offset pairs.
+// If background_rgba is NULL, the background is transparent.
+LIBHEIF_API
+heif_error heif_context_add_overlay_image(heif_context* ctx,
+                                          uint32_t image_width,
+                                          uint32_t image_height,
+                                          uint16_t nImages,
+                                          const heif_item_id* image_ids,
+                                          int32_t* offsets,
+                                          const uint16_t background_rgba[4],
+                                          heif_image_handle** out_iovl_image_handle);
+
+LIBHEIF_API
+heif_error heif_context_set_primary_image(heif_context*,
+                                          heif_image_handle* image_handle);
+
+// Set the major brand of the file.
+// If this function is not called, the major brand is determined automatically from
+// the image or sequence content.
+LIBHEIF_API
+void heif_context_set_major_brand(heif_context* ctx,
+                                  heif_brand2 major_brand);
+
+// Add a compatible brand that is now added automatically by libheif when encoding images (e.g. some application brands like 'geo1').
+LIBHEIF_API
+void heif_context_add_compatible_brand(heif_context* ctx,
+                                       heif_brand2 compatible_brand);
+
+// --- deprecated functions ---
+
+// DEPRECATED, typo in function name
+LIBHEIF_API
+int heif_encoder_descriptor_supportes_lossy_compression(const heif_encoder_descriptor*);
+
+// DEPRECATED, typo in function name
+LIBHEIF_API
+int heif_encoder_descriptor_supportes_lossless_compression(const heif_encoder_descriptor*);
+
+// DEPRECATED: use heif_get_encoder_descriptors() instead.
+// Get a list of available encoders. You can filter the encoders by compression format and name.
+// Use format_filter==heif_compression_undefined and name_filter==NULL as wildcards.
+// The returned list of encoders is sorted by their priority (which is a plugin property).
+// The number of encoders is returned, which are not more than 'count' if (out_encoders != nullptr).
+// By setting out_encoders==nullptr, you can query the number of encoders, 'count' is ignored.
+// Note: to get the actual encoder from the descriptors returned here, use heif_context_get_encoder().
+LIBHEIF_API
+int heif_context_get_encoder_descriptors(heif_context*, // TODO: why do we need this parameter?
+                                         enum heif_compression_format format_filter,
+                                         const char* name_filter,
+                                         const heif_encoder_descriptor** out_encoders,
+                                         int count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_entity_groups.cc 1.20.1-1/libheif/api/libheif/heif_entity_groups.cc
--- 1.19.8-1/libheif/api/libheif/heif_entity_groups.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_entity_groups.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,98 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_entity_groups.h"
+#include "box.h"
+#include "api_structs.h"
+#include "file.h"
+
+#include <memory>
+#include <vector>
+
+
+struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context* ctx,
+                                                         uint32_t type_filter,
+                                                         heif_item_id item_filter,
+                                                         int* out_num_groups)
+{
+  std::shared_ptr<Box_grpl> grplBox = ctx->context->get_heif_file()->get_grpl_box();
+  if (!grplBox) {
+    *out_num_groups = 0;
+    return nullptr;
+  }
+
+  std::vector<std::shared_ptr<Box>> all_entity_group_boxes = grplBox->get_all_child_boxes();
+  if (all_entity_group_boxes.empty()) {
+    *out_num_groups = 0;
+    return nullptr;
+  }
+
+  // --- filter groups
+
+  std::vector<std::shared_ptr<Box_EntityToGroup>> entity_group_boxes;
+  for (auto& group : all_entity_group_boxes) {
+    if (type_filter != 0 && group->get_short_type() != type_filter) {
+      continue;
+    }
+
+    auto groupBox = std::dynamic_pointer_cast<Box_EntityToGroup>(group);
+    const std::vector<heif_item_id>& items = groupBox->get_item_ids();
+
+    if (item_filter != 0 && std::all_of(items.begin(), items.end(), [item_filter](heif_item_id item) {
+      return item != item_filter;
+    })) {
+      continue;
+    }
+
+    entity_group_boxes.emplace_back(groupBox);
+  }
+
+  // --- convert to C structs
+
+  auto* groups = new heif_entity_group[entity_group_boxes.size()];
+  for (size_t i = 0; i < entity_group_boxes.size(); i++) {
+    const auto& groupBox = entity_group_boxes[i];
+    const std::vector<heif_item_id>& items = groupBox->get_item_ids();
+
+    groups[i].entity_group_id = groupBox->get_group_id();
+    groups[i].entity_group_type = groupBox->get_short_type();
+    groups[i].entities = (items.empty() ? nullptr : new heif_item_id[items.size()]);
+    groups[i].num_entities = static_cast<uint32_t>(items.size());
+
+    if (groups[i].entities) { // avoid clang static analyzer false positive
+      for (size_t k = 0; k < items.size(); k++) {
+        groups[i].entities[k] = items[k];
+      }
+    }
+  }
+
+  *out_num_groups = static_cast<int>(entity_group_boxes.size());
+  return groups;
+}
+
+
+void heif_entity_groups_release(struct heif_entity_group* grp, int num_groups)
+{
+  for (int i=0;i<num_groups;i++) {
+    delete[] grp[i].entities;
+  }
+
+  delete[] grp;
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_entity_groups.h 1.20.1-1/libheif/api/libheif/heif_entity_groups.h
--- 1.19.8-1/libheif/api/libheif/heif_entity_groups.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_entity_groups.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,60 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_ENTITY_GROUPS_H
+#define LIBHEIF_HEIF_ENTITY_GROUPS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libheif/heif_library.h"
+
+
+// ------------------------- entity groups ------------------------
+
+typedef uint32_t heif_entity_group_id;
+
+typedef struct heif_entity_group
+{
+  heif_entity_group_id entity_group_id;
+  uint32_t entity_group_type;  // this is a FourCC constant
+  heif_item_id* entities;
+  uint32_t num_entities;
+} heif_entity_group;
+
+// Use 0 for `type_filter` or `item_filter` to disable the filter.
+// Returns an array of heif_entity_group structs with *out_num_groups entries.
+LIBHEIF_API
+heif_entity_group* heif_context_get_entity_groups(const heif_context*,
+                                                  uint32_t type_filter,
+                                                  heif_item_id item_filter,
+                                                  int* out_num_groups);
+
+// Release an array of entity groups returned by heif_context_get_entity_groups().
+LIBHEIF_API
+void heif_entity_groups_release(heif_entity_group*, int num_groups);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_error.h 1.20.1-1/libheif/api/libheif/heif_error.h
--- 1.19.8-1/libheif/api/libheif/heif_error.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_error.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,302 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_ERROR_H
+#define LIBHEIF_HEIF_ERROR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+enum heif_error_code
+{
+  // Everything ok, no error occurred.
+  heif_error_Ok = 0,
+
+  // Input file does not exist.
+  heif_error_Input_does_not_exist = 1,
+
+  // Error in input file. Corrupted or invalid content.
+  heif_error_Invalid_input = 2,
+
+  // Input file type is not supported.
+  heif_error_Unsupported_filetype = 3,
+
+  // Image requires an unsupported decoder feature.
+  heif_error_Unsupported_feature = 4,
+
+  // Library API has been used in an invalid way.
+  heif_error_Usage_error = 5,
+
+  // Could not allocate enough memory.
+  heif_error_Memory_allocation_error = 6,
+
+  // The decoder plugin generated an error
+  heif_error_Decoder_plugin_error = 7,
+
+  // The encoder plugin generated an error
+  heif_error_Encoder_plugin_error = 8,
+
+  // Error during encoding or when writing to the output
+  heif_error_Encoding_error = 9,
+
+  // Application has asked for a color profile type that does not exist
+  heif_error_Color_profile_does_not_exist = 10,
+
+  // Error loading a dynamic plugin
+  heif_error_Plugin_loading_error = 11,
+
+  // Operation has been canceled
+  heif_error_Canceled = 12,
+
+  heif_error_End_of_sequence = 13
+};
+
+
+enum heif_suberror_code
+{
+  // no further information available
+  heif_suberror_Unspecified = 0,
+
+  // --- Invalid_input ---
+
+  // End of data reached unexpectedly.
+  heif_suberror_End_of_data = 100,
+
+  // Size of box (defined in header) is wrong
+  heif_suberror_Invalid_box_size = 101,
+
+  // Mandatory 'ftyp' box is missing
+  heif_suberror_No_ftyp_box = 102,
+
+  heif_suberror_No_idat_box = 103,
+
+  heif_suberror_No_meta_box = 104,
+
+  heif_suberror_No_hdlr_box = 105,
+
+  heif_suberror_No_hvcC_box = 106,
+
+  heif_suberror_No_pitm_box = 107,
+
+  heif_suberror_No_ipco_box = 108,
+
+  heif_suberror_No_ipma_box = 109,
+
+  heif_suberror_No_iloc_box = 110,
+
+  heif_suberror_No_iinf_box = 111,
+
+  heif_suberror_No_iprp_box = 112,
+
+  heif_suberror_No_iref_box = 113,
+
+  heif_suberror_No_pict_handler = 114,
+
+  // An item property referenced in the 'ipma' box is not existing in the 'ipco' container.
+  heif_suberror_Ipma_box_references_nonexisting_property = 115,
+
+  // No properties have been assigned to an item.
+  heif_suberror_No_properties_assigned_to_item = 116,
+
+  // Image has no (compressed) data
+  heif_suberror_No_item_data = 117,
+
+  // Invalid specification of image grid (tiled image)
+  heif_suberror_Invalid_grid_data = 118,
+
+  // Tile-images in a grid image are missing
+  heif_suberror_Missing_grid_images = 119,
+
+  heif_suberror_Invalid_clean_aperture = 120,
+
+  // Invalid specification of overlay image
+  heif_suberror_Invalid_overlay_data = 121,
+
+  // Overlay image completely outside of visible canvas area
+  heif_suberror_Overlay_image_outside_of_canvas = 122,
+
+  heif_suberror_Auxiliary_image_type_unspecified = 123,
+
+  heif_suberror_No_or_invalid_primary_item = 124,
+
+  heif_suberror_No_infe_box = 125,
+
+  heif_suberror_Unknown_color_profile_type = 126,
+
+  heif_suberror_Wrong_tile_image_chroma_format = 127,
+
+  heif_suberror_Invalid_fractional_number = 128,
+
+  heif_suberror_Invalid_image_size = 129,
+
+  heif_suberror_Invalid_pixi_box = 130,
+
+  heif_suberror_No_av1C_box = 131,
+
+  heif_suberror_Wrong_tile_image_pixel_depth = 132,
+
+  heif_suberror_Unknown_NCLX_color_primaries = 133,
+
+  heif_suberror_Unknown_NCLX_transfer_characteristics = 134,
+
+  heif_suberror_Unknown_NCLX_matrix_coefficients = 135,
+
+  // Invalid specification of region item
+  heif_suberror_Invalid_region_data = 136,
+
+  // Image has no ispe property
+  heif_suberror_No_ispe_property = 137,
+
+  heif_suberror_Camera_intrinsic_matrix_undefined = 138,
+
+  heif_suberror_Camera_extrinsic_matrix_undefined = 139,
+
+  // Invalid JPEG 2000 codestream - usually a missing marker
+  heif_suberror_Invalid_J2K_codestream = 140,
+
+  heif_suberror_No_vvcC_box = 141,
+
+  // icbr is only needed in some situations, this error is for those cases
+  heif_suberror_No_icbr_box = 142,
+
+  heif_suberror_No_avcC_box = 143,
+
+  // we got a mini box, but could not read it properly
+  heif_suberror_Invalid_mini_box = 149,
+
+  // Decompressing generic compression or header compression data failed (e.g. bitstream corruption)
+  heif_suberror_Decompression_invalid_data = 150,
+
+  heif_suberror_No_moov_box = 151,
+
+  // --- Memory_allocation_error ---
+
+  // A security limit preventing unreasonable memory allocations was exceeded by the input file.
+  // Please check whether the file is valid. If it is, contact us so that we could increase the
+  // security limits further.
+  heif_suberror_Security_limit_exceeded = 1000,
+
+  // There was an error from the underlying compression / decompression library.
+  // One possibility is lack of resources (e.g. memory).
+  heif_suberror_Compression_initialisation_error = 1001,
+
+  // --- Usage_error ---
+
+  // An item ID was used that is not present in the file.
+  heif_suberror_Nonexisting_item_referenced = 2000, // also used for Invalid_input
+
+  // An API argument was given a NULL pointer, which is not allowed for that function.
+  heif_suberror_Null_pointer_argument = 2001,
+
+  // Image channel referenced that does not exist in the image
+  heif_suberror_Nonexisting_image_channel_referenced = 2002,
+
+  // The version of the passed plugin is not supported.
+  heif_suberror_Unsupported_plugin_version = 2003,
+
+  // The version of the passed writer is not supported.
+  heif_suberror_Unsupported_writer_version = 2004,
+
+  // The given (encoder) parameter name does not exist.
+  heif_suberror_Unsupported_parameter = 2005,
+
+  // The value for the given parameter is not in the valid range.
+  heif_suberror_Invalid_parameter_value = 2006,
+
+  // Error in property specification
+  heif_suberror_Invalid_property = 2007,
+
+  // Image reference cycle found in iref
+  heif_suberror_Item_reference_cycle = 2008,
+
+
+  // --- Unsupported_feature ---
+
+  // Image was coded with an unsupported compression method.
+  heif_suberror_Unsupported_codec = 3000,
+
+  // Image is specified in an unknown way, e.g. as tiled grid image (which is supported)
+  heif_suberror_Unsupported_image_type = 3001,
+
+  heif_suberror_Unsupported_data_version = 3002,
+
+  // The conversion of the source image to the requested chroma / colorspace is not supported.
+  heif_suberror_Unsupported_color_conversion = 3003,
+
+  heif_suberror_Unsupported_item_construction_method = 3004,
+
+  heif_suberror_Unsupported_header_compression_method = 3005,
+
+  // Generically compressed data used an unsupported compression method
+  heif_suberror_Unsupported_generic_compression_method = 3006,
+
+  heif_suberror_Unsupported_essential_property = 3007,
+
+  // --- Encoder_plugin_error ---
+
+  heif_suberror_Unsupported_bit_depth = 4000,
+
+
+  // --- Encoding_error ---
+
+  heif_suberror_Cannot_write_output_data = 5000,
+
+  heif_suberror_Encoder_initialization = 5001,
+  heif_suberror_Encoder_encoding = 5002,
+  heif_suberror_Encoder_cleanup = 5003,
+
+  heif_suberror_Too_many_regions = 5004,
+
+
+  // --- Plugin loading error ---
+
+  heif_suberror_Plugin_loading_error = 6000,         // a specific plugin file cannot be loaded
+  heif_suberror_Plugin_is_not_loaded = 6001,         // trying to remove a plugin that is not loaded
+  heif_suberror_Cannot_read_plugin_directory = 6002, // error while scanning the directory for plugins
+  heif_suberror_No_matching_decoder_installed = 6003 // no decoder found for that compression format
+};
+
+
+typedef struct heif_error
+{
+  // main error category
+  enum heif_error_code code;
+
+  // more detailed error code
+  enum heif_suberror_code subcode;
+
+  // textual error message (is always defined, you do not have to check for NULL)
+  const char* message;
+} heif_error;
+
+// Default success return value. Intended for use in user-supplied callback functions.
+LIBHEIF_API extern const heif_error heif_error_success;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_experimental.cc 1.20.1-1/libheif/api/libheif/heif_experimental.cc
--- 1.19.8-1/libheif/api/libheif/heif_experimental.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_experimental.cc	2025-07-02 13:05:31.000000000 +0000
@@ -21,13 +21,14 @@
 #include "heif_experimental.h"
 #include "context.h"
 #include "api_structs.h"
-#include "file.h"
+#include "image-items/unc_image.h"
+#include "image-items/tiled.h"
 
 #include <array>
 #include <cstring>
 #include <memory>
 #include <vector>
-#include <string>
+#include <limits>
 
 
 struct heif_property_camera_intrinsic_matrix
@@ -279,20 +280,223 @@ struct heif_error heif_property_camera_e
 }
 
 
-struct heif_error heif_image_extract_area(const heif_image* srcimg,
-                                          uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
-                                          const heif_security_limits* limits,
-                                          struct heif_image** out_image)
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+
+static struct heif_error error_null_parameter = {heif_error_Usage_error,
+                                                 heif_suberror_Null_pointer_argument,
+                                                 "NULL passed"};
+
+
+struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx,
+                                                        const heif_item_id* layer_item_ids,
+                                                        size_t num_layers,
+    /*
+    uint16_t tile_width,
+    uint16_t tile_height,
+    uint32_t num_layers,
+    const heif_pyramid_layer_info* in_layers,
+     */
+                                                        heif_item_id* out_group_id)
+{
+  if (!layer_item_ids) {
+    return error_null_parameter;
+  }
+
+  if (num_layers == 0) {
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Number of layers cannot be 0."};
+  }
+
+  std::vector<heif_item_id> layers(num_layers);
+  for (size_t i = 0; i < num_layers; i++) {
+    layers[i] = layer_item_ids[i];
+  }
+
+  Result<heif_item_id> result = ctx->context->add_pyramid_group(layers);
+
+  if (result) {
+    if (out_group_id) {
+      *out_group_id = result.value;
+    }
+    return heif_error_success;
+  }
+  else {
+    return result.error.error_struct(ctx->context.get());
+  }
+}
+
+
+struct heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(struct heif_context* ctx, heif_entity_group_id id, int* out_num_layers)
+{
+  if (!out_num_layers) {
+    return nullptr;
+  }
+
+  std::shared_ptr<Box_EntityToGroup> groupBox = ctx->context->get_heif_file()->get_entity_group(id);
+  if (!groupBox) {
+    return nullptr;
+  }
+
+  const auto pymdBox = std::dynamic_pointer_cast<Box_pymd>(groupBox);
+  if (!pymdBox) {
+    return nullptr;
+  }
+
+  const std::vector<Box_pymd::LayerInfo> pymd_layers = pymdBox->get_layers();
+  if (pymd_layers.empty()) {
+    return nullptr;
+  }
+
+  auto items = pymdBox->get_item_ids();
+  assert(items.size() == pymd_layers.size());
+
+  auto* layerInfo = new heif_pyramid_layer_info[pymd_layers.size()];
+  for (size_t i=0; i<pymd_layers.size(); i++) {
+    layerInfo[i].layer_image_id = items[i];
+    layerInfo[i].layer_binning = pymd_layers[i].layer_binning;
+    layerInfo[i].tile_rows_in_layer = pymd_layers[i].tiles_in_layer_row_minus1 + 1;
+    layerInfo[i].tile_columns_in_layer = pymd_layers[i].tiles_in_layer_column_minus1 + 1;
+  }
+
+  *out_num_layers = static_cast<int>(pymd_layers.size());
+
+  return layerInfo;
+}
+
+
+void heif_pyramid_layer_info_release(struct heif_pyramid_layer_info* infos)
+{
+  delete[] infos;
+}
+
+
+struct heif_error heif_image_add_channel(struct heif_image* image,
+                                         enum heif_channel channel,
+                                         int width, int height,
+                                         heif_channel_datatype datatype, int bit_depth)
 {
-  auto extractResult = srcimg->image->extract_image_area(x0,y0,w,h, limits);
-  if (extractResult.error) {
-    return extractResult.error.error_struct(srcimg->image.get());
+  if (auto err = image->image->add_channel(channel, width, height, datatype, bit_depth, nullptr)) {
+    return err.error_struct(image->image.get());
   }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* image, enum heif_channel channel)
+{
+  if (image == nullptr) {
+    return heif_channel_datatype_undefined;
+  }
+
+  return image->image->get_datatype(channel);
+}
+
+
+int heif_image_list_channels(struct heif_image* image,
+                             enum heif_channel** out_channels)
+{
+  if (!image || !out_channels) {
+    return 0;
+  }
+
+  auto channels = image->image->get_channel_set();
+
+  *out_channels = new heif_channel[channels.size()];
+  heif_channel* p = *out_channels;
+  for (heif_channel c : channels) {
+    *p++ = c;
+  }
+
+  assert(channels.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+  return static_cast<int>(channels.size());
+}
 
-  heif_image* area = new heif_image;
-  area->image = extractResult.value;
 
-  *out_image = area;
+void heif_channel_release_list(enum heif_channel** channels)
+{
+  delete[] channels;
+}
+
+
+
+#define heif_image_get_channel_X(name, type, datatype, bits) \
+const type* heif_image_get_channel_ ## name ## _readonly(const struct heif_image* image, \
+                                                         enum heif_channel channel, \
+                                                         size_t* out_stride) \
+{                                                            \
+  if (!image || !image->image) {                             \
+    *out_stride = 0;                                         \
+    return nullptr;                                          \
+  }                                                          \
+                                                             \
+  if (image->image->get_datatype(channel) != datatype) {     \
+    return nullptr;                                          \
+  }                                                          \
+  if (image->image->get_storage_bits_per_pixel(channel) != bits) {     \
+    return nullptr;                                          \
+  }                                                          \
+  return  image->image->get_channel<type>(channel, out_stride);                      \
+}                                                            \
+                                                             \
+type* heif_image_get_channel_ ## name (struct heif_image* image, \
+                                       enum heif_channel channel, \
+                                       size_t* out_stride)      \
+{                                                            \
+  if (!image || !image->image) {                             \
+    *out_stride = 0;                                         \
+    return nullptr;                                          \
+  }                                                          \
+                                                             \
+  if (image->image->get_datatype(channel) != datatype) {     \
+    return nullptr;                                          \
+  }                                                          \
+  if (image->image->get_storage_bits_per_pixel(channel) != bits) {     \
+    return nullptr;                                          \
+  }                                                          \
+  return image->image->get_channel<type>(channel, out_stride); \
+}
+
+heif_image_get_channel_X(uint16, uint16_t, heif_channel_datatype_unsigned_integer, 16)
+heif_image_get_channel_X(uint32, uint32_t, heif_channel_datatype_unsigned_integer, 32)
+heif_image_get_channel_X(uint64, uint64_t, heif_channel_datatype_unsigned_integer, 64)
+heif_image_get_channel_X(int16, int16_t, heif_channel_datatype_signed_integer, 16)
+heif_image_get_channel_X(int32, int32_t, heif_channel_datatype_signed_integer, 32)
+heif_image_get_channel_X(int64, int64_t, heif_channel_datatype_signed_integer, 64)
+heif_image_get_channel_X(float32, float, heif_channel_datatype_floating_point, 32)
+heif_image_get_channel_X(float64, double, heif_channel_datatype_floating_point, 64)
+heif_image_get_channel_X(complex32, heif_complex32, heif_channel_datatype_complex_number, 64)
+heif_image_get_channel_X(complex64, heif_complex64, heif_channel_datatype_complex_number, 64)
+
+
+#endif
+
+
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+struct heif_error heif_context_add_tiled_image(struct heif_context* ctx,
+                                               const struct heif_tiled_image_parameters* parameters,
+                                               const struct heif_encoding_options* options, // TODO: do we need this?
+                                               const struct heif_encoder* encoder,
+                                               struct heif_image_handle** out_grid_image_handle)
+{
+  if (out_grid_image_handle) {
+    *out_grid_image_handle = nullptr;
+  }
+
+  Result<std::shared_ptr<ImageItem_Tiled>> gridImageResult;
+  gridImageResult = ImageItem_Tiled::add_new_tiled_item(ctx->context.get(), parameters, encoder);
+
+  if (gridImageResult.error != Error::Ok) {
+    return gridImageResult.error.error_struct(ctx->context.get());
+  }
+
+  if (out_grid_image_handle) {
+    *out_grid_image_handle = new heif_image_handle;
+    (*out_grid_image_handle)->image = gridImageResult.value;
+    (*out_grid_image_handle)->context = ctx->context;
+  }
 
   return heif_error_success;
 }
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_experimental.h 1.20.1-1/libheif/api/libheif/heif_experimental.h
--- 1.19.8-1/libheif/api/libheif/heif_experimental.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_experimental.h	2025-07-02 13:05:31.000000000 +0000
@@ -29,95 +29,96 @@ extern "C" {
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
 
-  /* ===================================================================================
-   *   This file contains candidate APIs that did not make it into the public API yet.
-   * ===================================================================================
-   */
+/* ===================================================================================
+ *   This file contains candidate APIs that did not make it into the public API yet.
+ * ===================================================================================
+ */
 
 
-  /*
-  heif_item_property_type_camera_intrinsic_matrix = heif_fourcc('c', 'm', 'i', 'n'),
-  heif_item_property_type_camera_extrinsic_matrix = heif_fourcc('c', 'm', 'e', 'x')
+/*
+heif_item_property_type_camera_intrinsic_matrix = heif_fourcc('c', 'm', 'i', 'n'),
+heif_item_property_type_camera_extrinsic_matrix = heif_fourcc('c', 'm', 'e', 'x')
 */
 
-struct heif_property_camera_intrinsic_matrix;
-struct heif_property_camera_extrinsic_matrix;
+typedef struct heif_property_camera_intrinsic_matrix heif_property_camera_intrinsic_matrix;
+typedef struct heif_property_camera_extrinsic_matrix heif_property_camera_extrinsic_matrix;
 
 //LIBHEIF_API
-struct heif_error heif_item_get_property_camera_intrinsic_matrix(const struct heif_context* context,
-                                                                 heif_item_id itemId,
-                                                                 heif_property_id propertyId,
-                                                                 struct heif_property_camera_intrinsic_matrix** out_matrix);
+heif_error heif_item_get_property_camera_intrinsic_matrix(const heif_context* context,
+                                                          heif_item_id itemId,
+                                                          heif_property_id propertyId,
+                                                          heif_property_camera_intrinsic_matrix** out_matrix);
 
 //LIBHEIF_API
-void heif_property_camera_intrinsic_matrix_release(struct heif_property_camera_intrinsic_matrix* matrix);
+void heif_property_camera_intrinsic_matrix_release(heif_property_camera_intrinsic_matrix* matrix);
 
 //LIBHEIF_API
-struct heif_error heif_property_camera_intrinsic_matrix_get_focal_length(const struct heif_property_camera_intrinsic_matrix* matrix,
-                                                                int image_width, int image_height,
-                                                                double* out_focal_length_x,
-                                                                double* out_focal_length_y);
+heif_error heif_property_camera_intrinsic_matrix_get_focal_length(const heif_property_camera_intrinsic_matrix* matrix,
+                                                                  int image_width, int image_height,
+                                                                  double* out_focal_length_x,
+                                                                  double* out_focal_length_y);
 
 //LIBHEIF_API
-struct heif_error heif_property_camera_intrinsic_matrix_get_principal_point(const struct heif_property_camera_intrinsic_matrix* matrix,
-                                                                   int image_width, int image_height,
-                                                                   double* out_principal_point_x,
-                                                                   double* out_principal_point_y);
+heif_error heif_property_camera_intrinsic_matrix_get_principal_point(const heif_property_camera_intrinsic_matrix* matrix,
+                                                                     int image_width, int image_height,
+                                                                     double* out_principal_point_x,
+                                                                     double* out_principal_point_y);
 
 //LIBHEIF_API
-struct heif_error heif_property_camera_intrinsic_matrix_get_skew(const struct heif_property_camera_intrinsic_matrix* matrix,
-                                                        double* out_skew);
+heif_error heif_property_camera_intrinsic_matrix_get_skew(const heif_property_camera_intrinsic_matrix* matrix,
+                                                          double* out_skew);
 
 //LIBHEIF_API
-struct heif_property_camera_intrinsic_matrix* heif_property_camera_intrinsic_matrix_alloc();
+heif_property_camera_intrinsic_matrix* heif_property_camera_intrinsic_matrix_alloc(void);
 
 //LIBHEIF_API
-void heif_property_camera_intrinsic_matrix_set_simple(struct heif_property_camera_intrinsic_matrix* matrix,
-                                             int image_width, int image_height,
-                                             double focal_length, double principal_point_x, double principal_point_y);
+void heif_property_camera_intrinsic_matrix_set_simple(heif_property_camera_intrinsic_matrix* matrix,
+                                                      int image_width, int image_height,
+                                                      double focal_length, double principal_point_x, double principal_point_y);
 
 //LIBHEIF_API
-void heif_property_camera_intrinsic_matrix_set_full(struct heif_property_camera_intrinsic_matrix* matrix,
-                                           int image_width, int image_height,
-                                           double focal_length_x,
-                                           double focal_length_y,
-                                           double principal_point_x, double principal_point_y,
-                                           double skew);
+void heif_property_camera_intrinsic_matrix_set_full(heif_property_camera_intrinsic_matrix* matrix,
+                                                    int image_width, int image_height,
+                                                    double focal_length_x,
+                                                    double focal_length_y,
+                                                    double principal_point_x, double principal_point_y,
+                                                    double skew);
 
 //LIBHEIF_API
-struct heif_error heif_item_add_property_camera_intrinsic_matrix(const struct heif_context* context,
+heif_error heif_item_add_property_camera_intrinsic_matrix(const heif_context* context,
                                                           heif_item_id itemId,
-                                                          const struct heif_property_camera_intrinsic_matrix* matrix,
+                                                          const heif_property_camera_intrinsic_matrix* matrix,
                                                           heif_property_id* out_propertyId);
 
 
 //LIBHEIF_API
-struct heif_error heif_item_get_property_camera_extrinsic_matrix(const struct heif_context* context,
-                                                                 heif_item_id itemId,
-                                                                 heif_property_id propertyId,
-                                                                 struct heif_property_camera_extrinsic_matrix** out_matrix);
+heif_error heif_item_get_property_camera_extrinsic_matrix(const heif_context* context,
+                                                          heif_item_id itemId,
+                                                          heif_property_id propertyId,
+                                                          heif_property_camera_extrinsic_matrix** out_matrix);
 
 //LIBHEIF_API
-void heif_property_camera_extrinsic_matrix_release(struct heif_property_camera_extrinsic_matrix* matrix);
+void heif_property_camera_extrinsic_matrix_release(heif_property_camera_extrinsic_matrix* matrix);
 
 // `out_matrix` must point to a 9-element matrix, which will be filled in row-major order.
 //LIBHEIF_API
-struct heif_error heif_property_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_property_camera_extrinsic_matrix* matrix,
-                                                                            double* out_matrix);
+heif_error heif_property_camera_extrinsic_matrix_get_rotation_matrix(const heif_property_camera_extrinsic_matrix* matrix,
+                                                                     double* out_matrix);
 
 // `out_vector` must point to a 3-element vector, which will be filled with the (X,Y,Z) coordinates (in micrometers).
 //LIBHEIF_API
-struct heif_error heif_property_camera_extrinsic_matrix_get_position_vector(const struct heif_property_camera_extrinsic_matrix* matrix,
-                                                                            int32_t* out_vector);
+heif_error heif_property_camera_extrinsic_matrix_get_position_vector(const heif_property_camera_extrinsic_matrix* matrix,
+                                                                     int32_t* out_vector);
 
 //LIBHEIF_API
-struct heif_error heif_property_camera_extrinsic_matrix_get_world_coordinate_system_id(const struct heif_property_camera_extrinsic_matrix* matrix,
-                                                                                       uint32_t* out_wcs_id);
+heif_error heif_property_camera_extrinsic_matrix_get_world_coordinate_system_id(const heif_property_camera_extrinsic_matrix* matrix,
+                                                                                uint32_t* out_wcs_id);
 #endif
 
 // --- Tiled images
 
-struct heif_tiled_image_parameters {
+typedef struct heif_tiled_image_parameters
+{
   int version;
 
   // --- version 1
@@ -138,79 +139,42 @@ struct heif_tiled_image_parameters {
 
   // boolean flags
   uint8_t tiles_are_sequential;  // TODO: can we derive this automatically
-};
-
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-LIBHEIF_API
-struct heif_error heif_context_add_tiled_image(struct heif_context* ctx,
-                                               const struct heif_tiled_image_parameters* parameters,
-                                               const struct heif_encoding_options* options, // TODO: do we need this?
-                                               const struct heif_encoder* encoder,
-                                               struct heif_image_handle** out_tiled_image_handle);
-#endif
-
-// --- 'unci' images
-
-// This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
-// contain the allowed values.
-enum heif_unci_compression
-{
-  heif_unci_compression_off = 0,
-  //heif_unci_compression_auto = 1,
-  //heif_unci_compression_unknown = 2, // only used when reading unknown method from input file
-  heif_unci_compression_deflate = 3,
-  heif_unci_compression_zlib = 4,
-  heif_unci_compression_brotli = 5
-};
-
-
-struct heif_unci_image_parameters {
-  int version;
-
-  // --- version 1
-
-  uint32_t image_width;
-  uint32_t image_height;
-
-  uint32_t tile_width;
-  uint32_t tile_height;
-
-  enum heif_unci_compression compression; // TODO
-
-  // TODO: interleave type, padding
-};
+} heif_tiled_image_parameters;
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
 LIBHEIF_API
-struct heif_error heif_context_add_unci_image(struct heif_context* ctx,
-                                              const struct heif_unci_image_parameters* parameters,
-                                              const struct heif_encoding_options* encoding_options,
-                                              const struct heif_image* prototype,
-                                              struct heif_image_handle** out_unci_image_handle);
+heif_error heif_context_add_tiled_image(heif_context* ctx,
+                                        const heif_tiled_image_parameters* parameters,
+                                        const heif_encoding_options* options, // TODO: do we need this?
+                                        const heif_encoder* encoder,
+                                        heif_image_handle** out_tiled_image_handle);
 #endif
 
 // --- 'pymd' entity group (pyramid layers)
 
-struct heif_pyramid_layer_info {
+typedef struct heif_pyramid_layer_info
+{
   heif_item_id layer_image_id;
   uint16_t layer_binning;
   uint32_t tile_rows_in_layer;
   uint32_t tile_columns_in_layer;
-};
+} heif_pyramid_layer_info;
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
 // The input images are automatically sorted according to resolution. You can provide them in any order.
 LIBHEIF_API
-struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx,
-                                                        const heif_item_id* layer_item_ids,
-                                                        size_t num_layers,
-                                                        heif_item_id* out_group_id);
+heif_error heif_context_add_pyramid_entity_group(heif_context* ctx,
+                                                 const heif_item_id* layer_item_ids,
+                                                 size_t num_layers,
+                                                 heif_item_id* out_group_id);
 
 LIBHEIF_API
-struct heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(struct heif_context*, heif_entity_group_id id, int* out_num_layers);
+heif_pyramid_layer_info* heif_context_get_pyramid_entity_group_info(heif_context*,
+                                                                    heif_entity_group_id id,
+                                                                    int* out_num_layers);
 
 LIBHEIF_API
-void heif_pyramid_layer_info_release(struct heif_pyramid_layer_info*);
+void heif_pyramid_layer_info_release(heif_pyramid_layer_info*);
 #endif
 
 // --- other pixel datatype support
@@ -226,207 +190,137 @@ enum heif_channel_datatype
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
 LIBHEIF_API
-struct heif_error heif_image_add_channel(struct heif_image* image,
-                                         enum heif_channel channel,
-                                         int width, int height,
-                                         enum heif_channel_datatype datatype, int bit_depth);
+heif_error heif_image_add_channel(heif_image* image,
+                                  enum heif_channel channel,
+                                  int width, int height,
+                                  enum heif_channel_datatype datatype,
+                                  int bit_depth);
 
 
 LIBHEIF_API
-int heif_image_list_channels(struct heif_image*,
+int heif_image_list_channels(heif_image*,
                              enum heif_channel** out_channels);
 
 LIBHEIF_API
 void heif_channel_release_list(enum heif_channel** channels);
 #endif
 
-struct heif_complex32 {
+typedef struct heif_complex32
+{
   float real, imaginary;
-};
+} heif_complex32;
 
-struct heif_complex64 {
+typedef struct heif_complex64
+{
   double real, imaginary;
-};
+} heif_complex64;
 
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
 LIBHEIF_API
-enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* img, enum heif_channel channel);
+enum heif_channel_datatype heif_image_get_datatype(const heif_image* img,
+                                                   enum heif_channel channel);
 
 
 // The 'stride' in all of these functions are in units of the underlying datatype.
 LIBHEIF_API
-const uint16_t* heif_image_get_channel_uint16_readonly(const struct heif_image*,
+const uint16_t* heif_image_get_channel_uint16_readonly(const heif_image*,
                                                        enum heif_channel channel,
                                                        size_t* out_stride);
 
 LIBHEIF_API
-const uint32_t* heif_image_get_channel_uint32_readonly(const struct heif_image*,
+const uint32_t* heif_image_get_channel_uint32_readonly(const heif_image*,
                                                        enum heif_channel channel,
                                                        size_t* out_stride);
 
 LIBHEIF_API
-const uint64_t* heif_image_get_channel_uint64_readonly(const struct heif_image*,
+const uint64_t* heif_image_get_channel_uint64_readonly(const heif_image*,
                                                        enum heif_channel channel,
                                                        size_t* out_stride);
 
 LIBHEIF_API
-const int16_t* heif_image_get_channel_int16_readonly(const struct heif_image*,
+const int16_t* heif_image_get_channel_int16_readonly(const heif_image*,
                                                      enum heif_channel channel,
                                                      size_t* out_stride);
 
 LIBHEIF_API
-const int32_t* heif_image_get_channel_int32_readonly(const struct heif_image*,
+const int32_t* heif_image_get_channel_int32_readonly(const heif_image*,
                                                      enum heif_channel channel,
                                                      size_t* out_stride);
 
 LIBHEIF_API
-const int64_t* heif_image_get_channel_int64_readonly(const struct heif_image*,
+const int64_t* heif_image_get_channel_int64_readonly(const heif_image*,
                                                      enum heif_channel channel,
                                                      size_t* out_stride);
 
 LIBHEIF_API
-const float* heif_image_get_channel_float32_readonly(const struct heif_image*,
+const float* heif_image_get_channel_float32_readonly(const heif_image*,
                                                      enum heif_channel channel,
                                                      size_t* out_stride);
 
 LIBHEIF_API
-const double* heif_image_get_channel_float64_readonly(const struct heif_image*,
+const double* heif_image_get_channel_float64_readonly(const heif_image*,
                                                       enum heif_channel channel,
                                                       size_t* out_stride);
 
 LIBHEIF_API
-const struct heif_complex32* heif_image_get_channel_complex32_readonly(const struct heif_image*,
-                                                                       enum heif_channel channel,
-                                                                       size_t* out_stride);
+const heif_complex32* heif_image_get_channel_complex32_readonly(const heif_image*,
+                                                                enum heif_channel channel,
+                                                                size_t* out_stride);
 
 LIBHEIF_API
-const struct heif_complex64* heif_image_get_channel_complex64_readonly(const struct heif_image*,
-                                                                       enum heif_channel channel,
-                                                                       size_t* out_stride);
+const heif_complex64* heif_image_get_channel_complex64_readonly(const heif_image*,
+                                                                enum heif_channel channel,
+                                                                size_t* out_stride);
 
 LIBHEIF_API
-uint16_t* heif_image_get_channel_uint16(struct heif_image*,
+uint16_t* heif_image_get_channel_uint16(heif_image*,
                                         enum heif_channel channel,
                                         size_t* out_stride);
 
 LIBHEIF_API
-uint32_t* heif_image_get_channel_uint32(struct heif_image*,
+uint32_t* heif_image_get_channel_uint32(heif_image*,
                                         enum heif_channel channel,
                                         size_t* out_stride);
 
 LIBHEIF_API
-uint64_t* heif_image_get_channel_uint64(struct heif_image*,
+uint64_t* heif_image_get_channel_uint64(heif_image*,
                                         enum heif_channel channel,
                                         size_t* out_stride);
 
 LIBHEIF_API
-int16_t* heif_image_get_channel_int16(struct heif_image*,
+int16_t* heif_image_get_channel_int16(heif_image*,
                                       enum heif_channel channel,
                                       size_t* out_stride);
 
 LIBHEIF_API
-int32_t* heif_image_get_channel_int32(struct heif_image*,
+int32_t* heif_image_get_channel_int32(heif_image*,
                                       enum heif_channel channel,
                                       size_t* out_stride);
 
 LIBHEIF_API
-int64_t* heif_image_get_channel_int64(struct heif_image*,
+int64_t* heif_image_get_channel_int64(heif_image*,
                                       enum heif_channel channel,
                                       size_t* out_stride);
 
 LIBHEIF_API
-float* heif_image_get_channel_float32(struct heif_image*,
+float* heif_image_get_channel_float32(heif_image*,
                                       enum heif_channel channel,
                                       size_t* out_stride);
 
 LIBHEIF_API
-double* heif_image_get_channel_float64(struct heif_image*,
+double* heif_image_get_channel_float64(heif_image*,
                                        enum heif_channel channel,
                                        size_t* out_stride);
 
 LIBHEIF_API
-struct heif_complex32* heif_image_get_channel_complex32(struct heif_image*,
-                                                        enum heif_channel channel,
-                                                        size_t* out_stride);
-
-LIBHEIF_API
-struct heif_complex64* heif_image_get_channel_complex64(struct heif_image*,
-                                                        enum heif_channel channel,
-                                                        size_t* out_stride);
-
-
-// ========================= Timestamps =========================
-
-LIBHEIF_API extern const uint64_t heif_tai_clock_info_unknown_time_uncertainty;
-LIBHEIF_API extern const int32_t heif_tai_clock_info_unknown_drift_rate;
-LIBHEIF_API extern const uint64_t heif_unknown_tai_timestamp;
-#endif
-
-struct heif_tai_clock_info
-{
-  uint8_t version;
-
-  // version 1
-
-  uint64_t time_uncertainty;
-  uint32_t clock_resolution;
-  int32_t clock_drift_rate;
-  uint8_t clock_type;
-};
-
-
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-int heif_is_tai_clock_info_drift_rate_undefined(int32_t drift_rate);
-
-
-// Creates a new clock info property if it doesn't already exist.
-LIBHEIF_API
-struct heif_error heif_property_set_clock_info(struct heif_context* ctx,
-                                               heif_item_id itemId,
-                                               const struct heif_tai_clock_info* clock,
-                                               heif_property_id* out_propertyId);
-
-// The `out_clock` struct passed in needs to have the `version` field set so that this
-// function knows which fields it is safe to fill.
-// When the read property is a lower version, the version variable of out_clock will be reduced.
-LIBHEIF_API
-struct heif_error heif_property_get_clock_info(const struct heif_context* ctx,
-                                               heif_item_id itemId,
-                                               struct heif_tai_clock_info* out_clock);
-#endif
-
-struct heif_tai_timestamp_packet
-{
-  uint8_t version;
-
-  // version 1
-
-  uint64_t tai_timestamp;
-  uint8_t synchronization_state;         // bool
-  uint8_t timestamp_generation_failure;  // bool
-  uint8_t timestamp_is_modified;         // bool
-};
-
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-
-// Creates a new TAI timestamp property if one doesn't already exist for itemId.
-// Creates a new clock info property if one doesn't already exist for itemId.
-LIBHEIF_API
-struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx,
-                                                  heif_item_id itemId,
-                                                  struct heif_tai_timestamp_packet* timestamp,
-                                                  heif_property_id* out_propertyId);
-
-LIBHEIF_API
-struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx,
-                                                  heif_item_id itemId,
-                                                  struct heif_tai_timestamp_packet* out_timestamp);
+heif_complex32* heif_image_get_channel_complex32(heif_image*,
+                                                 enum heif_channel channel,
+                                                 size_t* out_stride);
 
 LIBHEIF_API
-heif_error heif_image_extract_area(const heif_image*,
-                                   uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
-                                   const heif_security_limits* limits,
-                                   struct heif_image** out_image);
+heif_complex64* heif_image_get_channel_complex64(heif_image*,
+                                                 enum heif_channel channel,
+                                                 size_t* out_stride);
 #endif
 
 #ifdef __cplusplus
diff -pruN 1.19.8-1/libheif/api/libheif/heif_image.cc 1.20.1-1/libheif/api/libheif/heif_image.cc
--- 1.19.8-1/libheif/api/libheif/heif_image.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_image.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,415 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_image.h"
+#include "heif.h"
+#include "pixelimage.h"
+#include "api_structs.h"
+#include "error.h"
+#include <limits>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <cstring>
+#include <array>
+
+
+
+heif_colorspace heif_image_get_colorspace(const struct heif_image* img)
+{
+  return img->image->get_colorspace();
+}
+
+enum heif_chroma heif_image_get_chroma_format(const struct heif_image* img)
+{
+  return img->image->get_chroma_format();
+}
+
+
+static int uint32_to_int(uint32_t v)
+{
+  if (v == 0 || v > static_cast<uint64_t>(std::numeric_limits<int>::max())) {
+    return -1;
+  }
+  else {
+    return static_cast<int>(v);
+  }
+}
+
+
+int heif_image_get_width(const struct heif_image* img, enum heif_channel channel)
+{
+  return uint32_to_int(img->image->get_width(channel));
+}
+
+
+int heif_image_get_height(const struct heif_image* img, enum heif_channel channel)
+{
+  return uint32_to_int(img->image->get_height(channel));
+}
+
+
+int heif_image_get_primary_width(const struct heif_image* img)
+{
+  if (img->image->get_colorspace() == heif_colorspace_RGB) {
+    if (img->image->get_chroma_format() == heif_chroma_444) {
+      return uint32_to_int(img->image->get_width(heif_channel_G));
+    }
+    else {
+      return uint32_to_int(img->image->get_width(heif_channel_interleaved));
+    }
+  }
+  else {
+    return uint32_to_int(img->image->get_width(heif_channel_Y));
+  }
+}
+
+
+int heif_image_get_primary_height(const struct heif_image* img)
+{
+  if (img->image->get_colorspace() == heif_colorspace_RGB) {
+    if (img->image->get_chroma_format() == heif_chroma_444) {
+      return uint32_to_int(img->image->get_height(heif_channel_G));
+    }
+    else {
+      return uint32_to_int(img->image->get_height(heif_channel_interleaved));
+    }
+  }
+  else {
+    return uint32_to_int(img->image->get_height(heif_channel_Y));
+  }
+}
+
+
+heif_error heif_image_crop(struct heif_image* img,
+                           int left, int right, int top, int bottom)
+{
+  uint32_t w = img->image->get_width();
+  uint32_t h = img->image->get_height();
+
+  if (w==0 || w>0x7FFFFFFF ||
+      h==0 || h>0x7FFFFFFF) {
+    return heif_error{heif_error_Usage_error,
+                      heif_suberror_Invalid_image_size,
+                      "Image size exceeds maximum supported size"};
+  }
+
+  auto cropResult = img->image->crop(left, static_cast<int>(w) - 1 - right, top, static_cast<int>(h) - 1 - bottom, nullptr);
+  if (cropResult.error) {
+    return cropResult.error.error_struct(img->image.get());
+  }
+
+  img->image = cropResult.value;
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_image_extract_area(const heif_image* srcimg,
+                                          uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
+                                          const heif_security_limits* limits,
+                                          struct heif_image** out_image)
+{
+  auto extractResult = srcimg->image->extract_image_area(x0,y0,w,h, limits);
+  if (extractResult.error) {
+    return extractResult.error.error_struct(srcimg->image.get());
+  }
+
+  heif_image* area = new heif_image;
+  area->image = extractResult.value;
+
+  *out_image = area;
+
+  return heif_error_success;
+}
+
+
+int heif_image_get_bits_per_pixel(const struct heif_image* img, enum heif_channel channel)
+{
+  return img->image->get_storage_bits_per_pixel(channel);
+}
+
+
+int heif_image_get_bits_per_pixel_range(const struct heif_image* img, enum heif_channel channel)
+{
+  return img->image->get_bits_per_pixel(channel);
+}
+
+
+int heif_image_has_channel(const struct heif_image* img, enum heif_channel channel)
+{
+  return img->image->has_channel(channel);
+}
+
+
+const uint8_t* heif_image_get_plane_readonly(const struct heif_image* image,
+                                             enum heif_channel channel,
+                                             int* out_stride)
+{
+  if (!out_stride) {
+    return nullptr;
+  }
+
+  if (!image || !image->image) {
+    *out_stride = 0;
+    return nullptr;
+  }
+
+  size_t stride;
+  const auto* p = image->image->get_plane(channel, &stride);
+
+  // TODO: use C++20 std::cmp_greater()
+  if (stride > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
+    return nullptr;
+  }
+
+  *out_stride = static_cast<int>(stride);
+  return p;
+}
+
+
+uint8_t* heif_image_get_plane(struct heif_image* image,
+                              enum heif_channel channel,
+                              int* out_stride)
+{
+  if (!out_stride) {
+    return nullptr;
+  }
+
+  if (!image || !image->image) {
+    *out_stride = 0;
+    return nullptr;
+  }
+
+  size_t stride;
+  uint8_t* p = image->image->get_plane(channel, &stride);
+
+  // TODO: use C++20 std::cmp_greater()
+  if (stride > static_cast<uint32_t>(std::numeric_limits<int>::max())) {
+    return nullptr;
+  }
+
+  *out_stride = static_cast<int>(stride);
+  return p;
+}
+
+
+const uint8_t* heif_image_get_plane_readonly2(const struct heif_image* image,
+                                              enum heif_channel channel,
+                                              size_t* out_stride)
+{
+  if (!out_stride) {
+    return nullptr;
+  }
+
+  if (!image || !image->image) {
+    *out_stride = 0;
+    return nullptr;
+  }
+
+  return image->image->get_plane(channel, out_stride);
+}
+
+
+uint8_t* heif_image_get_plane2(struct heif_image* image,
+                               enum heif_channel channel,
+                               size_t* out_stride)
+{
+  if (!out_stride) {
+    return nullptr;
+  }
+
+  if (!image || !image->image) {
+    *out_stride = 0;
+    return nullptr;
+  }
+
+  return image->image->get_plane(channel, out_stride);
+}
+
+
+struct heif_error heif_image_scale_image(const struct heif_image* input,
+                                         struct heif_image** output,
+                                         int width, int height,
+                                         const struct heif_scaling_options* options)
+{
+  std::shared_ptr<HeifPixelImage> out_img;
+
+  Error err = input->image->scale_nearest_neighbor(out_img, width, height, nullptr);
+  if (err) {
+    return err.error_struct(input->image.get());
+  }
+
+  *output = new heif_image;
+  (*output)->image = std::move(out_img);
+
+  return Error::Ok.error_struct(input->image.get());
+}
+
+
+struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image,
+                                                           uint32_t width, uint32_t height)
+{
+  Error err = image->image->extend_to_size_with_zero(width, height, nullptr);
+  if (err) {
+    return err.error_struct(image->image.get());
+  }
+
+  return heif_error_ok;
+}
+
+
+int heif_image_get_decoding_warnings(struct heif_image* image,
+                                     int first_warning_idx,
+                                     struct heif_error* out_warnings,
+                                     int max_output_buffer_entries)
+{
+  if (max_output_buffer_entries == 0) {
+    return (int) image->image->get_warnings().size();
+  }
+  else {
+    const auto& warnings = image->image->get_warnings();
+    int n;
+    for (n = 0; n + first_warning_idx < (int) warnings.size(); n++) {
+      out_warnings[n] = warnings[n + first_warning_idx].error_struct(image->image.get());
+    }
+    return n;
+  }
+}
+
+void heif_image_add_decoding_warning(struct heif_image* image,
+                                     struct heif_error err)
+{
+  image->image->add_warning(Error(err.code, err.subcode));
+}
+
+
+void heif_image_release(const struct heif_image* img)
+{
+  delete img;
+}
+
+
+void heif_image_get_pixel_aspect_ratio(const struct heif_image* image, uint32_t* aspect_h, uint32_t* aspect_v)
+{
+  image->image->get_pixel_ratio(aspect_h, aspect_v);
+}
+
+
+void heif_image_set_pixel_aspect_ratio(struct heif_image* image, uint32_t aspect_h, uint32_t aspect_v)
+{
+  image->image->set_pixel_ratio(aspect_h, aspect_v);
+}
+
+
+struct heif_error heif_image_create(int width, int height,
+                                    heif_colorspace colorspace,
+                                    heif_chroma chroma,
+                                    struct heif_image** image)
+{
+  if (image == nullptr) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "heif_image_create: NULL passed as image pointer."};
+  }
+
+  // auto-correct colorspace_YCbCr + chroma_monochrome to colorspace_monochrome
+  // TODO: this should return an error in a later version (see below)
+  if (chroma == heif_chroma_monochrome && colorspace == heif_colorspace_YCbCr) {
+    colorspace = heif_colorspace_monochrome;
+
+    std::cerr << "libheif warning: heif_image_create() used with an illegal colorspace/chroma combination. This will return an error in a future version.\n";
+  }
+
+  // return error if invalid colorspace + chroma combination is used
+  auto validChroma = get_valid_chroma_values_for_colorspace(colorspace);
+  if (std::find(validChroma.begin(), validChroma.end(), chroma) == validChroma.end()) {
+    *image = nullptr;
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "Invalid colorspace/chroma combination."};
+  }
+
+  struct heif_image* img = new heif_image;
+  img->image = std::make_shared<HeifPixelImage>();
+
+  img->image->create(width, height, colorspace, chroma);
+
+  *image = img;
+
+  return heif_error_success;
+}
+
+struct heif_error heif_image_add_plane(struct heif_image* image,
+                                       heif_channel channel, int width, int height, int bit_depth)
+{
+  // Note: no security limit, because this is explicitly requested by the user.
+  if (auto err = image->image->add_plane(channel, width, height, bit_depth, nullptr)) {
+    return err.error_struct(image->image.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+struct heif_error heif_image_add_plane_safe(struct heif_image* image,
+                                            heif_channel channel, int width, int height, int bit_depth,
+                                            const heif_security_limits* limits)
+{
+  if (auto err = image->image->add_plane(channel, width, height, bit_depth, limits)) {
+    return err.error_struct(image->image.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+void heif_image_set_premultiplied_alpha(struct heif_image* image,
+                                        int is_premultiplied_alpha)
+{
+  if (image == nullptr) {
+    return;
+  }
+
+  image->image->set_premultiplied_alpha(is_premultiplied_alpha);
+}
+
+
+int heif_image_is_premultiplied_alpha(struct heif_image* image)
+{
+  if (image == nullptr) {
+    return 0;
+  }
+
+  return image->image->is_premultiplied_alpha();
+}
+
+
+struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height)
+{
+  Error err = image->image->extend_padding_to_size(min_physical_width, min_physical_height, false, nullptr);
+  if (err) {
+    return err.error_struct(image->image.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_image.h 1.20.1-1/libheif/api/libheif/heif_image.h
--- 1.19.8-1/libheif/api/libheif/heif_image.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_image.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,352 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_IMAGE_H
+#define LIBHEIF_HEIF_IMAGE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_error.h>
+#include <stddef.h>
+#include <stdint.h>
+
+
+enum heif_chroma
+{
+  heif_chroma_undefined = 99,
+  heif_chroma_monochrome = 0,
+  heif_chroma_420 = 1,
+  heif_chroma_422 = 2,
+  heif_chroma_444 = 3,
+  heif_chroma_interleaved_RGB = 10,
+  heif_chroma_interleaved_RGBA = 11,
+  heif_chroma_interleaved_RRGGBB_BE = 12,   // HDR, big endian.
+  heif_chroma_interleaved_RRGGBBAA_BE = 13, // HDR, big endian.
+  heif_chroma_interleaved_RRGGBB_LE = 14,   // HDR, little endian.
+  heif_chroma_interleaved_RRGGBBAA_LE = 15  // HDR, little endian.
+};
+
+// DEPRECATED ENUM NAMES
+#define heif_chroma_interleaved_24bit  heif_chroma_interleaved_RGB
+#define heif_chroma_interleaved_32bit  heif_chroma_interleaved_RGBA
+
+
+enum heif_colorspace
+{
+  heif_colorspace_undefined = 99,
+
+  // heif_colorspace_YCbCr should be used with one of these heif_chroma values:
+  // * heif_chroma_444
+  // * heif_chroma_422
+  // * heif_chroma_420
+  heif_colorspace_YCbCr = 0,
+
+  // heif_colorspace_RGB should be used with one of these heif_chroma values:
+  // * heif_chroma_444 (for planar RGB)
+  // * heif_chroma_interleaved_RGB
+  // * heif_chroma_interleaved_RGBA
+  // * heif_chroma_interleaved_RRGGBB_BE
+  // * heif_chroma_interleaved_RRGGBBAA_BE
+  // * heif_chroma_interleaved_RRGGBB_LE
+  // * heif_chroma_interleaved_RRGGBBAA_LE
+  heif_colorspace_RGB = 1,
+
+  // heif_colorspace_monochrome should only be used with heif_chroma = heif_chroma_monochrome
+  heif_colorspace_monochrome = 2,
+
+  // Indicates that this image has no visual channels.
+  heif_colorspace_nonvisual = 3
+};
+
+enum heif_channel
+{
+  heif_channel_Y = 0,
+  heif_channel_Cb = 1,
+  heif_channel_Cr = 2,
+  heif_channel_R = 3,
+  heif_channel_G = 4,
+  heif_channel_B = 5,
+  heif_channel_Alpha = 6,
+  heif_channel_interleaved = 10,
+  heif_channel_filter_array = 11,
+  heif_channel_depth = 12,
+  heif_channel_disparity = 13
+};
+
+
+// An heif_image contains a decoded pixel image in various colorspaces, chroma formats,
+// and bit depths.
+
+// Note: when converting images to an interleaved chroma format, the resulting
+// image contains only a single channel of type channel_interleaved with, e.g., 3 bytes per pixel,
+// containing the interleaved R,G,B values.
+
+// Planar RGB images are specified as heif_colorspace_RGB / heif_chroma_444.
+
+typedef struct heif_image heif_image;
+typedef struct heif_image_handle heif_image_handle;
+typedef struct heif_security_limits heif_security_limits;
+
+
+// Get the colorspace format of the image.
+LIBHEIF_API
+enum heif_colorspace heif_image_get_colorspace(const heif_image*);
+
+// Get the chroma format of the image.
+LIBHEIF_API
+enum heif_chroma heif_image_get_chroma_format(const heif_image*);
+
+/**
+ * Get the width of a specified image channel.
+ *
+ * @param img the image to get the width for
+ * @param channel the channel to select
+ * @return the width of the channel in pixels, or -1 the channel does not exist in the image
+ */
+LIBHEIF_API
+int heif_image_get_width(const heif_image* img, enum heif_channel channel);
+
+/**
+ * Get the height of a specified image channel.
+ *
+ * @param img the image to get the height for
+ * @param channel the channel to select
+ * @return the height of the channel in pixels, or -1 the channel does not exist in the image
+ */
+LIBHEIF_API
+int heif_image_get_height(const heif_image* img, enum heif_channel channel);
+
+/**
+ * Get the width of the main channel.
+ *
+ * This is the Y channel in YCbCr or mono, or any in RGB.
+ *
+ * @param img the image to get the primary width for
+ * @return the width in pixels
+ */
+LIBHEIF_API
+int heif_image_get_primary_width(const heif_image* img);
+
+/**
+ * Get the height of the main channel.
+ *
+ * This is the Y channel in YCbCr or mono, or any in RGB.
+ *
+ * @param img the image to get the primary height for
+ * @return the height in pixels
+ */
+LIBHEIF_API
+int heif_image_get_primary_height(const heif_image* img);
+
+LIBHEIF_API
+heif_error heif_image_crop(heif_image* img,
+                           int left, int right, int top, int bottom);
+
+LIBHEIF_API
+heif_error heif_image_extract_area(const heif_image*,
+                                   uint32_t x0, uint32_t y0, uint32_t w, uint32_t h,
+                                   const heif_security_limits* limits,
+                                   heif_image** out_image);
+
+// Get the number of bits per pixel in the given image channel. Returns -1 if
+// a non-existing channel was given.
+// Note that the number of bits per pixel may be different for each color channel.
+// This function returns the number of bits used for storage of each pixel.
+// Especially for HDR images, this is probably not what you want. Have a look at
+// heif_image_get_bits_per_pixel_range() instead.
+LIBHEIF_API
+int heif_image_get_bits_per_pixel(const heif_image*, enum heif_channel channel);
+
+// Get the number of bits per pixel in the given image channel. This function returns
+// the number of bits used for representing the pixel value, which might be smaller
+// than the number of bits used in memory.
+// For example, in 12bit HDR images, this function returns '12', while still 16 bits
+// are reserved for storage. For interleaved RGBA with 12 bit, this function also returns
+// '12', not '48' or '64' (heif_image_get_bits_per_pixel returns 64 in this case).
+LIBHEIF_API
+int heif_image_get_bits_per_pixel_range(const heif_image*, enum heif_channel channel);
+
+LIBHEIF_API
+int heif_image_has_channel(const heif_image*, enum heif_channel channel);
+
+// Get a pointer to the actual pixel data.
+// The 'out_stride' is returned as "bytes per line".
+// When out_stride is NULL, no value will be written.
+// Returns NULL if a non-existing channel was given.
+// Deprecated, use the safer version heif_image_get_plane_readonly2() instead.
+LIBHEIF_API
+const uint8_t* heif_image_get_plane_readonly(const heif_image*,
+                                             enum heif_channel channel,
+                                             int* out_stride);
+
+// Deprecated, use the safer version heif_image_get_plane2() instead.
+LIBHEIF_API
+uint8_t* heif_image_get_plane(heif_image*,
+                              enum heif_channel channel,
+                              int* out_stride);
+
+// These are safer variants of the two functions above.
+// The 'stride' parameter is often multiplied by the image height in the client application.
+// For very large images, this can lead to integer overflows and, consequently, illegal memory accesses.
+// The changed 'stride' parameter types eliminates this common error.
+LIBHEIF_API
+const uint8_t* heif_image_get_plane_readonly2(const heif_image*,
+                                              enum heif_channel channel,
+                                              size_t* out_stride);
+
+LIBHEIF_API
+uint8_t* heif_image_get_plane2(heif_image*,
+                               enum heif_channel channel,
+                               size_t* out_stride);
+
+
+typedef struct heif_scaling_options heif_scaling_options;
+
+// Currently, heif_scaling_options is not defined yet. Pass a NULL pointer.
+LIBHEIF_API
+heif_error heif_image_scale_image(const heif_image* input,
+                                  heif_image** output,
+                                  int width, int height,
+                                  const heif_scaling_options* options);
+
+// Extends the image size to match the given size by extending the right and bottom borders.
+// The border areas are filled with zero.
+LIBHEIF_API
+heif_error heif_image_extend_to_size_fill_with_zero(heif_image* image,
+                                                    uint32_t width, uint32_t height);
+
+// Fills the image decoding warnings into the provided 'out_warnings' array.
+// The size of the array has to be provided in max_output_buffer_entries.
+// If max_output_buffer_entries==0, the number of decoder warnings is returned.
+// The function fills the warnings into the provided buffer, starting with 'first_warning_idx'.
+// It returns the number of warnings filled into the buffer.
+// Note: you can iterate through all warnings by using 'max_output_buffer_entries=1' and iterate 'first_warning_idx'.
+LIBHEIF_API
+int heif_image_get_decoding_warnings(heif_image* image,
+                                     int first_warning_idx,
+                                     heif_error* out_warnings,
+                                     int max_output_buffer_entries);
+
+// This function is only for decoder plugin implementors.
+LIBHEIF_API
+void heif_image_add_decoding_warning(heif_image* image,
+                                     heif_error err);
+
+// Release heif_image.
+LIBHEIF_API
+void heif_image_release(const heif_image*);
+
+LIBHEIF_API
+void heif_image_get_pixel_aspect_ratio(const heif_image*, uint32_t* aspect_h, uint32_t* aspect_v);
+
+LIBHEIF_API
+void heif_image_set_pixel_aspect_ratio(heif_image*, uint32_t aspect_h, uint32_t aspect_v);
+
+
+// --- heif_image allocation
+
+/**
+ * Create a new image of the specified resolution and colorspace.
+ *
+ * <p>This does not allocate memory for the image data. Use {@link heif_image_add_plane} to
+ * add the corresponding planes to match the specified {@code colorspace} and {@code chroma}.
+ *
+ * @param width the width of the image in pixels
+ * @param height the height of the image in pixels
+ * @param colorspace the colorspace of the image
+ * @param chroma the chroma of the image
+ * @param out_image pointer to pointer of the resulting image
+ * @return whether the creation succeeded or there was an error
+*/
+LIBHEIF_API
+heif_error heif_image_create(int width, int height,
+                             enum heif_colorspace colorspace,
+                             enum heif_chroma chroma,
+                             heif_image** out_image);
+
+/**
+ * Add an image plane to the image.
+ *
+ * <p>The image plane needs to match the colorspace and chroma of the image. Note
+ * that this does not need to be a single "planar" format - interleaved pixel channels
+ * can also be used if the chroma is interleaved.
+ *
+ * <p>The indicated bit_depth corresponds to the bit depth per channel. For example,
+ * with an interleaved format like RRGGBB where each color is represented by 10 bits,
+ * the {@code bit_depth} would be {@code 10} rather than {@code 30}.
+ *
+ * <p>For backward compatibility, one can also specify 24bits for RGB and 32bits for RGBA,
+ * instead of the preferred 8 bits. However, this use is deprecated.
+ *
+ * @param image the parent image to add the channel plane to
+ * @param channel the channel of the plane to add
+ * @param width the width of the plane
+ * @param height the height of the plane
+ * @param bit_depth the bit depth per color channel
+ * @return whether the addition succeeded or there was an error
+ *
+ * @note The width and height are usually the same as the parent image, but can be
+ * less for subsampling.
+ *
+ * @note The specified width can differ from the row stride of the resulting image plane.
+ * Always use the result of {@link heif_image_get_plane} or {@link heif_image_get_plane_readonly}
+ * to determine row stride.
+ */
+LIBHEIF_API
+heif_error heif_image_add_plane(heif_image* image,
+                                enum heif_channel channel,
+                                int width, int height, int bit_depth);
+
+/*
+ * The security limits should preferably be the limits from a heif_context.
+ * The memory allocated will then be registered in the memory budget of that context.
+ */
+LIBHEIF_API
+heif_error heif_image_add_plane_safe(heif_image* image,
+                                     enum heif_channel channel,
+                                     int width, int height, int bit_depth,
+                                     const heif_security_limits* limits);
+
+// Signal that the image is premultiplied by the alpha pixel values.
+LIBHEIF_API
+void heif_image_set_premultiplied_alpha(heif_image* image,
+                                        int is_premultiplied_alpha);
+
+LIBHEIF_API
+int heif_image_is_premultiplied_alpha(heif_image* image);
+
+// This function extends the padding of the image so that it has at least the given physical size.
+// The padding border is filled with the pixels along the right/bottom border.
+// This function may be useful if you want to process the image, but have some external padding requirements.
+// The image size will not be modified if it is already larger/equal than the given physical size.
+// I.e. you cannot assume that after calling this function, the stride will be equal to min_physical_width.
+LIBHEIF_API
+heif_error heif_image_extend_padding_to_size(heif_image* image,
+                                             int min_physical_width, int min_physical_height);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_image_handle.cc 1.20.1-1/libheif/api/libheif/heif_image_handle.cc
--- 1.19.8-1/libheif/api/libheif/heif_image_handle.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_image_handle.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,150 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_image_handle.h"
+#include "api_structs.h"
+
+
+void heif_image_handle_release(const struct heif_image_handle* handle)
+{
+  delete handle;
+}
+
+
+int heif_image_handle_is_primary_image(const struct heif_image_handle* handle)
+{
+  return handle->image->is_primary();
+}
+
+
+heif_item_id heif_image_handle_get_item_id(const struct heif_image_handle* handle)
+{
+  return handle->image->get_id();
+}
+
+
+int heif_image_handle_get_width(const struct heif_image_handle* handle)
+{
+  if (handle && handle->image) {
+    return handle->image->get_width();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+int heif_image_handle_get_height(const struct heif_image_handle* handle)
+{
+  if (handle && handle->image) {
+    return handle->image->get_height();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+int heif_image_handle_has_alpha_channel(const struct heif_image_handle* handle)
+{
+  // TODO: for now, also scan the grid tiles for alpha information (issue #708), but depending about
+  // how the discussion about this structure goes forward, we might remove this again.
+
+  return handle->context->has_alpha(handle->image->get_id());   // handle case in issue #708
+  //return handle->image->get_alpha_channel() != nullptr;       // old alpha check that fails on alpha in grid tiles
+}
+
+
+int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle* handle)
+{
+  // TODO: what about images that have the alpha in the grid tiles (issue #708) ?
+  return handle->image->is_premultiplied_alpha();
+}
+
+
+int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle* handle)
+{
+  return handle->image->get_luma_bits_per_pixel();
+}
+
+
+int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle* handle)
+{
+  return handle->image->get_chroma_bits_per_pixel();
+}
+
+
+struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle,
+                                                                      enum heif_colorspace* out_colorspace,
+                                                                      enum heif_chroma* out_chroma)
+{
+  Error err = image_handle->image->get_coded_image_colorspace(out_colorspace, out_chroma);
+  if (err) {
+    return err.error_struct(image_handle->image.get());
+  }
+
+  return heif_error_success;
+}
+
+
+int heif_image_handle_get_ispe_width(const struct heif_image_handle* handle)
+{
+  if (handle && handle->image) {
+    return handle->image->get_ispe_width();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+int heif_image_handle_get_ispe_height(const struct heif_image_handle* handle)
+{
+  if (handle && handle->image) {
+    return handle->image->get_ispe_height();
+  }
+  else {
+    return 0;
+  }
+}
+
+
+int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle* handle, uint32_t* aspect_h, uint32_t* aspect_v)
+{
+  auto pasp = handle->image->get_property<Box_pasp>();
+  if (pasp) {
+    *aspect_h = pasp->hSpacing;
+    *aspect_v = pasp->vSpacing;
+    return 1;
+  }
+  else {
+    *aspect_h = 1;
+    *aspect_v = 1;
+    return 0;
+  }
+}
+
+
+struct heif_context* heif_image_handle_get_context(const struct heif_image_handle* handle)
+{
+  auto ctx = new heif_context();
+  ctx->context = handle->context;
+  return ctx;
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_image_handle.h 1.20.1-1/libheif/api/libheif/heif_image_handle.h
--- 1.19.8-1/libheif/api/libheif/heif_image_handle.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_image_handle.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,120 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_IMAGE_HANDLE_H
+#define LIBHEIF_HEIF_IMAGE_HANDLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_image.h>
+
+
+// ========================= heif_image_handle =========================
+
+// An heif_image_handle is a handle to a logical image in the HEIF file.
+// To get the actual pixel data, you have to decode the handle to an heif_image.
+// An heif_image_handle also gives you access to the thumbnails and Exif data
+// associated with an image.
+
+// Once you obtained an heif_image_handle, you can already release the heif_context,
+// since it is internally ref-counted.
+
+// Release image handle.
+LIBHEIF_API
+void heif_image_handle_release(const heif_image_handle*);
+
+// Check whether the given image_handle is the primary image of the file.
+LIBHEIF_API
+int heif_image_handle_is_primary_image(const heif_image_handle* handle);
+
+LIBHEIF_API
+heif_item_id heif_image_handle_get_item_id(const heif_image_handle* handle);
+
+// Get the resolution of an image.
+LIBHEIF_API
+int heif_image_handle_get_width(const heif_image_handle* handle);
+
+LIBHEIF_API
+int heif_image_handle_get_height(const heif_image_handle* handle);
+
+LIBHEIF_API
+int heif_image_handle_has_alpha_channel(const heif_image_handle*);
+
+LIBHEIF_API
+int heif_image_handle_is_premultiplied_alpha(const heif_image_handle*);
+
+// Returns -1 on error, e.g. if this information is not present in the image.
+// Only defined for images coded in the YCbCr or monochrome colorspace.
+LIBHEIF_API
+int heif_image_handle_get_luma_bits_per_pixel(const heif_image_handle*);
+
+// Returns -1 on error, e.g. if this information is not present in the image.
+// Only defined for images coded in the YCbCr colorspace.
+LIBHEIF_API
+int heif_image_handle_get_chroma_bits_per_pixel(const heif_image_handle*);
+
+// Return the colorspace that libheif proposes to use for decoding.
+// Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images
+// encoded with matrix_coefficients=0 or for images coded natively in RGB.
+// It may also return *_undefined if the file misses relevant information to determine this without decoding.
+// These are only proposed values that avoid colorspace conversions as much as possible.
+// You can still request the output in your preferred colorspace, but this may involve an internal conversion.
+LIBHEIF_API
+heif_error heif_image_handle_get_preferred_decoding_colorspace(const heif_image_handle* image_handle,
+                                                               enum heif_colorspace* out_colorspace,
+                                                               enum heif_chroma* out_chroma);
+
+// Get the image width from the 'ispe' box. This is the original image size without
+// any transformations applied to it. Do not use this unless you know exactly what
+// you are doing.
+LIBHEIF_API
+int heif_image_handle_get_ispe_width(const heif_image_handle* handle);
+
+LIBHEIF_API
+int heif_image_handle_get_ispe_height(const heif_image_handle* handle);
+
+// Returns whether the image has 'pixel aspect ratio information' information. If 0 is returned, the output is filled with the 1:1 default.
+LIBHEIF_API
+int heif_image_handle_get_pixel_aspect_ratio(const heif_image_handle*, uint32_t* aspect_h, uint32_t* aspect_v);
+
+
+// This gets the context associated with the image handle.
+// Note that you have to release the returned context with heif_context_free() in any case.
+//
+// This means: when you have several image-handles that originate from the same file and you get the
+// context of each of them, the returned pointer may be different even though it refers to the same
+// logical context. You have to call heif_context_free() on all those context pointers.
+// After you freed a context pointer, you can still use the context through a different pointer that you
+// might have acquired from elsewhere.
+LIBHEIF_API
+heif_context* heif_image_handle_get_context(const heif_image_handle* handle);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_items.cc 1.20.1-1/libheif/api/libheif/heif_items.cc
--- 1.19.8-1/libheif/api/libheif/heif_items.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_items.cc	2025-07-02 13:05:31.000000000 +0000
@@ -89,6 +89,7 @@ const char* heif_item_get_mime_item_cont
   return infe->get_content_type().c_str();
 }
 
+
 const char* heif_item_get_mime_item_content_encoding(const struct heif_context* ctx, heif_item_id item_id)
 {
   auto infe = ctx->context->get_heif_file()->get_infe_box(item_id);
@@ -114,6 +115,7 @@ const char* heif_item_get_uri_item_uri_t
   return infe->get_item_uri_type().c_str();
 }
 
+
 const char* heif_item_get_item_name(const struct heif_context* ctx, heif_item_id item_id)
 {
   auto infe = ctx->context->get_heif_file()->get_infe_box(item_id);
@@ -123,13 +125,28 @@ const char* heif_item_get_item_name(cons
 }
 
 
+struct heif_error heif_item_set_item_name(struct heif_context* ctx,
+                                          heif_item_id item,
+                                          const char* item_name)
+{
+  auto infe = ctx->context->get_heif_file()->get_infe_box(item);
+  if (!infe) {
+    return heif_error{heif_error_Input_does_not_exist, heif_suberror_Nonexisting_item_referenced, "Item does not exist"};
+  }
+
+  infe->set_item_name(item_name);
+
+  return heif_error_success;
+}
+
+
 struct heif_error heif_item_get_item_data(const struct heif_context* ctx,
-                                             heif_item_id item_id,
-                                             heif_metadata_compression* out_compression_format,
-                                             uint8_t** out_data, size_t* out_data_size)
+                                          heif_item_id item_id,
+                                          heif_metadata_compression* out_compression_format,
+                                          uint8_t** out_data, size_t* out_data_size)
 {
   if (out_data && !out_data_size) {
-      return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "cannot return data with out_data_size==NULL"};
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "cannot return data with out_data_size==NULL"};
   }
 
   std::vector<uint8_t> data;
@@ -167,7 +184,6 @@ void heif_release_item_data(const struct
 }
 
 
-
 size_t heif_context_get_item_references(const struct heif_context* ctx,
                                         heif_item_id from_item_id,
                                         int index,
@@ -215,6 +231,32 @@ void heif_release_item_references(const
 }
 
 
+struct heif_error heif_context_add_item_reference(struct heif_context* ctx,
+                                                  uint32_t reference_type,
+                                                  heif_item_id from_item,
+                                                  heif_item_id to_item)
+{
+  ctx->context->get_heif_file()->add_iref_reference(from_item,
+                                                    reference_type, {to_item});
+
+  return heif_error_success;
+}
+
+struct heif_error heif_context_add_item_references(struct heif_context* ctx,
+                                                   uint32_t reference_type,
+                                                   heif_item_id from_item,
+                                                   const heif_item_id* to_item,
+                                                   int num_to_items)
+{
+  std::vector<heif_item_id> to_refs(to_item, to_item + num_to_items);
+
+  ctx->context->get_heif_file()->add_iref_reference(from_item,
+                                                    reference_type, to_refs);
+
+  return heif_error_success;
+}
+
+
 // ------------------------- writing -------------------------
 
 struct heif_error heif_context_add_item(struct heif_context* ctx,
@@ -291,41 +333,3 @@ struct heif_error heif_context_add_uri_i
 }
 
 
-struct heif_error heif_context_add_item_reference(struct heif_context* ctx,
-                                                  uint32_t reference_type,
-                                                  heif_item_id from_item,
-                                                  heif_item_id to_item)
-{
-  ctx->context->get_heif_file()->add_iref_reference(from_item,
-                                                    reference_type, {to_item});
-
-  return heif_error_success;
-}
-
-struct heif_error heif_context_add_item_references(struct heif_context* ctx,
-                                                   uint32_t reference_type,
-                                                   heif_item_id from_item,
-                                                   const heif_item_id* to_item,
-                                                   int num_to_items)
-{
-  std::vector<heif_item_id> to_refs(to_item, to_item + num_to_items);
-
-  ctx->context->get_heif_file()->add_iref_reference(from_item,
-                                                    reference_type, to_refs);
-
-  return heif_error_success;
-}
-
-struct heif_error heif_item_set_item_name(struct heif_context* ctx,
-                                             heif_item_id item,
-                                             const char* item_name)
-{
-  auto infe = ctx->context->get_heif_file()->get_infe_box(item);
-  if (!infe) {
-    return heif_error{heif_error_Input_does_not_exist, heif_suberror_Nonexisting_item_referenced, "Item does not exist"};
-  }
-
-  infe->set_item_name(item_name);
-
-  return heif_error_success;
-}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_items.h 1.20.1-1/libheif/api/libheif/heif_items.h
--- 1.19.8-1/libheif/api/libheif/heif_items.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_items.h	2025-07-02 13:05:31.000000000 +0000
@@ -38,7 +38,7 @@ extern "C" {
  * @return the number of items
  */
 LIBHEIF_API
-int heif_context_get_number_of_items(const struct heif_context* ctx);
+int heif_context_get_number_of_items(const heif_context* ctx);
 
 /**
  * Get the item identifiers.
@@ -51,7 +51,7 @@ int heif_context_get_number_of_items(con
  * @return the total number of IDs filled into the array, which may be less than {@code count}.
  */
 LIBHEIF_API
-int heif_context_get_list_of_item_IDs(const struct heif_context* ctx,
+int heif_context_get_list_of_item_IDs(const heif_context* ctx,
                                       heif_item_id* ID_array,
                                       int count);
 
@@ -67,13 +67,13 @@ int heif_context_get_list_of_item_IDs(co
  * @return the item type
  */
 LIBHEIF_API
-uint32_t heif_item_get_item_type(const struct heif_context* ctx, heif_item_id item_id);
+uint32_t heif_item_get_item_type(const heif_context* ctx, heif_item_id item_id);
 
 #define heif_item_type_mime   heif_fourcc('m','i','m','e')
 #define heif_item_type_uri    heif_fourcc('u','r','i',' ')
 
 LIBHEIF_API
-int heif_item_is_item_hidden(const struct heif_context* ctx, heif_item_id item_id);
+int heif_item_is_item_hidden(const heif_context* ctx, heif_item_id item_id);
 
 
 /**
@@ -87,7 +87,7 @@ int heif_item_is_item_hidden(const struc
  * @return the item content_type
  */
 LIBHEIF_API
-const char* heif_item_get_mime_item_content_type(const struct heif_context* ctx, heif_item_id item_id);
+const char* heif_item_get_mime_item_content_type(const heif_context* ctx, heif_item_id item_id);
 
 /**
  * Gets the content_encoding for a MIME item.
@@ -102,7 +102,7 @@ const char* heif_item_get_mime_item_cont
  * @return the item content_type
  */
 LIBHEIF_API
-const char* heif_item_get_mime_item_content_encoding(const struct heif_context* ctx, heif_item_id item_id);
+const char* heif_item_get_mime_item_content_encoding(const heif_context* ctx, heif_item_id item_id);
 
 /**
  * Gets the item_uri_type for an item.
@@ -115,15 +115,15 @@ const char* heif_item_get_mime_item_cont
  * @return the item item_uri_type
  */
 LIBHEIF_API
-const char* heif_item_get_uri_item_uri_type(const struct heif_context* ctx, heif_item_id item_id);
+const char* heif_item_get_uri_item_uri_type(const heif_context* ctx, heif_item_id item_id);
 
 LIBHEIF_API
-const char* heif_item_get_item_name(const struct heif_context* ctx, heif_item_id item_id);
+const char* heif_item_get_item_name(const heif_context* ctx, heif_item_id item_id);
 
 LIBHEIF_API
-struct heif_error heif_item_set_item_name(struct heif_context* ctx,
-                                          heif_item_id item,
-                                          const char* item_name);
+heif_error heif_item_set_item_name(heif_context* ctx,
+                                   heif_item_id item,
+                                   const char* item_name);
 
 
 /**
@@ -149,10 +149,10 @@ struct heif_error heif_item_set_item_nam
  * @return whether the call succeeded, or there was an error
  */
 LIBHEIF_API
-struct heif_error heif_item_get_item_data(const struct heif_context* ctx,
-                                          heif_item_id item_id,
-                                          enum heif_metadata_compression* out_compression_format,
-                                          uint8_t** out_data, size_t* out_data_size);
+heif_error heif_item_get_item_data(const heif_context* ctx,
+                                   heif_item_id item_id,
+                                   enum heif_metadata_compression* out_compression_format,
+                                   uint8_t** out_data, size_t* out_data_size);
 
 /**
  * Free the item data.
@@ -164,7 +164,7 @@ struct heif_error heif_item_get_item_dat
  * @param item_data the data to free
  */
 LIBHEIF_API
-void heif_release_item_data(const struct heif_context* ctx, uint8_t** item_data);
+void heif_release_item_data(const heif_context* ctx, uint8_t** item_data);
 
 
 // ------------------------- item references -------------------------
@@ -180,55 +180,55 @@ void heif_release_item_data(const struct
  * @return the number of items that reference the given item. Returns 0 if the index exceeds the number of references.
  */
 LIBHEIF_API
-size_t heif_context_get_item_references(const struct heif_context* ctx,
+size_t heif_context_get_item_references(const heif_context* ctx,
                                         heif_item_id from_item_id,
                                         int index,
                                         uint32_t* out_reference_type_4cc,
                                         heif_item_id** out_references_to);
 
 LIBHEIF_API
-void heif_release_item_references(const struct heif_context* ctx, heif_item_id** references);
+void heif_release_item_references(const heif_context* ctx, heif_item_id** references);
 
 LIBHEIF_API
-struct heif_error heif_context_add_item_reference(struct heif_context* ctx,
-                                                  uint32_t reference_type,
-                                                  heif_item_id from_item,
-                                                  heif_item_id to_item);
+heif_error heif_context_add_item_reference(heif_context* ctx,
+                                           uint32_t reference_type,
+                                           heif_item_id from_item,
+                                           heif_item_id to_item);
 
 LIBHEIF_API
-struct heif_error heif_context_add_item_references(struct heif_context* ctx,
-                                                   uint32_t reference_type,
-                                                   heif_item_id from_item,
-                                                   const heif_item_id* to_item,
-                                                   int num_to_items);
+heif_error heif_context_add_item_references(heif_context* ctx,
+                                            uint32_t reference_type,
+                                            heif_item_id from_item,
+                                            const heif_item_id* to_item,
+                                            int num_to_items);
 
 // ------------------------- adding new items -------------------------
 
 LIBHEIF_API
-struct heif_error heif_context_add_item(struct heif_context* ctx,
-                                        const char* item_type,
-                                        const void* data, int size,
-                                        heif_item_id* out_item_id);
-
-LIBHEIF_API
-struct heif_error heif_context_add_mime_item(struct heif_context* ctx,
-                                             const char* content_type,
-                                             enum heif_metadata_compression content_encoding,
-                                             const void* data, int size,
-                                             heif_item_id* out_item_id);
-
-LIBHEIF_API
-struct heif_error heif_context_add_precompressed_mime_item(struct heif_context* ctx,
-                                                           const char* content_type,
-                                                           const char* content_encoding,
-                                                           const void* data, int size,
-                                                           heif_item_id* out_item_id);
-
-LIBHEIF_API
-struct heif_error heif_context_add_uri_item(struct heif_context* ctx,
-                                            const char* item_uri_type,
-                                            const void* data, int size,
-                                            heif_item_id* out_item_id);
+heif_error heif_context_add_item(heif_context* ctx,
+                                 const char* item_type,
+                                 const void* data, int size,
+                                 heif_item_id* out_item_id);
+
+LIBHEIF_API
+heif_error heif_context_add_mime_item(heif_context* ctx,
+                                      const char* content_type,
+                                      enum heif_metadata_compression content_encoding,
+                                      const void* data, int size,
+                                      heif_item_id* out_item_id);
+
+LIBHEIF_API
+heif_error heif_context_add_precompressed_mime_item(heif_context* ctx,
+                                                    const char* content_type,
+                                                    const char* content_encoding,
+                                                    const void* data, int size,
+                                                    heif_item_id* out_item_id);
+
+LIBHEIF_API
+heif_error heif_context_add_uri_item(heif_context* ctx,
+                                     const char* item_uri_type,
+                                     const void* data, int size,
+                                     heif_item_id* out_item_id);
 
 #ifdef __cplusplus
 }
diff -pruN 1.19.8-1/libheif/api/libheif/heif_library.cc 1.20.1-1/libheif/api/libheif/heif_library.cc
--- 1.19.8-1/libheif/api/libheif/heif_library.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_library.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,110 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_library.h"
+#include "heif_plugin.h"
+#include "api_structs.h"
+#include "plugin_registry.h"
+
+#ifdef _WIN32
+// for _write
+#include <io.h>
+#else
+
+#include <unistd.h>
+
+#endif
+
+#include <cassert>
+
+
+static struct heif_error error_null_parameter = {heif_error_Usage_error,
+                                                 heif_suberror_Null_pointer_argument,
+                                                 "NULL passed"};
+static struct heif_error error_unsupported_plugin_version = {heif_error_Usage_error,
+                                                             heif_suberror_Unsupported_plugin_version,
+                                                             "Unsupported plugin version"};
+
+
+const char* heif_get_version(void)
+{
+  return (LIBHEIF_VERSION);
+}
+
+uint32_t heif_get_version_number(void)
+{
+  return (LIBHEIF_NUMERIC_VERSION);
+}
+
+int heif_get_version_number_major(void)
+{
+  return ((LIBHEIF_NUMERIC_VERSION) >> 24) & 0xFF;
+}
+
+int heif_get_version_number_minor(void)
+{
+  return ((LIBHEIF_NUMERIC_VERSION) >> 16) & 0xFF;
+}
+
+int heif_get_version_number_maintenance(void)
+{
+  return ((LIBHEIF_NUMERIC_VERSION) >> 8) & 0xFF;
+}
+
+
+
+struct heif_error heif_register_decoder_plugin(const heif_decoder_plugin* decoder_plugin)
+{
+  if (!decoder_plugin) {
+    return error_null_parameter;
+  }
+  else if (decoder_plugin->plugin_api_version > heif_decoder_plugin_latest_version) {
+    return error_unsupported_plugin_version;
+  }
+
+  register_decoder(decoder_plugin);
+  return heif_error_success;
+}
+
+struct heif_error heif_register_encoder_plugin(const heif_encoder_plugin* encoder_plugin)
+{
+  if (!encoder_plugin) {
+    return error_null_parameter;
+  }
+  else if (encoder_plugin->plugin_api_version > heif_encoder_plugin_latest_version) {
+    return error_unsupported_plugin_version;
+  }
+
+  register_encoder(encoder_plugin);
+  return heif_error_success;
+}
+
+
+void heif_string_release(const char* str)
+{
+  delete[] str;
+}
+
+
+// DEPRECATED
+struct heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin* decoder_plugin)
+{
+  return heif_register_decoder_plugin(decoder_plugin);
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_library.h 1.20.1-1/libheif/api/libheif/heif_library.h
--- 1.19.8-1/libheif/api/libheif/heif_library.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_library.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,216 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_LIBRARY_H
+#define LIBHEIF_HEIF_LIBRARY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+// API versions table
+//
+// release    dec.options   enc.options   heif_reader   heif_writer   depth.rep   col.profile
+// ------------------------------------------------------------------------------------------
+//  1.0            1           N/A           N/A           N/A           1           N/A
+//  1.1            1           N/A           N/A            1            1           N/A
+//  1.3            1            1             1             1            1           N/A
+//  1.4            1            1             1             1            1            1
+//  1.7            2            1             1             1            1            1
+//  1.9.2          2            2             1             1            1            1
+//  1.10           2            3             1             1            1            1
+//  1.11           2            4             1             1            1            1
+//  1.13           3            4             1             1            1            1
+//  1.14           3            5             1             1            1            1
+//  1.15           4            5             1             1            1            1
+//  1.16           5            6             1             1            1            1
+//  1.18           5            7             1             1            1            1
+//  1.19           6            7             2             1            1            1
+//  1.20           7            7             2             1            1            1
+
+#if (defined(_WIN32) || defined __CYGWIN__) && !defined(LIBHEIF_STATIC_BUILD)
+#ifdef LIBHEIF_EXPORTS
+#define LIBHEIF_API __declspec(dllexport)
+#else
+#define LIBHEIF_API __declspec(dllimport)
+#endif
+#elif defined(HAVE_VISIBILITY) && HAVE_VISIBILITY
+#ifdef LIBHEIF_EXPORTS
+#define LIBHEIF_API __attribute__((__visibility__("default")))
+#else
+#define LIBHEIF_API
+#endif
+#else
+#define LIBHEIF_API
+#endif
+
+#define heif_fourcc(a, b, c, d) ((uint32_t)((a<<24) | (b<<16) | (c<<8) | d))
+
+#include <libheif/heif_version.h>
+#include <libheif/heif_error.h>
+
+
+/* === version numbers === */
+
+// Version string of linked libheif library.
+LIBHEIF_API const char* heif_get_version(void);
+
+// Numeric version of linked libheif library, encoded as 0xHHMMLL00 = hh.mm.ll, where hh, mm, ll is the decimal representation of HH, MM, LL.
+// For example: 0x02150300 is version 2.21.3
+LIBHEIF_API uint32_t heif_get_version_number(void);
+
+// Numeric part "HH" from above. Returned as a decimal number.
+LIBHEIF_API int heif_get_version_number_major(void);
+// Numeric part "MM" from above. Returned as a decimal number.
+LIBHEIF_API int heif_get_version_number_minor(void);
+// Numeric part "LL" from above. Returned as a decimal number.
+LIBHEIF_API int heif_get_version_number_maintenance(void);
+
+// Helper macros to check for given versions of libheif at compile time.
+#define LIBHEIF_MAKE_VERSION(h, m, l) ((h) << 24 | (m) << 16 | (l) << 8)
+#define LIBHEIF_HAVE_VERSION(h, m, l) (LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(h, m, l))
+
+typedef struct heif_context heif_context;
+typedef struct heif_image_handle heif_image_handle;
+
+typedef uint32_t heif_item_id;
+typedef uint32_t heif_property_id;
+
+/**
+ * Free a string returned by libheif in various API functions.
+ * You may pass NULL.
+ */
+LIBHEIF_API
+void heif_string_release(const char*);
+
+
+// ========================= library initialization ======================
+
+typedef struct heif_init_params
+{
+  int version;
+
+  // currently no parameters
+} heif_init_params;
+
+
+/**
+ * Initialise library.
+ *
+ * You should call heif_init() when you start using libheif and heif_deinit() when you are finished.
+ * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit().
+ *
+ * For backwards compatibility, it is not really necessary to call heif_init(), but some library memory objects
+ * will never be freed if you do not call heif_init()/heif_deinit().
+ *
+ * heif_init() will load the external modules installed in the default plugin path. Thus, you need it when you
+ * want to load external plugins from the default path.
+ * Codec plugins that are compiled into the library directly (selected by the compile-time parameters of libheif)
+ * will be available even without heif_init().
+ *
+ * Make sure that you do not have one part of your program use heif_init()/heif_deinit() and another part that does
+ * not use it as the latter may try to use an uninitialized library. If in doubt, enclose everything with init/deinit.
+ *
+ * You may pass nullptr to get default parameters. Currently, no parameters are supported.
+ */
+LIBHEIF_API
+heif_error heif_init(heif_init_params*);
+
+/**
+ * Deinitialise and clean up library.
+ *
+ * You should call heif_init() when you start using libheif and heif_deinit() when you are finished.
+ * These calls are reference counted. Each call to heif_init() should be matched by one call to heif_deinit().
+ *
+ * Note: heif_deinit() must not be called after exit(), for example in a global C++ object's destructor.
+ * If you do, global variables in libheif might have already been released when heif_deinit() is running,
+ * leading to a crash.
+ *
+ * \sa heif_init()
+ */
+LIBHEIF_API
+void heif_deinit(void);
+
+
+// --- Codec plugins ---
+
+// --- Plugins are currently only supported on Unix platforms.
+
+enum heif_plugin_type
+{
+  heif_plugin_type_encoder,
+  heif_plugin_type_decoder
+};
+
+typedef struct heif_plugin_info
+{
+  int version; // version of this info struct
+  enum heif_plugin_type type;
+  const void* plugin;
+  void* internal_handle; // for internal use only
+} heif_plugin_info;
+
+LIBHEIF_API
+heif_error heif_load_plugin(const char* filename, heif_plugin_info const** out_plugin);
+
+LIBHEIF_API
+heif_error heif_load_plugins(const char* directory,
+                             const heif_plugin_info** out_plugins,
+                             int* out_nPluginsLoaded,
+                             int output_array_size);
+
+LIBHEIF_API
+heif_error heif_unload_plugin(const heif_plugin_info* plugin);
+
+// Get a NULL terminated array of the plugin directories that are searched by libheif.
+// This includes the paths specified in the environment variable LIBHEIF_PLUGIN_PATHS and the built-in path
+// (if not overridden by the environment variable).
+LIBHEIF_API
+const char* const* heif_get_plugin_directories(void);
+
+LIBHEIF_API
+void heif_free_plugin_directories(const char* const*);
+
+
+// --- register plugins
+
+typedef struct heif_decoder_plugin heif_decoder_plugin;
+typedef struct heif_encoder_plugin heif_encoder_plugin;
+
+LIBHEIF_API
+heif_error heif_register_decoder_plugin(const heif_decoder_plugin*);
+
+LIBHEIF_API
+heif_error heif_register_encoder_plugin(const heif_encoder_plugin*);
+
+
+// DEPRECATED. Use heif_register_decoder_plugin(const struct heif_decoder_plugin*) instead.
+LIBHEIF_API
+heif_error heif_register_decoder(heif_context* heif, const heif_decoder_plugin*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_metadata.cc 1.20.1-1/libheif/api/libheif/heif_metadata.cc
--- 1.19.8-1/libheif/api/libheif/heif_metadata.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_metadata.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,226 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_metadata.h"
+#include "libheif/heif.h"
+#include "libheif/api_structs.h"
+
+#include "box.h"
+
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <array>
+
+
+int heif_image_handle_get_number_of_metadata_blocks(const struct heif_image_handle* handle,
+                                                    const char* type_filter)
+{
+  int cnt = 0;
+  for (const auto& metadata : handle->image->get_metadata()) {
+    if (type_filter == nullptr ||
+        metadata->item_type == type_filter) {
+      cnt++;
+    }
+  }
+
+  return cnt;
+}
+
+
+int heif_image_handle_get_list_of_metadata_block_IDs(const struct heif_image_handle* handle,
+                                                     const char* type_filter,
+                                                     heif_item_id* ids, int count)
+{
+  int cnt = 0;
+  for (const auto& metadata : handle->image->get_metadata()) {
+    if (type_filter == nullptr ||
+        metadata->item_type == type_filter) {
+      if (cnt < count) {
+        ids[cnt] = metadata->item_id;
+        cnt++;
+      }
+      else {
+        break;
+      }
+    }
+  }
+
+  return cnt;
+}
+
+
+const char* heif_image_handle_get_metadata_type(const struct heif_image_handle* handle,
+                                                heif_item_id metadata_id)
+{
+  for (auto& metadata : handle->image->get_metadata()) {
+    if (metadata->item_id == metadata_id) {
+      return metadata->item_type.c_str();
+    }
+  }
+
+  return nullptr;
+}
+
+
+const char* heif_image_handle_get_metadata_content_type(const struct heif_image_handle* handle,
+                                                        heif_item_id metadata_id)
+{
+  for (auto& metadata : handle->image->get_metadata()) {
+    if (metadata->item_id == metadata_id) {
+      return metadata->content_type.c_str();
+    }
+  }
+
+  return nullptr;
+}
+
+
+size_t heif_image_handle_get_metadata_size(const struct heif_image_handle* handle,
+                                           heif_item_id metadata_id)
+{
+  for (auto& metadata : handle->image->get_metadata()) {
+    if (metadata->item_id == metadata_id) {
+      return metadata->m_data.size();
+    }
+  }
+
+  return 0;
+}
+
+
+struct heif_error heif_image_handle_get_metadata(const struct heif_image_handle* handle,
+                                                 heif_item_id metadata_id,
+                                                 void* out_data)
+{
+  for (auto& metadata : handle->image->get_metadata()) {
+    if (metadata->item_id == metadata_id) {
+
+      if (!metadata->m_data.empty()) {
+        if (out_data == nullptr) {
+          Error err(heif_error_Usage_error,
+                    heif_suberror_Null_pointer_argument);
+          return err.error_struct(handle->image.get());
+        }
+
+        memcpy(out_data,
+               metadata->m_data.data(),
+               metadata->m_data.size());
+      }
+
+      return Error::Ok.error_struct(handle->image.get());
+    }
+  }
+
+  Error err(heif_error_Usage_error,
+            heif_suberror_Nonexisting_item_referenced);
+  return err.error_struct(handle->image.get());
+}
+
+
+const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle,
+                                                         heif_item_id metadata_id)
+{
+  for (auto& metadata : handle->image->get_metadata()) {
+    if (metadata->item_id == metadata_id) {
+      return metadata->item_uri_type.c_str();
+    }
+  }
+
+  return nullptr;
+}
+
+
+struct heif_error heif_context_add_exif_metadata(struct heif_context* ctx,
+                                                 const struct heif_image_handle* image_handle,
+                                                 const void* data, int size)
+{
+  Error error = ctx->context->add_exif_metadata(image_handle->image, data, size);
+  if (error != Error::Ok) {
+    return error.error_struct(ctx->context.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+struct heif_error heif_context_add_XMP_metadata(struct heif_context* ctx,
+                                                const struct heif_image_handle* image_handle,
+                                                const void* data, int size)
+{
+  return heif_context_add_XMP_metadata2(ctx, image_handle, data, size,
+                                        heif_metadata_compression_off);
+}
+
+
+struct heif_error heif_context_add_XMP_metadata2(struct heif_context* ctx,
+                                                 const struct heif_image_handle* image_handle,
+                                                 const void* data, int size,
+                                                 heif_metadata_compression compression)
+{
+  Error error = ctx->context->add_XMP_metadata(image_handle->image, data, size, compression);
+  if (error != Error::Ok) {
+    return error.error_struct(ctx->context.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx,
+                                                    const struct heif_image_handle* image_handle,
+                                                    const void* data, int size,
+                                                    const char* item_type, const char* content_type)
+{
+  if (item_type == nullptr || strlen(item_type) != 4) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "called heif_context_add_generic_metadata() with invalid 'item_type'."};
+  }
+
+  Error error = ctx->context->add_generic_metadata(image_handle->image, data, size,
+                                                   fourcc(item_type), content_type, nullptr, heif_metadata_compression_off, nullptr);
+  if (error != Error::Ok) {
+    return error.error_struct(ctx->context.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx,
+                                                        const struct heif_image_handle* image_handle,
+                                                        const void* data, int size,
+                                                        const char* item_uri_type,
+                                                        heif_item_id* out_item_id)
+{
+  Error error = ctx->context->add_generic_metadata(image_handle->image, data, size,
+                                                   fourcc("uri "), nullptr, item_uri_type, heif_metadata_compression_off, out_item_id);
+  if (error != Error::Ok) {
+    return error.error_struct(ctx->context.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_metadata.h 1.20.1-1/libheif/api/libheif/heif_metadata.h
--- 1.19.8-1/libheif/api/libheif/heif_metadata.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_metadata.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,133 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2023 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_METADATA_H
+#define LIBHEIF_HEIF_METADATA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+
+
+enum heif_metadata_compression
+{
+  heif_metadata_compression_off = 0,
+  heif_metadata_compression_auto = 1,
+  heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file
+  heif_metadata_compression_deflate = 3,
+  heif_metadata_compression_zlib = 4,    // do not use for header data
+  heif_metadata_compression_brotli = 5
+};
+
+// ------------------------- metadata (Exif / XMP) -------------------------
+
+// How many metadata blocks are attached to an image. If you only want to get EXIF data,
+// set the type_filter to "Exif". Otherwise, set the type_filter to NULL.
+LIBHEIF_API
+int heif_image_handle_get_number_of_metadata_blocks(const heif_image_handle* handle,
+                                                    const char* type_filter);
+
+// 'type_filter' can be used to get only metadata of specific types, like "Exif".
+// If 'type_filter' is NULL, it will return all types of metadata IDs.
+LIBHEIF_API
+int heif_image_handle_get_list_of_metadata_block_IDs(const heif_image_handle* handle,
+                                                     const char* type_filter,
+                                                     heif_item_id* ids, int count);
+
+// Return a string indicating the type of the metadata, as specified in the HEIF file.
+// Exif data will have the type string "Exif".
+// This string will be valid until the next call to a libheif function.
+// You do not have to free this string.
+LIBHEIF_API
+const char* heif_image_handle_get_metadata_type(const heif_image_handle* handle,
+                                                heif_item_id metadata_id);
+
+// For EXIF, the content type is empty.
+// For XMP, the content type is "application/rdf+xml".
+LIBHEIF_API
+const char* heif_image_handle_get_metadata_content_type(const heif_image_handle* handle,
+                                                        heif_item_id metadata_id);
+
+// Get the size of the raw metadata, as stored in the HEIF file.
+LIBHEIF_API
+size_t heif_image_handle_get_metadata_size(const heif_image_handle* handle,
+                                           heif_item_id metadata_id);
+
+// 'out_data' must point to a memory area of the size reported by heif_image_handle_get_metadata_size().
+// The data is returned exactly as stored in the HEIF file.
+// For Exif data, you probably have to skip the first four bytes of the data, since they
+// indicate the offset to the start of the TIFF header of the Exif data.
+LIBHEIF_API
+heif_error heif_image_handle_get_metadata(const heif_image_handle* handle,
+                                          heif_item_id metadata_id,
+                                          void* out_data);
+
+// Only valid for item type == "uri ", an absolute URI
+LIBHEIF_API
+const char* heif_image_handle_get_metadata_item_uri_type(const heif_image_handle* handle,
+                                                         heif_item_id metadata_id);
+
+// --- writing Exif / XMP metadata ---
+
+// Add EXIF metadata to an image.
+LIBHEIF_API
+heif_error heif_context_add_exif_metadata(heif_context*,
+                                          const heif_image_handle* image_handle,
+                                          const void* data, int size);
+
+// Add XMP metadata to an image.
+LIBHEIF_API
+heif_error heif_context_add_XMP_metadata(heif_context*,
+                                         const heif_image_handle* image_handle,
+                                         const void* data, int size);
+
+// New version of heif_context_add_XMP_metadata() with data compression (experimental).
+LIBHEIF_API
+heif_error heif_context_add_XMP_metadata2(heif_context*,
+                                          const heif_image_handle* image_handle,
+                                          const void* data, int size,
+                                          enum heif_metadata_compression compression);
+
+// Add generic, proprietary metadata to an image. You have to specify an 'item_type' that will
+// identify your metadata. 'content_type' can be an additional type, or it can be NULL.
+// For example, this function can be used to add IPTC metadata (IIM stream, not XMP) to an image.
+// Although not standard, we propose to store IPTC data with item type="iptc", content_type=NULL.
+LIBHEIF_API
+heif_error heif_context_add_generic_metadata(heif_context* ctx,
+                                             const heif_image_handle* image_handle,
+                                             const void* data, int size,
+                                             const char* item_type, const char* content_type);
+
+// Add generic metadata with item_type "uri ". Items with this type do not have a content_type, but
+// an item_uri_type and they have no content_encoding (they are always stored uncompressed).
+LIBHEIF_API
+heif_error heif_context_add_generic_uri_metadata(heif_context* ctx,
+                                                 const heif_image_handle* image_handle,
+                                                 const void* data, int size,
+                                                 const char* item_uri_type,
+                                                 heif_item_id* out_item_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_plugin.h 1.20.1-1/libheif/api/libheif/heif_plugin.h
--- 1.19.8-1/libheif/api/libheif/heif_plugin.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_plugin.h	2025-07-02 13:05:31.000000000 +0000
@@ -25,7 +25,7 @@
 extern "C" {
 #endif
 
-#include <libheif/heif.h>
+#include <libheif/heif_encoding.h>
 
 
 // ====================================================================================================
@@ -42,7 +42,10 @@ extern "C" {
 //  1.8          1         2          2
 //  1.13         2         3          2
 //  1.15         3         3          2
+//  1.20         4         3          2
 
+#define heif_decoder_plugin_latest_version 4
+#define heif_encoder_plugin_latest_version 3
 
 // ====================================================================================================
 //  Decoder plugin API
@@ -50,7 +53,7 @@ extern "C" {
 //  added as plugins. A plugin has to implement the functions specified in heif_decoder_plugin
 //  and the plugin has to be registered to the libheif library using heif_register_decoder().
 
-struct heif_decoder_plugin
+typedef struct heif_decoder_plugin
 {
   // API version supported by this plugin (see table above for supported versions)
   int plugin_api_version;
@@ -59,13 +62,13 @@ struct heif_decoder_plugin
   // --- version 1 functions ---
 
   // Human-readable name of the plugin
-  const char* (* get_plugin_name)();
+  const char* (* get_plugin_name)(void);
 
   // Global plugin initialization (may be NULL)
-  void (* init_plugin)();
+  void (* init_plugin)(void);
 
   // Global plugin deinitialization (may be NULL)
-  void (* deinit_plugin)();
+  void (* deinit_plugin)(void);
 
   // Query whether the plugin supports decoding of the given format
   // Result is a priority value. The plugin with the largest value wins.
@@ -73,24 +76,24 @@ struct heif_decoder_plugin
   int (* does_support_format)(enum heif_compression_format format);
 
   // Create a new decoder context for decoding an image
-  struct heif_error (* new_decoder)(void** decoder);
+  heif_error (* new_decoder)(void** decoder);
 
   // Free the decoder context (heif_image can still be used after destruction)
   void (* free_decoder)(void* decoder);
 
   // Push more data into the decoder. This can be called multiple times.
   // This may not be called after any decode_*() function has been called.
-  struct heif_error (* push_data)(void* decoder, const void* data, size_t size);
+  heif_error (* push_data)(void* decoder, const void* data, size_t size);
 
 
   // --- After pushing the data into the decoder, the decode functions may be called only once.
 
-  struct heif_error (* decode_image)(void* decoder, struct heif_image** out_img);
+  heif_error (* decode_image)(void* decoder, heif_image** out_img);
 
 
-  // --- version 2 functions will follow below ... ---
+  // --- version 2 functions ---
 
-  void (*set_strict_decoding)(void* decoder, int flag);
+  void (* set_strict_decoding)(void* decoder, int flag);
 
   // If not NULL, this can provide a specialized function to convert YCbCr to sRGB, because
   // only the codec itself knows how to interpret the chroma samples and their locations.
@@ -104,12 +107,19 @@ struct heif_decoder_plugin
   // Reset decoder, such that we can feed in new data for another image.
   // void (*reset_image)(void* decoder);
 
-  // --- version 3 functions will follow below ... ---
+  // --- version 3 functions ---
 
   const char* id_name;
 
-  // --- version 4 functions will follow below ... ---
-};
+  // --- version 4 functions ---
+
+  heif_error (* decode_next_image)(void* decoder, heif_image** out_img,
+                                   const heif_security_limits* limits);
+
+  // --- version 5 functions will follow below ... ---
+
+  // --- Note: when adding new versions, also update `heif_decoder_plugin_latest_version`.
+} heif_decoder_plugin;
 
 
 enum heif_encoded_data_type
@@ -132,7 +142,7 @@ enum heif_image_input_class
 };
 
 
-struct heif_encoder_plugin
+typedef struct heif_encoder_plugin
 {
   // API version supported by this plugin (see table above for supported versions)
   int plugin_api_version;
@@ -157,46 +167,46 @@ struct heif_encoder_plugin
 
 
   // Human-readable name of the plugin
-  const char* (* get_plugin_name)();
+  const char* (* get_plugin_name)(void);
 
   // Global plugin initialization (may be NULL)
-  void (* init_plugin)();
+  void (* init_plugin)(void);
 
   // Global plugin cleanup (may be NULL).
   // Free data that was allocated in init_plugin()
-  void (* cleanup_plugin)();
+  void (* cleanup_plugin)(void);
 
   // Create a new decoder context for decoding an image
-  struct heif_error (* new_encoder)(void** encoder);
+  heif_error (* new_encoder)(void** encoder);
 
   // Free the decoder context (heif_image can still be used after destruction)
   void (* free_encoder)(void* encoder);
 
-  struct heif_error (* set_parameter_quality)(void* encoder, int quality);
+  heif_error (* set_parameter_quality)(void* encoder, int quality);
 
-  struct heif_error (* get_parameter_quality)(void* encoder, int* quality);
+  heif_error (* get_parameter_quality)(void* encoder, int* quality);
 
-  struct heif_error (* set_parameter_lossless)(void* encoder, int lossless);
+  heif_error (* set_parameter_lossless)(void* encoder, int lossless);
 
-  struct heif_error (* get_parameter_lossless)(void* encoder, int* lossless);
+  heif_error (* get_parameter_lossless)(void* encoder, int* lossless);
 
-  struct heif_error (* set_parameter_logging_level)(void* encoder, int logging);
+  heif_error (* set_parameter_logging_level)(void* encoder, int logging);
 
-  struct heif_error (* get_parameter_logging_level)(void* encoder, int* logging);
+  heif_error (* get_parameter_logging_level)(void* encoder, int* logging);
 
-  const struct heif_encoder_parameter** (* list_parameters)(void* encoder);
+  const heif_encoder_parameter** (* list_parameters)(void* encoder);
 
-  struct heif_error (* set_parameter_integer)(void* encoder, const char* name, int value);
+  heif_error (* set_parameter_integer)(void* encoder, const char* name, int value);
 
-  struct heif_error (* get_parameter_integer)(void* encoder, const char* name, int* value);
+  heif_error (* get_parameter_integer)(void* encoder, const char* name, int* value);
 
-  struct heif_error (* set_parameter_boolean)(void* encoder, const char* name, int value);
+  heif_error (* set_parameter_boolean)(void* encoder, const char* name, int value);
 
-  struct heif_error (* get_parameter_boolean)(void* encoder, const char* name, int* value);
+  heif_error (* get_parameter_boolean)(void* encoder, const char* name, int* value);
 
-  struct heif_error (* set_parameter_string)(void* encoder, const char* name, const char* value);
+  heif_error (* set_parameter_string)(void* encoder, const char* name, const char* value);
 
-  struct heif_error (* get_parameter_string)(void* encoder, const char* name, char* value, int value_size);
+  heif_error (* get_parameter_string)(void* encoder, const char* name, char* value, int value_size);
 
   // Replace the input colorspace/chroma with the one that is supported by the encoder and that
   // comes as close to the input colorspace/chroma as possible.
@@ -206,12 +216,12 @@ struct heif_encoder_plugin
   // Encode an image.
   // After pushing an image into the encoder, you should call get_compressed_data() to
   // get compressed data until it returns a NULL data pointer.
-  struct heif_error (* encode_image)(void* encoder, const struct heif_image* image,
-                                     enum heif_image_input_class image_class);
+  heif_error (* encode_image)(void* encoder, const heif_image* image,
+                              enum heif_image_input_class image_class);
 
   // Get a packet of decoded data. The data format depends on the codec.
   // For HEVC, each packet shall contain exactly one NAL, starting with the NAL header without startcode.
-  struct heif_error (* get_compressed_data)(void* encoder, uint8_t** data, int* size,
+  heif_error (* get_compressed_data)(void* encoder, uint8_t** data, int* size,
                                             enum heif_encoded_data_type* type);
 
 
@@ -231,7 +241,9 @@ struct heif_encoder_plugin
                               uint32_t* encoded_width, uint32_t* encoded_height);
 
   // --- version 4 functions will follow below ... ---
-};
+
+  // --- Note: when adding new versions, also update `heif_encoder_plugin_latest_version`.
+} heif_encoder_plugin;
 
 
 // Names for standard parameters. These should only be used by the encoder plugins.
@@ -241,7 +253,7 @@ struct heif_encoder_plugin
 // For use only by the encoder plugins.
 // Application programs should use the access functions.
 // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
-struct heif_encoder_parameter
+typedef struct heif_encoder_parameter
 {
   int version; // current version: 2
 
@@ -280,15 +292,15 @@ struct heif_encoder_parameter
   // --- version 2 fields
 
   int has_default;
-};
+} heif_encoder_parameter;
 
 
-extern struct heif_error heif_error_ok;
-extern struct heif_error heif_error_unsupported_parameter;
-extern struct heif_error heif_error_invalid_parameter_value;
+extern heif_error heif_error_ok;
+extern heif_error heif_error_unsupported_parameter;
+extern heif_error heif_error_invalid_parameter_value;
 
 #define HEIF_WARN_OR_FAIL(strict, image, cmd, cleanupBlock) \
-{ struct heif_error e = cmd;                  \
+{ heif_error e = cmd;                         \
   if (e.code != heif_error_Ok) {              \
     if (strict) {                             \
       cleanupBlock                            \
diff -pruN 1.19.8-1/libheif/api/libheif/heif_properties.cc 1.20.1-1/libheif/api/libheif/heif_properties.cc
--- 1.19.8-1/libheif/api/libheif/heif_properties.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_properties.cc	2025-07-02 13:05:31.000000000 +0000
@@ -207,6 +207,21 @@ struct heif_error heif_item_add_property
 }
 
 
+void heif_property_user_description_release(struct heif_property_user_description* udes)
+{
+  if (udes == nullptr) {
+    return;
+  }
+
+  delete[] udes->lang;
+  delete[] udes->name;
+  delete[] udes->description;
+  delete[] udes->tags;
+
+  delete udes;
+}
+
+
 enum heif_transform_mirror_direction heif_item_get_property_transform_mirror(const struct heif_context* context,
                                                                              heif_item_id itemId,
                                                                              heif_property_id propertyId)
@@ -287,21 +302,6 @@ void heif_item_get_property_transform_cr
 }
 
 
-void heif_property_user_description_release(struct heif_property_user_description* udes)
-{
-  if (udes == nullptr) {
-    return;
-  }
-
-  delete[] udes->lang;
-  delete[] udes->name;
-  delete[] udes->description;
-  delete[] udes->tags;
-
-  delete udes;
-}
-
-
 struct heif_error heif_item_add_raw_property(const struct heif_context* context,
                                               heif_item_id itemId,
                                               uint32_t short_type,
@@ -358,172 +358,6 @@ struct heif_error find_property(const st
 }
 
 
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-const uint64_t heif_tai_clock_info_unknown_time_uncertainty = 0xFFFFFFFFFFFFFFFF;
-const uint64_t heif_unknown_tai_timestamp = 0xFFFFFFFFFFFFFFFF;
-const int32_t heif_tai_clock_info_unknown_drift_rate = 0x7FFFFFFF;
-
-
-int heif_is_tai_clock_info_drift_rate_undefined(int32_t drift_rate)
-{
-  if (drift_rate == heif_tai_clock_info_unknown_drift_rate) {
-    return 1;
-  }
-  return 0;
-}
-
-
-struct heif_error heif_property_set_clock_info(struct heif_context* ctx,
-                                               heif_item_id itemId,
-                                               const heif_tai_clock_info* clock,
-                                               heif_property_id* out_propertyId)
-{
-  if (!ctx || !clock) {
-    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
-  }
-
-  // Check if itemId exists
-  auto file = ctx->context->get_heif_file();
-  if (!file->image_exists(itemId)) {
-    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"};
-  }
-
-  // Create new taic if one doesn't exist for the itemId.
-  auto taic = ctx->context->get_heif_file()->get_property_for_item<Box_taic>(itemId);
-  if (!taic) {
-    taic = std::make_shared<Box_taic>();
-  }
-
-  taic->set_time_uncertainty(clock->time_uncertainty);
-  taic->set_clock_resolution(clock->clock_resolution);
-  taic->set_clock_drift_rate(clock->clock_drift_rate);
-  taic->set_clock_type(clock->clock_type);
-
-  bool essential = false;
-  heif_property_id id = ctx->context->add_property(itemId, taic, essential);
-
-  if (out_propertyId) {
-    *out_propertyId = id;
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_property_get_clock_info(const struct heif_context* ctx,
-                                               heif_item_id itemId,
-                                               heif_tai_clock_info* out_clock)
-{
-  if (!ctx) {
-    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL heif_context passed in"};
-  } else if (!out_clock) {
-    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_clock_info passed in"};
-  }
-
-  // Check if itemId exists
-  auto file = ctx->context->get_heif_file();
-  if (!file->image_exists(itemId)) {
-    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"};
-  }
-
-  // Check if taic exists for itemId
-  auto taic = file->get_property_for_item<Box_taic>(itemId);
-  if (!taic) {
-    out_clock = nullptr;
-    return {heif_error_Usage_error, heif_suberror_Invalid_property, "TAI Clock property not found for itemId"};
-
-  }
-
-  if (out_clock->version >= 1) {
-    out_clock->version = 1;
-    out_clock->time_uncertainty = taic->get_time_uncertainty();
-    out_clock->clock_resolution = taic->get_clock_resolution();
-    out_clock->clock_drift_rate = taic->get_clock_drift_rate();
-    out_clock->clock_type = taic->get_clock_type();
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx,
-                                                  heif_item_id itemId,
-                                                  heif_tai_timestamp_packet* timestamp,
-                                                  heif_property_id* out_propertyId)
-{
-  if (!ctx) {
-    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
-  }
-
-  // Check if itemId exists
-  auto file = ctx->context->get_heif_file();
-  if (!file->image_exists(itemId)) {
-    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"};
-  }
-
-  // Create new itai if one doesn't exist for the itemId.
-  auto itai = file->get_property_for_item<Box_itai>(itemId);
-  if (!itai) {
-    itai = std::make_shared<Box_itai>();
-  }
-
-  // Set timestamp values
-  itai->set_tai_timestamp(timestamp->tai_timestamp);
-  itai->set_synchronization_state(timestamp->synchronization_state);
-  itai->set_timestamp_generation_failure(timestamp->timestamp_generation_failure);
-  itai->set_timestamp_is_modified(timestamp->timestamp_is_modified);
-  heif_property_id id = ctx->context->add_property(itemId, itai, false);
-  
-  // Create new taic if one doesn't exist for the itemId.
-  auto taic = file->get_property_for_item<Box_taic>(itemId);
-  if (!taic) {
-    taic = std::make_shared<Box_taic>();
-    ctx->context->add_property(itemId, taic, false);
-    // Should we output taic_id?
-  }
-    
-
-  if (out_propertyId) {
-    *out_propertyId = id;
-  }
-
-  return heif_error_success;
-}
-
-struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx,
-                                                  heif_item_id itemId,
-                                                  heif_tai_timestamp_packet* out_timestamp)
-{
-  if (!ctx) {
-    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"};
-  }
-
-  // Check if itemId exists
-  auto file = ctx->context->get_heif_file();
-  if (!file->image_exists(itemId)) {
-    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"};
-  }
-
-  //Check if itai exists for itemId
-  auto itai = file->get_property_for_item<Box_itai>(itemId);
-  if (!itai) {
-    out_timestamp = nullptr;
-    return {heif_error_Usage_error, heif_suberror_Invalid_property, "Timestamp property not found for itemId"};
-  }
-
-  if (out_timestamp) {
-    out_timestamp->version = 1;
-    out_timestamp->tai_timestamp = itai->get_tai_timestamp();
-    out_timestamp->synchronization_state = itai->get_synchronization_state();
-    out_timestamp->timestamp_generation_failure = itai->get_timestamp_generation_failure();
-    out_timestamp->timestamp_is_modified = itai->get_timestamp_is_modified();
-  }
-
-  return heif_error_success;
-}
-#endif
-
-
 struct heif_error heif_item_get_property_raw_size(const struct heif_context* context,
                                                   heif_item_id itemId,
                                                   heif_property_id propertyId,
@@ -579,6 +413,7 @@ struct heif_error heif_item_get_property
   return heif_error_success;
 }
 
+
 struct heif_error heif_item_get_property_uuid_type(const struct heif_context* context,
                                                    heif_item_id itemId,
                                                    heif_property_id propertyId,
@@ -604,3 +439,104 @@ struct heif_error heif_item_get_property
 
   return heif_error_success;
 }
+
+
+
+// ------------------------- intrinsic and extrinsic matrices -------------------------
+
+
+int heif_image_handle_has_camera_intrinsic_matrix(const struct heif_image_handle* handle)
+{
+  if (!handle) {
+    return false;
+  }
+
+  return handle->image->has_intrinsic_matrix();
+}
+
+
+struct heif_error heif_image_handle_get_camera_intrinsic_matrix(const struct heif_image_handle* handle,
+                                                                struct heif_camera_intrinsic_matrix* out_matrix)
+{
+  if (handle == nullptr || out_matrix == nullptr) {
+    return heif_error{heif_error_Usage_error,
+                      heif_suberror_Null_pointer_argument};
+  }
+
+  if (!handle->image->has_intrinsic_matrix()) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Camera_intrinsic_matrix_undefined);
+    return err.error_struct(handle->image.get());
+  }
+
+  const auto& m = handle->image->get_intrinsic_matrix();
+  out_matrix->focal_length_x = m.focal_length_x;
+  out_matrix->focal_length_y = m.focal_length_y;
+  out_matrix->principal_point_x = m.principal_point_x;
+  out_matrix->principal_point_y = m.principal_point_y;
+  out_matrix->skew = m.skew;
+
+  return heif_error_success;
+}
+
+
+int heif_image_handle_has_camera_extrinsic_matrix(const struct heif_image_handle* handle)
+{
+  if (!handle) {
+    return false;
+  }
+
+  return handle->image->has_extrinsic_matrix();
+}
+
+
+struct heif_camera_extrinsic_matrix
+{
+  Box_cmex::ExtrinsicMatrix matrix;
+};
+
+
+struct heif_error heif_image_handle_get_camera_extrinsic_matrix(const struct heif_image_handle* handle,
+                                                                struct heif_camera_extrinsic_matrix** out_matrix)
+{
+  if (handle == nullptr || out_matrix == nullptr) {
+    return heif_error{heif_error_Usage_error,
+                      heif_suberror_Null_pointer_argument};
+  }
+
+  if (!handle->image->has_extrinsic_matrix()) {
+    Error err(heif_error_Usage_error,
+              heif_suberror_Camera_extrinsic_matrix_undefined);
+    return err.error_struct(handle->image.get());
+  }
+
+  *out_matrix = new heif_camera_extrinsic_matrix;
+  (*out_matrix)->matrix = handle->image->get_extrinsic_matrix();
+
+  return heif_error_success;
+}
+
+
+void heif_camera_extrinsic_matrix_release(struct heif_camera_extrinsic_matrix* matrix)
+{
+  delete matrix;
+}
+
+
+struct heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const struct heif_camera_extrinsic_matrix* matrix,
+                                                                   double* out_matrix_row_major)
+{
+  if (matrix == nullptr || out_matrix_row_major == nullptr) {
+    return heif_error{heif_error_Usage_error,
+                      heif_suberror_Null_pointer_argument};
+  }
+
+  auto m3x3 = matrix->matrix.calculate_rotation_matrix();
+
+  for (int i=0;i<9;i++) {
+    out_matrix_row_major[i] = m3x3[i];
+  }
+
+  return heif_error_success;
+}
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_properties.h 1.20.1-1/libheif/api/libheif/heif_properties.h
--- 1.19.8-1/libheif/api/libheif/heif_properties.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_properties.h	2025-07-02 13:05:31.000000000 +0000
@@ -49,7 +49,7 @@ enum heif_item_property_type
 // The number of properties is returned, which are not more than 'count' if (out_list != nullptr).
 // By setting out_list==nullptr, you can query the number of properties, 'count' is ignored.
 LIBHEIF_API
-int heif_item_get_properties_of_type(const struct heif_context* context,
+int heif_item_get_properties_of_type(const heif_context* context,
                                      heif_item_id id,
                                      enum heif_item_property_type type,
                                      heif_property_id* out_list,
@@ -60,18 +60,18 @@ int heif_item_get_properties_of_type(con
 // The number of properties is returned, which are not more than 'count' if (out_list != nullptr).
 // By setting out_list==nullptr, you can query the number of properties, 'count' is ignored.
 LIBHEIF_API
-int heif_item_get_transformation_properties(const struct heif_context* context,
+int heif_item_get_transformation_properties(const heif_context* context,
                                             heif_item_id id,
                                             heif_property_id* out_list,
                                             int count);
 
 LIBHEIF_API
-enum heif_item_property_type heif_item_get_property_type(const struct heif_context* context,
+enum heif_item_property_type heif_item_get_property_type(const heif_context* context,
                                                          heif_item_id id,
                                                          heif_property_id property_id);
 
 // The strings are managed by libheif. They will be deleted in heif_property_user_description_release().
-struct heif_property_user_description
+typedef struct heif_property_user_description
 {
   int version;
 
@@ -81,28 +81,28 @@ struct heif_property_user_description
   const char* name;
   const char* description;
   const char* tags;
-};
+} heif_property_user_description;
 
 // Get the "udes" user description property content.
 // Undefined strings are returned as empty strings.
 LIBHEIF_API
-struct heif_error heif_item_get_property_user_description(const struct heif_context* context,
-                                                          heif_item_id itemId,
-                                                          heif_property_id propertyId,
-                                                          struct heif_property_user_description** out);
+heif_error heif_item_get_property_user_description(const heif_context* context,
+                                                   heif_item_id itemId,
+                                                   heif_property_id propertyId,
+                                                   heif_property_user_description** out);
 
 // Add a "udes" user description property to the item.
 // If any string pointers are NULL, an empty string will be used instead.
 LIBHEIF_API
-struct heif_error heif_item_add_property_user_description(const struct heif_context* context,
-                                                          heif_item_id itemId,
-                                                          const struct heif_property_user_description* description,
-                                                          heif_property_id* out_propertyId);
+heif_error heif_item_add_property_user_description(const heif_context* context,
+                                                   heif_item_id itemId,
+                                                   const heif_property_user_description* description,
+                                                   heif_property_id* out_propertyId);
 
 // Release all strings and the object itself.
 // Only call for objects that you received from heif_item_get_property_user_description().
 LIBHEIF_API
-void heif_property_user_description_release(struct heif_property_user_description*);
+void heif_property_user_description_release(heif_property_user_description*);
 
 enum heif_transform_mirror_direction
 {
@@ -113,14 +113,14 @@ enum heif_transform_mirror_direction
 
 // Will return 'heif_transform_mirror_direction_invalid' in case of error.
 LIBHEIF_API
-enum heif_transform_mirror_direction heif_item_get_property_transform_mirror(const struct heif_context* context,
+enum heif_transform_mirror_direction heif_item_get_property_transform_mirror(const heif_context* context,
                                                                              heif_item_id itemId,
                                                                              heif_property_id propertyId);
 
 // Returns only 0, 90, 180, or 270 angle values.
 // Returns -1 in case of error (but it will only return an error in case of wrong usage).
 LIBHEIF_API
-int heif_item_get_property_transform_rotation_ccw(const struct heif_context* context,
+int heif_item_get_property_transform_rotation_ccw(const heif_context* context,
                                                   heif_item_id itemId,
                                                   heif_property_id propertyId);
 
@@ -128,7 +128,7 @@ int heif_item_get_property_transform_rot
 // Because of the way this data is stored, you have to pass the image size at the moment of the crop operation
 // to compute the cropped border sizes.
 LIBHEIF_API
-void heif_item_get_property_transform_crop_borders(const struct heif_context* context,
+void heif_item_get_property_transform_crop_borders(const heif_context* context,
                                                    heif_item_id itemId,
                                                    heif_property_id propertyId,
                                                    int image_width, int image_height,
@@ -145,28 +145,28 @@ void heif_item_get_property_transform_cr
  * @param out_propertyId Outputs the id of the inserted property. Can be NULL.
 */
 LIBHEIF_API
-struct heif_error heif_item_add_raw_property(const struct heif_context* context,
-                                             heif_item_id itemId,
-                                             uint32_t fourcc_type,
-                                             const uint8_t* uuid_type,
-                                             const uint8_t* data, size_t size,
-                                             int is_essential,
-                                             heif_property_id* out_propertyId);
+heif_error heif_item_add_raw_property(const heif_context* context,
+                                      heif_item_id itemId,
+                                      uint32_t fourcc_type,
+                                      const uint8_t* uuid_type,
+                                      const uint8_t* data, size_t size,
+                                      int is_essential,
+                                      heif_property_id* out_propertyId);
 
 LIBHEIF_API
-struct heif_error heif_item_get_property_raw_size(const struct heif_context* context,
-                                                  heif_item_id itemId,
-                                                  heif_property_id propertyId,
-                                                  size_t* out_size);
+heif_error heif_item_get_property_raw_size(const heif_context* context,
+                                           heif_item_id itemId,
+                                           heif_property_id propertyId,
+                                           size_t* out_size);
 
 /**
  * @param out_data User-supplied array to write the property data to. The required size of the output array is given by heif_item_get_property_raw_size().
 */
 LIBHEIF_API
-struct heif_error heif_item_get_property_raw_data(const struct heif_context* context,
-                                                  heif_item_id itemId,
-                                                  heif_property_id propertyId,
-                                                  uint8_t* out_data);
+heif_error heif_item_get_property_raw_data(const heif_context* context,
+                                           heif_item_id itemId,
+                                           heif_property_id propertyId,
+                                           uint8_t* out_data);
 
 /**
  * Get the extended type for an extended "uuid" box.
@@ -182,10 +182,47 @@ struct heif_error heif_item_get_property
  * @return heif_error_success or an error indicating the failure
  */
 LIBHEIF_API
-struct heif_error heif_item_get_property_uuid_type(const struct heif_context* context,
-                                                   heif_item_id itemId,
-                                                   heif_property_id propertyId,
-                                                   uint8_t out_extended_type[16]);
+heif_error heif_item_get_property_uuid_type(const heif_context* context,
+                                            heif_item_id itemId,
+                                            heif_property_id propertyId,
+                                            uint8_t out_extended_type[16]);
+
+
+// ------------------------- intrinsic and extrinsic matrices -------------------------
+
+typedef struct heif_camera_intrinsic_matrix
+{
+  double focal_length_x;
+  double focal_length_y;
+  double principal_point_x;
+  double principal_point_y;
+  double skew;
+} heif_camera_intrinsic_matrix;
+
+
+LIBHEIF_API
+int heif_image_handle_has_camera_intrinsic_matrix(const heif_image_handle* handle);
+
+LIBHEIF_API
+heif_error heif_image_handle_get_camera_intrinsic_matrix(const heif_image_handle* handle,
+                                                         heif_camera_intrinsic_matrix* out_matrix);
+
+
+typedef struct heif_camera_extrinsic_matrix heif_camera_extrinsic_matrix;
+
+LIBHEIF_API
+int heif_image_handle_has_camera_extrinsic_matrix(const heif_image_handle* handle);
+
+LIBHEIF_API
+heif_error heif_image_handle_get_camera_extrinsic_matrix(const heif_image_handle* handle,
+                                                         heif_camera_extrinsic_matrix** out_matrix);
+
+LIBHEIF_API
+void heif_camera_extrinsic_matrix_release(heif_camera_extrinsic_matrix*);
+
+LIBHEIF_API
+heif_error heif_camera_extrinsic_matrix_get_rotation_matrix(const heif_camera_extrinsic_matrix*,
+                                                            double* out_matrix_row_major);
 
 #ifdef __cplusplus
 }
diff -pruN 1.19.8-1/libheif/api/libheif/heif_regions.cc 1.20.1-1/libheif/api/libheif/heif_regions.cc
--- 1.19.8-1/libheif/api/libheif/heif_regions.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_regions.cc	2025-07-02 13:05:31.000000000 +0000
@@ -124,246 +124,12 @@ int heif_region_item_get_list_of_regions
 }
 
 
-struct heif_error heif_image_handle_add_region_item(struct heif_image_handle* image_handle,
-                                                    uint32_t reference_width, uint32_t reference_height,
-                                                    struct heif_region_item** out_region_item)
-{
-  std::shared_ptr<RegionItem> regionItem = image_handle->context->add_region_item(reference_width, reference_height);
-  image_handle->image->add_region_item_id(regionItem->item_id);
-
-  if (out_region_item) {
-    heif_region_item* item = new heif_region_item();
-    item->context = image_handle->context;
-    item->region_item = std::move(regionItem);
-
-    *out_region_item = item;
-  }
-
-  return heif_error_success;
-}
-
-
-static struct heif_region* create_region(const std::shared_ptr<RegionGeometry>& r,
-                                         heif_region_item* item)
-{
-  auto region = new heif_region();
-  region->region = r;
-  region->region_item = item->region_item;
-  region->context = item->context;
-  return region;
-}
-
-
-struct heif_error heif_region_item_add_region_point(struct heif_region_item* item,
-                                                    int32_t x, int32_t y,
-                                                    struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_Point>();
-  region->x = x;
-  region->y = y;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_rectangle(struct heif_region_item* item,
-                                                        int32_t x, int32_t y,
-                                                        uint32_t width, uint32_t height,
-                                                        struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_Rectangle>();
-  region->x = x;
-  region->y = y;
-  region->width = width;
-  region->height = height;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_ellipse(struct heif_region_item* item,
-                                                      int32_t x, int32_t y,
-                                                      uint32_t radius_x, uint32_t radius_y,
-                                                      struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_Ellipse>();
-  region->x = x;
-  region->y = y;
-  region->radius_x = radius_x;
-  region->radius_y = radius_y;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_polygon(struct heif_region_item* item,
-                                                      const int32_t* pts, int nPoints,
-                                                      struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_Polygon>();
-  region->points.resize(nPoints);
-
-  for (int i=0;i<nPoints;i++) {
-    region->points[i].x = pts[2*i+0];
-    region->points[i].y = pts[2*i+1];
-  }
-
-  region->closed = true;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_polyline(struct heif_region_item* item,
-                                                       const int32_t* pts, int nPoints,
-                                                       struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_Polygon>();
-  region->points.resize(nPoints);
-
-  for (int i=0;i<nPoints;i++) {
-    region->points[i].x = pts[2*i+0];
-    region->points[i].y = pts[2*i+1];
-  }
-
-  region->closed = false;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_referenced_mask(struct heif_region_item* item,
-                                                              int32_t x, int32_t y,
-                                                              uint32_t width, uint32_t height,
-                                                              heif_item_id mask_item_id,
-                                                              struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_ReferencedMask>();
-  region->x = x;
-  region->y = y;
-  region->width = width;
-  region->height = height;
-  region->referenced_item = mask_item_id;
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  /* When the geometry 'mask' of a region is represented by a mask stored in
-   * another image item the image item containing the mask shall be identified
-   * by an item reference of type 'mask' from the region item to the image item
-   * containing the mask. */
-  std::shared_ptr<HeifContext> ctx = item->context;
-  ctx->add_region_referenced_mask_ref(item->region_item->item_id, mask_item_id);
-
-  return heif_error_success;
-}
-
-
-struct heif_error heif_region_item_add_region_inline_mask_data(struct heif_region_item* item,
-                                                               int32_t x, int32_t y,
-                                                               uint32_t width, uint32_t height,
-                                                               const uint8_t* mask_data,
-                                                               size_t mask_data_len,
-                                                               struct heif_region** out_region)
-{
-  auto region = std::make_shared<RegionGeometry_InlineMask>();
-  region->x = x;
-  region->y = y;
-  region->width = width;
-  region->height = height;
-  region->mask_data.resize(mask_data_len);
-  std::memcpy(region->mask_data.data(), mask_data, region->mask_data.size());
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-struct heif_error heif_region_item_add_region_inline_mask(struct heif_region_item* item,
-                                                          int32_t x0, int32_t y0,
-                                                          uint32_t width, uint32_t height,
-                                                          heif_image* mask_image,
-                                                          struct heif_region** out_region)
-{
-  if (! heif_image_has_channel(mask_image, heif_channel_Y))
-  {
-    return {heif_error_Usage_error, heif_suberror_Nonexisting_image_channel_referenced, "Inline mask image must have a Y channel"};
-  }
-  auto region = std::make_shared<RegionGeometry_InlineMask>();
-  region->x = x0;
-  region->y = y0;
-  region->width = width;
-  region->height = height;
-  region->mask_data.resize((width * height + 7) / 8);
-  memset(region->mask_data.data(), 0, region->mask_data.size());
-
-  uint32_t mask_height = mask_image->image->get_height();
-  uint32_t mask_width = mask_image->image->get_width();
-  int stride;
-  uint8_t* p = heif_image_get_plane(mask_image, heif_channel_Y, &stride);
-  uint64_t pixel_index = 0;
-
-  for (uint32_t y = 0; y < mask_height; y++) {
-    for (uint32_t x = 0; x < mask_width; x++) {
-      uint8_t mask_bit = p[y * stride + x] & 0x80; // use high-order bit of the 8-bit mask value as binary mask value
-      region->mask_data.data()[pixel_index/8] |= uint8_t(mask_bit >> (pixel_index % 8));
-
-      pixel_index++;
-    }
-  }
-
-  item->region_item->add_region(region);
-
-  if (out_region) {
-    *out_region = create_region(region, item);
-  }
-
-  return heif_error_success;
-}
-
-
 void heif_region_release(const struct heif_region* region)
 {
   delete region;
 }
 
+
 void heif_region_release_many(const struct heif_region* const* regions, int num)
 {
   for (int i = 0; i < num; i++) {
@@ -517,12 +283,6 @@ int heif_region_get_polygon_num_points(c
 }
 
 
-int heif_region_get_polyline_num_points(const struct heif_region* region)
-{
-  return heif_region_get_poly_num_points(region);
-}
-
-
 static struct heif_error heif_region_get_poly_points(const struct heif_region* region, int32_t* pts)
 {
   if (pts == nullptr) {
@@ -547,12 +307,6 @@ struct heif_error heif_region_get_polygo
 }
 
 
-struct heif_error heif_region_get_polyline_points(const struct heif_region* region, int32_t* pts)
-{
-  return heif_region_get_poly_points(region, pts);
-}
-
-
 static struct heif_error heif_region_get_poly_points_scaled(const struct heif_region* region, double* pts, heif_item_id image_id)
 {
   if (pts == nullptr) {
@@ -583,11 +337,25 @@ struct heif_error heif_region_get_polygo
   return heif_region_get_poly_points_scaled(region, pts, image_id);
 }
 
+
+int heif_region_get_polyline_num_points(const struct heif_region* region)
+{
+  return heif_region_get_poly_num_points(region);
+}
+
+
+struct heif_error heif_region_get_polyline_points(const struct heif_region* region, int32_t* pts)
+{
+  return heif_region_get_poly_points(region, pts);
+}
+
+
 struct heif_error heif_region_get_polyline_points_transformed(const struct heif_region* region, heif_item_id image_id, double* pts)
 {
   return heif_region_get_poly_points_scaled(region, pts, image_id);
 }
 
+
 struct heif_error heif_region_get_referenced_mask_ID(const struct heif_region* region,
                                                      int32_t* x, int32_t* y,
                                                      uint32_t* width, uint32_t* height,
@@ -611,6 +379,7 @@ struct heif_error heif_region_get_refere
   return heif_error_invalid_parameter_value;
 }
 
+
 size_t heif_region_get_inline_mask_data_len(const struct heif_region* region)
 {
   const std::shared_ptr<RegionGeometry_InlineMask> mask = std::dynamic_pointer_cast<RegionGeometry_InlineMask>(region->region);
@@ -620,29 +389,43 @@ size_t heif_region_get_inline_mask_data_
   return 0;
 }
 
-struct heif_error heif_region_get_inline_mask_data(const struct heif_region* region,
-                                                   int32_t* x, int32_t* y,
-                                                   uint32_t* width, uint32_t* height,
-                                                   uint8_t* data)
+
+static struct heif_region* create_region(const std::shared_ptr<RegionGeometry>& r,
+                                         heif_region_item* item)
 {
-  if ((x == nullptr) || (y == nullptr) || (width == nullptr) || (height == nullptr))
-  {
-    return heif_error_invalid_parameter_value;
-  }
+  auto region = new heif_region();
+  region->region = r;
+  region->region_item = item->region_item;
+  region->context = item->context;
+  return region;
+}
 
-  const std::shared_ptr<RegionGeometry_InlineMask> mask = std::dynamic_pointer_cast<RegionGeometry_InlineMask>(region->region);
-  if (mask)
-  {
-    *x = mask->x;
-    *y = mask->y;
-    *width = mask->width;
-    *height = mask->height;
-    memcpy(data, mask->mask_data.data(), mask->mask_data.size());
-    return heif_error_success;
+
+struct heif_error heif_region_item_add_region_inline_mask_data(struct heif_region_item* item,
+                                                               int32_t x, int32_t y,
+                                                               uint32_t width, uint32_t height,
+                                                               const uint8_t* mask_data,
+                                                               size_t mask_data_len,
+                                                               struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_InlineMask>();
+  region->x = x;
+  region->y = y;
+  region->width = width;
+  region->height = height;
+  region->mask_data.resize(mask_data_len);
+  std::memcpy(region->mask_data.data(), mask_data, region->mask_data.size());
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
   }
-  return heif_error_invalid_parameter_value;
+
+  return heif_error_success;
 }
 
+
 static struct heif_error heif_region_get_inline_mask_image(const struct heif_region* region,
                                                            int32_t* out_x0, int32_t* out_y0,
                                                            uint32_t* out_width, uint32_t* out_height,
@@ -673,8 +456,8 @@ static struct heif_error heif_region_get
       heif_image_release(*out_mask_image);
       return err;
     }
-    int stride;
-    uint8_t* p = heif_image_get_plane(*out_mask_image, heif_channel_Y, &stride);
+    size_t stride;
+    uint8_t* p = heif_image_get_plane2(*out_mask_image, heif_channel_Y, &stride);
     uint64_t pixel_index = 0;
 
     for (uint32_t y = 0; y < height; y++)
@@ -729,3 +512,228 @@ struct heif_error heif_region_get_mask_i
 
   return heif_error_invalid_parameter_value;
 }
+
+
+struct heif_error heif_image_handle_add_region_item(struct heif_image_handle* image_handle,
+                                                    uint32_t reference_width, uint32_t reference_height,
+                                                    struct heif_region_item** out_region_item)
+{
+  std::shared_ptr<RegionItem> regionItem = image_handle->context->add_region_item(reference_width, reference_height);
+  image_handle->image->add_region_item_id(regionItem->item_id);
+
+  if (out_region_item) {
+    heif_region_item* item = new heif_region_item();
+    item->context = image_handle->context;
+    item->region_item = std::move(regionItem);
+
+    *out_region_item = item;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_point(struct heif_region_item* item,
+                                                    int32_t x, int32_t y,
+                                                    struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_Point>();
+  region->x = x;
+  region->y = y;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_rectangle(struct heif_region_item* item,
+                                                        int32_t x, int32_t y,
+                                                        uint32_t width, uint32_t height,
+                                                        struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_Rectangle>();
+  region->x = x;
+  region->y = y;
+  region->width = width;
+  region->height = height;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_ellipse(struct heif_region_item* item,
+                                                      int32_t x, int32_t y,
+                                                      uint32_t radius_x, uint32_t radius_y,
+                                                      struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_Ellipse>();
+  region->x = x;
+  region->y = y;
+  region->radius_x = radius_x;
+  region->radius_y = radius_y;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_polygon(struct heif_region_item* item,
+                                                      const int32_t* pts, int nPoints,
+                                                      struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_Polygon>();
+  region->points.resize(nPoints);
+
+  for (int i=0;i<nPoints;i++) {
+    region->points[i].x = pts[2*i+0];
+    region->points[i].y = pts[2*i+1];
+  }
+
+  region->closed = true;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_polyline(struct heif_region_item* item,
+                                                       const int32_t* pts, int nPoints,
+                                                       struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_Polygon>();
+  region->points.resize(nPoints);
+
+  for (int i=0;i<nPoints;i++) {
+    region->points[i].x = pts[2*i+0];
+    region->points[i].y = pts[2*i+1];
+  }
+
+  region->closed = false;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_item_add_region_referenced_mask(struct heif_region_item* item,
+                                                              int32_t x, int32_t y,
+                                                              uint32_t width, uint32_t height,
+                                                              heif_item_id mask_item_id,
+                                                              struct heif_region** out_region)
+{
+  auto region = std::make_shared<RegionGeometry_ReferencedMask>();
+  region->x = x;
+  region->y = y;
+  region->width = width;
+  region->height = height;
+  region->referenced_item = mask_item_id;
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  /* When the geometry 'mask' of a region is represented by a mask stored in
+   * another image item the image item containing the mask shall be identified
+   * by an item reference of type 'mask' from the region item to the image item
+   * containing the mask. */
+  std::shared_ptr<HeifContext> ctx = item->context;
+  ctx->add_region_referenced_mask_ref(item->region_item->item_id, mask_item_id);
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_region_get_inline_mask_data(const struct heif_region* region,
+                                                   int32_t* x, int32_t* y,
+                                                   uint32_t* width, uint32_t* height,
+                                                   uint8_t* data)
+{
+  if ((x == nullptr) || (y == nullptr) || (width == nullptr) || (height == nullptr))
+  {
+    return heif_error_invalid_parameter_value;
+  }
+
+  const std::shared_ptr<RegionGeometry_InlineMask> mask = std::dynamic_pointer_cast<RegionGeometry_InlineMask>(region->region);
+  if (mask)
+  {
+    *x = mask->x;
+    *y = mask->y;
+    *width = mask->width;
+    *height = mask->height;
+    memcpy(data, mask->mask_data.data(), mask->mask_data.size());
+    return heif_error_success;
+  }
+  return heif_error_invalid_parameter_value;
+}
+
+
+struct heif_error heif_region_item_add_region_inline_mask(struct heif_region_item* item,
+                                                          int32_t x0, int32_t y0,
+                                                          uint32_t width, uint32_t height,
+                                                          heif_image* mask_image,
+                                                          struct heif_region** out_region)
+{
+  if (! heif_image_has_channel(mask_image, heif_channel_Y))
+  {
+    return {heif_error_Usage_error, heif_suberror_Nonexisting_image_channel_referenced, "Inline mask image must have a Y channel"};
+  }
+  auto region = std::make_shared<RegionGeometry_InlineMask>();
+  region->x = x0;
+  region->y = y0;
+  region->width = width;
+  region->height = height;
+  region->mask_data.resize((width * height + 7) / 8);
+  memset(region->mask_data.data(), 0, region->mask_data.size());
+
+  uint32_t mask_height = mask_image->image->get_height();
+  uint32_t mask_width = mask_image->image->get_width();
+  size_t stride;
+  uint8_t* p = heif_image_get_plane2(mask_image, heif_channel_Y, &stride);
+  uint64_t pixel_index = 0;
+
+  for (uint32_t y = 0; y < mask_height; y++) {
+    for (uint32_t x = 0; x < mask_width; x++) {
+      uint8_t mask_bit = p[y * stride + x] & 0x80; // use high-order bit of the 8-bit mask value as binary mask value
+      region->mask_data.data()[pixel_index/8] |= uint8_t(mask_bit >> (pixel_index % 8));
+
+      pixel_index++;
+    }
+  }
+
+  item->region_item->add_region(region);
+
+  if (out_region) {
+    *out_region = create_region(region, item);
+  }
+
+  return heif_error_success;
+}
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_regions.h 1.20.1-1/libheif/api/libheif/heif_regions.h
--- 1.19.8-1/libheif/api/libheif/heif_regions.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_regions.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,7 +32,7 @@ extern "C" {
 
 // See ISO/IEC 23008-12:2022 Section 6.10 "Region items and region annotations"
 
-struct heif_region_item;
+typedef struct heif_region_item heif_region_item;
 
 /**
  * Region type.
@@ -121,7 +121,7 @@ enum heif_region_type
   heif_region_type_polyline = 6
 };
 
-struct heif_region;
+typedef struct heif_region heif_region;
 
 /**
  * Get the number of region items that are attached to an image.
@@ -130,7 +130,7 @@ struct heif_region;
  * @return the number of region items, which can be zero.
  */
 LIBHEIF_API
-int heif_image_handle_get_number_of_region_items(const struct heif_image_handle* image_handle);
+int heif_image_handle_get_number_of_region_items(const heif_image_handle* image_handle);
 
 /**
  * Get the region item identifiers for the region items attached to an image.
@@ -151,7 +151,7 @@ int heif_image_handle_get_number_of_regi
  * @return the number of region item identifiers that were returned.
  */
 LIBHEIF_API
-int heif_image_handle_get_list_of_region_item_ids(const struct heif_image_handle* image_handle,
+int heif_image_handle_get_list_of_region_item_ids(const heif_image_handle* image_handle,
                                                   heif_item_id* region_item_ids_array,
                                                   int max_count);
 
@@ -166,9 +166,9 @@ int heif_image_handle_get_list_of_region
  * @return heif_error_ok on success, or an error value indicating the problem
  */
 LIBHEIF_API
-struct heif_error heif_context_get_region_item(const struct heif_context* context,
-                                               heif_item_id region_item_id,
-                                               struct heif_region_item** out);
+heif_error heif_context_get_region_item(const heif_context* context,
+                                        heif_item_id region_item_id,
+                                        heif_region_item** out);
 
 /**
  * Get the item identifier for a region item.
@@ -177,7 +177,7 @@ struct heif_error heif_context_get_regio
  * @return the region item identifier (or -1 if the region_item is null)
  */
 LIBHEIF_API
-heif_item_id heif_region_item_get_id(struct heif_region_item* region_item);
+heif_item_id heif_region_item_get_id(heif_region_item* region_item);
 
 /**
  * Release a region item.
@@ -187,7 +187,7 @@ heif_item_id heif_region_item_get_id(str
  * @param region_item the item to release.
  */
 LIBHEIF_API
-void heif_region_item_release(struct heif_region_item* region_item);
+void heif_region_item_release(heif_region_item* region_item);
 
 /**
  * Get the reference size for a region item.
@@ -200,7 +200,7 @@ void heif_region_item_release(struct hei
  * @param out_height the return value for the reference height (before any transformation)
  */
 LIBHEIF_API
-void heif_region_item_get_reference_size(struct heif_region_item*, uint32_t* out_width, uint32_t* out_height);
+void heif_region_item_get_reference_size(heif_region_item*, uint32_t* out_width, uint32_t* out_height);
 
 /**
  * Get the number of regions within a region item.
@@ -209,7 +209,7 @@ void heif_region_item_get_reference_size
  * @return the number of regions
 */
 LIBHEIF_API
-int heif_region_item_get_number_of_regions(const struct heif_region_item* region_item);
+int heif_region_item_get_number_of_regions(const heif_region_item* region_item);
 
 /**
  * Get the regions that are part of a region item.
@@ -234,8 +234,8 @@ int heif_region_item_get_number_of_regio
  * @return the number of regions that were returned.
  */
 LIBHEIF_API
-int heif_region_item_get_list_of_regions(const struct heif_region_item* region_item,
-                                         struct heif_region** out_regions_array,
+int heif_region_item_get_list_of_regions(const heif_region_item* region_item,
+                                         heif_region** out_regions_array,
                                          int max_count);
 
 /**
@@ -248,7 +248,7 @@ int heif_region_item_get_list_of_regions
  * \sa heif_region_release_many() to release the whole list
  */
 LIBHEIF_API
-void heif_region_release(const struct heif_region* region);
+void heif_region_release(const heif_region* region);
 
 /**
  * Release a list of regions.
@@ -261,7 +261,7 @@ void heif_region_release(const struct he
  * \sa heif_region_release() to release a single region
  */
 LIBHEIF_API
-void heif_region_release_many(const struct heif_region* const* regions_array, int num_items);
+void heif_region_release_many(const heif_region* const* regions_array, int num_items);
 
 /**
  * Get the region type for a specified region. 
@@ -270,7 +270,7 @@ void heif_region_release_many(const stru
  * @return the corresponding region type as an enumeration value
  */
 LIBHEIF_API
-enum heif_region_type heif_region_get_type(const struct heif_region* region);
+enum heif_region_type heif_region_get_type(const heif_region* region);
 
 // When querying the region geometry, there is a version without and a version with "_transformed" suffix.
 // The version without returns the coordinates in the reference coordinate space.
@@ -289,7 +289,7 @@ enum heif_region_type heif_region_get_ty
  * \sa heif_region_get_point_transformed() for a version in pixels after all transformative properties have been applied.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_point(const struct heif_region* region, int32_t* out_x, int32_t* out_y);
+heif_error heif_region_get_point(const heif_region* region, int32_t* out_x, int32_t* out_y);
 
 /**
  * Get the transformed values for a point region.
@@ -305,7 +305,7 @@ struct heif_error heif_region_get_point(
  * \sa heif_region_get_point() for a version that returns the values in the reference coordinate space.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_point_transformed(const struct heif_region* region, heif_item_id image_id, double* out_x, double* out_y);
+heif_error heif_region_get_point_transformed(const heif_region* region, heif_item_id image_id, double* out_x, double* out_y);
 
 /**
  * Get the values for a rectangle region.
@@ -325,9 +325,9 @@ struct heif_error heif_region_get_point_
  * \sa heif_region_get_rectangle_transformed() for a version in pixels after all transformative properties have been applied.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_rectangle(const struct heif_region* region,
-                                            int32_t* out_x, int32_t* out_y,
-                                            uint32_t* out_width, uint32_t* out_height);
+heif_error heif_region_get_rectangle(const heif_region* region,
+                                     int32_t* out_x, int32_t* out_y,
+                                     uint32_t* out_width, uint32_t* out_height);
 
 /**
  * Get the transformed values for a rectangle region.
@@ -348,10 +348,10 @@ struct heif_error heif_region_get_rectan
  * \sa heif_region_get_rectangle() for a version that returns the values in the reference coordinate space.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_rectangle_transformed(const struct heif_region* region,
-                                                        heif_item_id image_id,
-                                                        double* out_x, double* out_y,
-                                                        double* out_width, double* out_height);
+heif_error heif_region_get_rectangle_transformed(const heif_region* region,
+                                                 heif_item_id image_id,
+                                                 double* out_x, double* out_y,
+                                                 double* out_width, double* out_height);
 
 /**
  * Get the values for an ellipse region.
@@ -371,9 +371,9 @@ struct heif_error heif_region_get_rectan
  * \sa heif_region_get_ellipse_transformed() for a version in pixels after all transformative properties have been applied.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_ellipse(const struct heif_region* region,
-                                          int32_t* out_x, int32_t* out_y,
-                                          uint32_t* out_radius_x, uint32_t* out_radius_y);
+heif_error heif_region_get_ellipse(const heif_region* region,
+                                   int32_t* out_x, int32_t* out_y,
+                                   uint32_t* out_radius_x, uint32_t* out_radius_y);
 
 
 /**
@@ -395,10 +395,10 @@ struct heif_error heif_region_get_ellips
  * \sa heif_region_get_ellipse() for a version that returns the values in the reference coordinate space.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_ellipse_transformed(const struct heif_region* region,
-                                                      heif_item_id image_id,
-                                                      double* out_x, double* out_y,
-                                                      double* out_radius_x, double* out_radius_y);
+heif_error heif_region_get_ellipse_transformed(const heif_region* region,
+                                               heif_item_id image_id,
+                                               double* out_x, double* out_y,
+                                               double* out_radius_x, double* out_radius_y);
 
 /**
  * Get the number of points in a polygon.
@@ -407,7 +407,7 @@ struct heif_error heif_region_get_ellips
  * @return the number of points, or -1 on error.
  */
 LIBHEIF_API
-int heif_region_get_polygon_num_points(const struct heif_region* region);
+int heif_region_get_polygon_num_points(const heif_region* region);
 
 /**
  * Get the points in a polygon region.
@@ -428,8 +428,8 @@ int heif_region_get_polygon_num_points(c
  * \sa heif_region_get_polygon_points_transformed() for a version in pixels after all transformative properties have been applied.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_polygon_points(const struct heif_region* region,
-                                                 int32_t* out_pts_array);
+heif_error heif_region_get_polygon_points(const heif_region* region,
+                                          int32_t* out_pts_array);
 
 /**
  * Get the transformed points in a polygon region.
@@ -451,9 +451,9 @@ struct heif_error heif_region_get_polygo
  * \sa heif_region_get_polygon_points() for a version that returns the values in the reference coordinate space.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_polygon_points_transformed(const struct heif_region* region,
-                                                             heif_item_id image_id,
-                                                             double* out_pts_array);
+heif_error heif_region_get_polygon_points_transformed(const heif_region* region,
+                                                      heif_item_id image_id,
+                                                      double* out_pts_array);
 /**
  * Get the number of points in a polyline.
  *
@@ -461,7 +461,7 @@ struct heif_error heif_region_get_polygo
  * @return the number of points, or -1 on error.
  */
 LIBHEIF_API
-int heif_region_get_polyline_num_points(const struct heif_region* region);
+int heif_region_get_polyline_num_points(const heif_region* region);
 
 /**
  * Get the points in a polyline region.
@@ -492,8 +492,8 @@ int heif_region_get_polyline_num_points(
  * \sa heif_region_get_polyline_points_transformed() for a version in pixels after all transformative properties have been applied.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_polyline_points(const struct heif_region* region,
-                                                  int32_t* out_pts_array);
+heif_error heif_region_get_polyline_points(const heif_region* region,
+                                           int32_t* out_pts_array);
 
 /**
  * Get the transformed points in a polyline region.
@@ -515,9 +515,9 @@ struct heif_error heif_region_get_polyli
  * \sa heif_region_get_polyline_points() for a version that returns the values in the reference coordinate space.
  */
 LIBHEIF_API
-struct heif_error heif_region_get_polyline_points_transformed(const struct heif_region* region,
-                                                              heif_item_id image_id,
-                                                              double* out_pts_array);
+heif_error heif_region_get_polyline_points_transformed(const heif_region* region,
+                                                       heif_item_id image_id,
+                                                       double* out_pts_array);
 
 /**
  * Get a referenced item mask region.
@@ -554,10 +554,10 @@ struct heif_error heif_region_get_polyli
  * @return heif_error_ok on success, or an error value indicating the problem on failure
  */
 LIBHEIF_API
-struct heif_error heif_region_get_referenced_mask_ID(const struct heif_region* region,
-                                                     int32_t* out_x, int32_t* out_y,
-                                                     uint32_t* out_width, uint32_t* out_height,
-                                                     heif_item_id *out_mask_item_id);
+heif_error heif_region_get_referenced_mask_ID(const heif_region* region,
+                                              int32_t* out_x, int32_t* out_y,
+                                              uint32_t* out_width, uint32_t* out_height,
+                                              heif_item_id* out_mask_item_id);
 
 /**
  * Get the length of the data in an inline mask region.
@@ -566,7 +566,7 @@ struct heif_error heif_region_get_refere
  * @return the number of bytes in the mask data, or 0 on error.
  */
 LIBHEIF_API
-size_t heif_region_get_inline_mask_data_len(const struct heif_region* region);
+size_t heif_region_get_inline_mask_data_len(const heif_region* region);
 
 
 /**
@@ -599,10 +599,10 @@ size_t heif_region_get_inline_mask_data_
  * @return heif_error_ok on success, or an error value indicating the problem on failure
  */
 LIBHEIF_API
-struct heif_error heif_region_get_inline_mask_data(const struct heif_region* region,
-                                                   int32_t* out_x, int32_t* out_y,
-                                                   uint32_t* out_width, uint32_t* out_height,
-                                                   uint8_t* out_mask_data);
+heif_error heif_region_get_inline_mask_data(const heif_region* region,
+                                            int32_t* out_x, int32_t* out_y,
+                                            uint32_t* out_width, uint32_t* out_height,
+                                            uint8_t* out_mask_data);
 
 /**
  * Get a mask region image.
@@ -632,10 +632,10 @@ struct heif_error heif_region_get_inline
  * \note the caller is responsible for releasing the mask image
  */
 LIBHEIF_API
-struct heif_error heif_region_get_mask_image(const struct heif_region* region,
-                                             int32_t* out_x, int32_t* out_y,
-                                             uint32_t* out_width, uint32_t* out_height,
-                                             struct heif_image** out_mask_image);
+heif_error heif_region_get_mask_image(const heif_region* region,
+                                      int32_t* out_x, int32_t* out_y,
+                                      uint32_t* out_width, uint32_t* out_height,
+                                      heif_image** out_mask_image);
 
 // --- adding region items
 
@@ -654,9 +654,9 @@ struct heif_error heif_region_get_mask_i
  * @return heif_error_ok on success, or an error indicating the problem on failure
 */
 LIBHEIF_API
-struct heif_error heif_image_handle_add_region_item(struct heif_image_handle* image_handle,
-                                                    uint32_t reference_width, uint32_t reference_height,
-                                                    struct heif_region_item** out_region_item);
+heif_error heif_image_handle_add_region_item(heif_image_handle* image_handle,
+                                             uint32_t reference_width, uint32_t reference_height,
+                                             heif_region_item** out_region_item);
 
 /**
  * Add a point region to the region item.
@@ -670,9 +670,9 @@ struct heif_error heif_image_handle_add_
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_point(struct heif_region_item* region_item,
-                                                    int32_t x, int32_t y,
-                                                    struct heif_region** out_region);
+heif_error heif_region_item_add_region_point(heif_region_item* region_item,
+                                             int32_t x, int32_t y,
+                                             heif_region** out_region);
 
 /**
  * Add a rectangle region to the region item.
@@ -688,10 +688,10 @@ struct heif_error heif_region_item_add_r
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_rectangle(struct heif_region_item* region_item,
-                                                        int32_t x, int32_t y,
-                                                        uint32_t width, uint32_t height,
-                                                        struct heif_region** out_region);
+heif_error heif_region_item_add_region_rectangle(heif_region_item* region_item,
+                                                 int32_t x, int32_t y,
+                                                 uint32_t width, uint32_t height,
+                                                 heif_region** out_region);
 
 /**
  * Add a ellipse region to the region item.
@@ -707,10 +707,10 @@ struct heif_error heif_region_item_add_r
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_ellipse(struct heif_region_item* region_item,
-                                                      int32_t x, int32_t y,
-                                                      uint32_t radius_x, uint32_t radius_y,
-                                                      struct heif_region** out_region);
+heif_error heif_region_item_add_region_ellipse(heif_region_item* region_item,
+                                               int32_t x, int32_t y,
+                                               uint32_t radius_x, uint32_t radius_y,
+                                               heif_region** out_region);
 
 /**
  * Add a polygon region to the region item.
@@ -730,9 +730,9 @@ struct heif_error heif_region_item_add_r
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_polygon(struct heif_region_item* region_item,
-                                                      const int32_t* pts_array, int nPoints,
-                                                      struct heif_region** out_region);
+heif_error heif_region_item_add_region_polygon(heif_region_item* region_item,
+                                               const int32_t* pts_array, int nPoints,
+                                               heif_region** out_region);
 
 /**
  * Add a polyline region to the region item.
@@ -753,9 +753,9 @@ struct heif_error heif_region_item_add_r
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_polyline(struct heif_region_item* region_item,
-                                                       const int32_t* pts_array, int nPoints,
-                                                       struct heif_region** out_region);
+heif_error heif_region_item_add_region_polyline(heif_region_item* region_item,
+                                                const int32_t* pts_array, int nPoints,
+                                                heif_region** out_region);
 
 
 /**
@@ -794,11 +794,11 @@ struct heif_error heif_region_item_add_r
  * @note The `out_region` parameter is optional, and can be set to `NULL` if not needed.
  */
 LIBHEIF_API
-struct heif_error heif_region_item_add_region_referenced_mask(struct heif_region_item* region_item,
-                                                              int32_t x, int32_t y,
-                                                              uint32_t width, uint32_t height,
-                                                              heif_item_id mask_item_id,
-                                                              struct heif_region** out_region);
+heif_error heif_region_item_add_region_referenced_mask(heif_region_item* region_item,
+                                                       int32_t x, int32_t y,
+                                                       uint32_t width, uint32_t height,
+                                                       heif_item_id mask_item_id,
+                                                       heif_region** out_region);
 
 
 /**
@@ -822,13 +822,13 @@ struct heif_error heif_region_item_add_r
  * @param out_region pointer to pointer to the returned region (optional, see below)
  * @return heif_error_ok on success, or an error value indicating the problem on failure
  */
- LIBHEIF_API
-struct heif_error heif_region_item_add_region_inline_mask_data(struct heif_region_item* region_item,
-                                                               int32_t x, int32_t y,
-                                                               uint32_t width, uint32_t height,
-                                                               const uint8_t* mask_data,
-                                                               size_t mask_data_len,
-                                                               struct heif_region** out_region);
+LIBHEIF_API
+heif_error heif_region_item_add_region_inline_mask_data(heif_region_item* region_item,
+                                                        int32_t x, int32_t y,
+                                                        uint32_t width, uint32_t height,
+                                                        const uint8_t* mask_data,
+                                                        size_t mask_data_len,
+                                                        heif_region** out_region);
 
 /**
  * Add an inline mask region image to the region item.
@@ -853,12 +853,12 @@ struct heif_error heif_region_item_add_r
  * @param out_region pointer to pointer to the returned region (optional, see below)
  * @return heif_error_ok on success, or an error value indicating the problem on failure
  */
- LIBHEIF_API
-struct heif_error heif_region_item_add_region_inline_mask(struct heif_region_item* region_item,
-                                                          int32_t x, int32_t y,
-                                                          uint32_t width, uint32_t height,
-                                                          struct heif_image* image,
-                                                          struct heif_region** out_region);
+LIBHEIF_API
+heif_error heif_region_item_add_region_inline_mask(heif_region_item* region_item,
+                                                   int32_t x, int32_t y,
+                                                   uint32_t width, uint32_t height,
+                                                   heif_image* image,
+                                                   heif_region** out_region);
 #ifdef __cplusplus
 }
 #endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_security.cc 1.20.1-1/libheif/api/libheif/heif_security.cc
--- 1.19.8-1/libheif/api/libheif/heif_security.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_security.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,68 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_security.h"
+#include "api_structs.h"
+
+#include "context.h"
+
+
+
+const struct heif_security_limits* heif_get_global_security_limits()
+{
+  return &global_security_limits;
+}
+
+
+const struct heif_security_limits* heif_get_disabled_security_limits()
+{
+  return &disabled_security_limits;
+}
+
+
+struct heif_security_limits* heif_context_get_security_limits(const struct heif_context* ctx)
+{
+  if (!ctx) {
+    return nullptr;
+  }
+
+  return ctx->context->get_security_limits();
+}
+
+
+struct heif_error heif_context_set_security_limits(struct heif_context* ctx, const struct heif_security_limits* limits)
+{
+  if (ctx==nullptr || limits==nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument};
+  }
+
+  ctx->context->set_security_limits(limits);
+
+  return heif_error_ok;
+}
+
+
+// DEPRECATED
+
+void heif_context_set_maximum_image_size_limit(struct heif_context* ctx, int maximum_width)
+{
+  ctx->context->get_security_limits()->max_image_size_pixels = static_cast<uint64_t>(maximum_width) * maximum_width;
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_security.h 1.20.1-1/libheif/api/libheif/heif_security.h
--- 1.19.8-1/libheif/api/libheif/heif_security.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_security.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,102 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_SECURITY_H
+#define LIBHEIF_HEIF_SECURITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <libheif/heif_library.h>
+
+
+// --- security limits
+
+// If you set a limit to 0, the limit is disabled.
+typedef struct heif_security_limits
+{
+  uint8_t version;
+
+  // --- version 1
+
+  // Limit on the maximum image size to avoid allocating too much memory.
+  // For example, setting this to 32768^2 pixels = 1 Gigapixels results
+  // in 1.5 GB memory need for YUV-4:2:0 or 4 GB for RGB32.
+  uint64_t max_image_size_pixels;
+  uint64_t max_number_of_tiles;
+  uint32_t max_bayer_pattern_pixels;
+  uint32_t max_items;
+
+  uint32_t max_color_profile_size;
+  uint64_t max_memory_block_size;
+
+  uint32_t max_components;
+
+  uint32_t max_iloc_extents_per_item;
+  uint32_t max_size_entity_group;
+
+  uint32_t max_children_per_box; // for all boxes that are not covered by other limits
+
+  // --- version 2
+
+  uint64_t max_total_memory;
+  uint32_t max_sample_description_box_entries;
+  uint32_t max_sample_group_description_box_entries;
+} heif_security_limits;
+
+
+// The global security limits are the default for new heif_contexts.
+// These global limits cannot be changed, but you can override the limits for a specific heif_context.
+LIBHEIF_API
+const heif_security_limits* heif_get_global_security_limits(void);
+
+// Returns a set of fully disabled security limits. Use with care and only after user confirmation.
+LIBHEIF_API
+const heif_security_limits* heif_get_disabled_security_limits(void);
+
+// Returns the security limits for a heif_context.
+// By default, the limits are set to the global limits, but you can change them in the returned object.
+LIBHEIF_API
+heif_security_limits* heif_context_get_security_limits(const heif_context*);
+
+// Overwrites the security limits of a heif_context.
+// This is a convenience function to easily copy limits.
+LIBHEIF_API
+heif_error heif_context_set_security_limits(heif_context*, const heif_security_limits*);
+
+
+// --- DEPRECATED ---
+
+// Set the maximum image size security limit. This function will set the maximum image area (number of pixels)
+// to maximum_width ^ 2. Alternatively to using this function, you can also set the maximum image area
+// in the security limits structure returned by heif_context_get_security_limits().
+LIBHEIF_API
+void heif_context_set_maximum_image_size_limit(heif_context* ctx, int maximum_width);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_sequences.cc 1.20.1-1/libheif/api/libheif/heif_sequences.cc
--- 1.19.8-1/libheif/api/libheif/heif_sequences.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_sequences.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,766 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_sequences.h"
+#include "context.h"
+#include "api_structs.h"
+#include "file.h"
+#include "sequences/track.h"
+#include "sequences/track_visual.h"
+#include "sequences/track_metadata.h"
+
+#include <array>
+#include <cstring>
+#include <memory>
+#include <vector>
+#include <string>
+#include <utility>
+
+
+int heif_context_has_sequence(const heif_context* ctx)
+{
+  return ctx->context->has_sequence();
+}
+
+
+uint32_t heif_context_get_sequence_timescale(const heif_context* ctx)
+{
+  return ctx->context->get_sequence_timescale();
+}
+
+
+uint64_t heif_context_get_sequence_duration(const heif_context* ctx)
+{
+  return ctx->context->get_sequence_duration();
+}
+
+
+void heif_track_release(heif_track* track)
+{
+  delete track;
+}
+
+
+int heif_context_number_of_sequence_tracks(const struct heif_context* ctx)
+{
+  return ctx->context->get_number_of_tracks();
+}
+
+
+void heif_context_get_track_ids(const struct heif_context* ctx, uint32_t out_track_id_array[])
+{
+  std::vector<uint32_t> IDs;
+  IDs = ctx->context->get_track_IDs();
+
+  for (uint32_t id : IDs) {
+    *out_track_id_array++ = id;
+  }
+}
+
+
+uint32_t heif_track_get_id(const struct heif_track* track)
+{
+  return track->track->get_id();
+}
+
+
+// Use id=0 for the first visual track.
+struct heif_track* heif_context_get_track(const struct heif_context* ctx, uint32_t track_id)
+{
+  auto trackResult = ctx->context->get_track(track_id);
+  if (trackResult.error) {
+    return nullptr;
+  }
+
+  auto* track = new heif_track;
+  track->track = trackResult.value;
+  track->context = ctx->context;
+
+  return track;
+}
+
+
+uint32_t heif_track_get_track_handler_type(const struct heif_track* track)
+{
+  return track->track->get_handler();
+}
+
+
+uint32_t heif_track_get_timescale(const struct heif_track* track)
+{
+  return track->track->get_timescale();
+}
+
+
+struct heif_error heif_track_get_image_resolution(const struct heif_track* track_ptr, uint16_t* out_width, uint16_t* out_height)
+{
+  auto track = track_ptr->track;
+
+  auto visual_track = std::dynamic_pointer_cast<Track_Visual>(track);
+  if (!visual_track) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Cannot get resolution of non-visual track."};
+  }
+
+  if (out_width) *out_width = visual_track->get_width();
+  if (out_height) *out_height = visual_track->get_height();
+
+  return heif_error_ok;
+}
+
+
+struct heif_error heif_track_decode_next_image(struct heif_track* track_ptr,
+                                               struct heif_image** out_img,
+                                               enum heif_colorspace colorspace,
+                                               enum heif_chroma chroma,
+                                               const struct heif_decoding_options* options)
+{
+  if (out_img == nullptr) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "Output image pointer is NULL."};
+  }
+
+  // --- get the visual track
+
+  auto track = track_ptr->track;
+
+  // --- reached end of sequence ?
+
+  if (track->end_of_sequence_reached()) {
+    *out_img = nullptr;
+    return {heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"};
+  }
+
+  // --- decode next sequence image
+
+  std::unique_ptr<heif_decoding_options, void(*)(heif_decoding_options*)> opts(heif_decoding_options_alloc(), heif_decoding_options_free);
+  heif_decoding_options_copy(opts.get(), options);
+
+
+  auto visual_track = std::dynamic_pointer_cast<Track_Visual>(track);
+  if (!visual_track) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Cannot get image from non-visual track."};
+  }
+
+  auto decodingResult = visual_track->decode_next_image_sample(*opts);
+  if (!decodingResult) {
+    return decodingResult.error.error_struct(track_ptr->context.get());
+  }
+
+  std::shared_ptr<HeifPixelImage> img = *decodingResult;
+
+
+  // --- convert to output colorspace
+
+  auto conversion_result = track_ptr->context->convert_to_output_colorspace(img, colorspace, chroma, *opts);
+  if (conversion_result.error) {
+    return conversion_result.error.error_struct(track_ptr->context.get());
+  }
+  else {
+    img = *conversion_result;
+  }
+
+  *out_img = new heif_image();
+  (*out_img)->image = std::move(img);
+
+  return {};
+}
+
+
+uint32_t heif_image_get_duration(const heif_image* img)
+{
+  return img->image->get_sample_duration();
+}
+
+
+uint32_t heif_track_get_sample_entry_type_of_first_cluster(const struct heif_track* track)
+{
+  return track->track->get_first_cluster_sample_entry_type();
+}
+
+
+heif_error heif_track_get_urim_sample_entry_uri_of_first_cluster(const struct heif_track* track, const char** out_uri)
+{
+  Result<std::string> uriResult = track->track->get_first_cluster_urim_uri();
+
+  if (uriResult.error.error_code) {
+    return uriResult.error.error_struct(track->context.get());
+  }
+
+  if (out_uri) {
+    const std::string& uri = uriResult.value;
+
+    char* s = new char[uri.size() + 1];
+    strncpy(s, uri.c_str(), uri.size());
+    s[uri.size()] = '\0';
+
+    *out_uri = s;
+  }
+
+  return heif_error_ok;
+}
+
+
+struct heif_error heif_track_get_next_raw_sequence_sample(struct heif_track* track_ptr,
+                                                          heif_raw_sequence_sample** out_sample)
+{
+  auto track = track_ptr->track;
+
+  // --- reached end of sequence ?
+
+  if (track->end_of_sequence_reached()) {
+    return {heif_error_End_of_sequence, heif_suberror_Unspecified, "End of sequence"};
+  }
+
+  // --- get next raw sample
+
+  auto decodingResult = track->get_next_sample_raw_data();
+  if (!decodingResult) {
+    return decodingResult.error.error_struct(track_ptr->context.get());
+  }
+
+  *out_sample = decodingResult.value;
+
+  return heif_error_success;
+}
+
+
+void heif_raw_sequence_sample_release(struct heif_raw_sequence_sample* sample)
+{
+  delete sample;
+}
+
+
+const uint8_t* heif_raw_sequence_sample_get_data(const heif_raw_sequence_sample* sample, size_t* out_array_size)
+{
+  if (out_array_size) { *out_array_size = sample->data.size(); }
+
+  return sample->data.data();
+}
+
+
+size_t heif_raw_sequence_sample_get_data_size(const heif_raw_sequence_sample* sample)
+{
+  return sample->data.size();
+}
+
+
+uint32_t heif_raw_sequence_sample_get_duration(const heif_raw_sequence_sample* sample)
+{
+  return sample->duration;
+}
+
+
+// --- writing sequences
+
+
+void heif_context_set_sequence_timescale(heif_context* ctx, uint32_t timescale)
+{
+  ctx->context->set_sequence_timescale(timescale);
+}
+
+
+struct heif_track_options {
+  TrackOptions options;
+};
+
+
+heif_track_options* heif_track_options_alloc()
+{
+  return new heif_track_options;
+}
+
+
+void heif_track_options_release(struct heif_track_options* options)
+{
+  delete options;
+}
+
+
+void heif_track_options_set_timescale(struct heif_track_options* options, uint32_t timescale)
+{
+  options->options.track_timescale = timescale;
+}
+
+
+void heif_track_options_set_interleaved_sample_aux_infos(struct heif_track_options* options, int interleaved_flag)
+{
+  options->options.write_sample_aux_infos_interleaved = (interleaved_flag != 0);
+}
+
+
+struct heif_error heif_track_options_enable_sample_tai_timestamps(struct heif_track_options* options,
+                                                                  const struct heif_tai_clock_info* tai_info,
+                                                                  enum heif_sample_aux_info_presence presence)
+{
+  if (presence != heif_sample_aux_info_presence_none &&
+      tai_info == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Unspecified,
+            "NULL tai clock info passed for track with TAI timestamps"};
+  }
+
+  options->options.with_sample_tai_timestamps = presence;
+
+  // delete old object in case we are calling heif_track_options_enable_sample_tai_timestamps() multiple times
+  delete options->options.tai_clock_info;
+
+  if (tai_info != nullptr) {
+    options->options.tai_clock_info = heif_tai_clock_info_alloc();
+    heif_tai_clock_info_copy(options->options.tai_clock_info, tai_info);
+  }
+  else {
+    options->options.tai_clock_info = nullptr;
+  }
+
+  return heif_error_ok;
+}
+
+
+void heif_track_options_enable_sample_gimi_content_ids(struct heif_track_options* options,
+                                                       enum heif_sample_aux_info_presence presence)
+{
+  options->options.with_sample_content_ids = presence;
+}
+
+
+void heif_track_options_set_gimi_track_id(struct heif_track_options* options,
+                                          const char* track_id)
+{
+  if (track_id == nullptr) {
+    options->options.gimi_track_content_id.clear();
+    return;
+  }
+
+  options->options.gimi_track_content_id = track_id;
+}
+
+
+heif_sequence_encoding_options* heif_sequence_encoding_options_alloc()
+{
+  heif_sequence_encoding_options* options = new heif_sequence_encoding_options();
+
+  options->version = 1;
+  options->output_nclx_profile = nullptr;
+
+  options->color_conversion_options.version = 1;
+  options->color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average;
+  options->color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear;
+  options->color_conversion_options.only_use_preferred_chroma_algorithm = false;
+
+  return options;
+}
+
+
+void heif_sequence_encoding_options_release(heif_sequence_encoding_options* options)
+{
+  delete options;
+}
+
+
+struct heif_error heif_context_add_visual_sequence_track(heif_context* ctx,
+                                                         uint16_t width, uint16_t height,
+                                                         heif_track_type track_type,
+                                                         const struct heif_track_options* track_options,
+                                                         const struct heif_sequence_encoding_options* encoding_options,
+                                                         heif_track** out_track)
+{
+  if (track_type != heif_track_type_video &&
+      track_type != heif_track_type_image_sequence) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "visual track has to be of type video or image sequence"};
+  }
+
+  TrackOptions default_track_info;
+  const TrackOptions* track_info = &default_track_info;
+  if (track_options != nullptr) {
+    track_info = &track_options->options;
+  }
+
+  Result<std::shared_ptr<Track_Visual>> addResult = ctx->context->add_visual_sequence_track(track_info, track_type, width,height);
+  if (addResult.error) {
+    return addResult.error.error_struct(ctx->context.get());
+  }
+
+  if (out_track) {
+    auto* track = new heif_track;
+    track->track = *addResult;
+    track->context = ctx->context;
+
+    *out_track = track;
+  }
+
+  return heif_error_ok;
+}
+
+
+void heif_image_set_duration(heif_image* img, uint32_t duration)
+{
+  img->image->set_sample_duration(duration);
+}
+
+
+struct heif_error heif_track_encode_sequence_image(struct heif_track* track,
+                                                   const struct heif_image* input_image,
+                                                   struct heif_encoder* encoder,
+                                                   const struct heif_sequence_encoding_options* sequence_encoding_options)
+{
+  // the input track must be a visual track
+
+  auto visual_track = std::dynamic_pointer_cast<Track_Visual>(track->track);
+  if (!visual_track) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Cannot encode image for non-visual track."};
+  }
+
+
+  // convert heif_sequence_encoding_options to heif_encoding_options that is used by track->encode_image()
+
+  heif_encoding_options* encoding_options = heif_encoding_options_alloc();
+  heif_color_profile_nclx nclx;
+  if (sequence_encoding_options) {
+    if (sequence_encoding_options->version >= 4) {
+      // the const_cast<> is ok, because output_nclx_profile will not be changed. It should actually be const, but we cannot change that.
+      encoding_options->output_nclx_profile = const_cast<heif_color_profile_nclx*>(sequence_encoding_options->output_nclx_profile);
+    }
+
+    if (sequence_encoding_options->version >= 6) {
+      encoding_options->color_conversion_options = sequence_encoding_options->color_conversion_options;
+    }
+
+    if (encoding_options->output_nclx_profile == nullptr) {
+      auto input_nclx = input_image->image->get_color_profile_nclx();
+      if (input_nclx) {
+        encoding_options->output_nclx_profile = &nclx;
+        nclx.version = 1;
+        nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries();
+        nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics();
+        nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients();
+        nclx.full_range_flag = input_nclx->get_full_range_flag();
+      }
+    }
+  }
+
+  // encode the image
+
+  auto error = visual_track->encode_image(input_image->image,
+                                          encoder,
+                                          *encoding_options,
+                                          heif_image_input_class_normal);
+  heif_encoding_options_free(encoding_options);
+
+  if (error.error_code) {
+    return error.error_struct(track->context.get());
+  }
+
+  return heif_error_ok;
+}
+
+
+struct heif_error heif_context_add_uri_metadata_sequence_track(heif_context* ctx,
+                                                               const char* uri,
+                                                               const struct heif_track_options* track_options,
+                                                               heif_track** out_track)
+{
+  struct TrackOptions default_track_info;
+  const struct TrackOptions* track_info = &default_track_info;
+  if (track_options != nullptr) {
+    track_info = &track_options->options;
+  }
+
+  Result<std::shared_ptr<Track_Metadata>> addResult = ctx->context->add_uri_metadata_sequence_track(track_info, uri);
+  if (addResult.error) {
+    return addResult.error.error_struct(ctx->context.get());
+  }
+
+  if (out_track) {
+    auto* track = new heif_track;
+    track->track = *addResult;
+    track->context = ctx->context;
+
+    *out_track = track;
+  }
+
+  return heif_error_ok;
+}
+
+
+heif_raw_sequence_sample* heif_raw_sequence_sample_alloc()
+{
+  return new heif_raw_sequence_sample();
+}
+
+
+heif_error heif_raw_sequence_sample_set_data(heif_raw_sequence_sample* sample, const uint8_t* data, size_t size)
+{
+  // TODO: do we have to check the vector memory allocation?
+
+  sample->data.clear();
+  sample->data.insert(sample->data.begin(), data, data + size);
+
+  return heif_error_ok;
+}
+
+
+void heif_raw_sequence_sample_set_duration(heif_raw_sequence_sample* sample, uint32_t duration)
+{
+  sample->duration = duration;
+}
+
+
+struct heif_error heif_track_add_raw_sequence_sample(struct heif_track* track,
+                                                     const struct heif_raw_sequence_sample* sample)
+{
+  auto metadata_track = std::dynamic_pointer_cast<Track_Metadata>(track->track);
+  if (!metadata_track) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Cannot save metadata in a non-metadata track."};
+  }
+
+  auto error = metadata_track->write_raw_metadata(sample);
+  if (error.error_code) {
+    return error.error_struct(track->context.get());
+  }
+
+  return heif_error_ok;
+}
+
+
+int heif_track_get_number_of_sample_aux_infos(const struct heif_track* track)
+{
+  std::vector<heif_sample_aux_info_type> aux_info_types = track->track->get_sample_aux_info_types();
+  return (int)aux_info_types.size();
+}
+
+
+void heif_track_get_sample_aux_info_types(const struct heif_track* track, struct heif_sample_aux_info_type out_types[])
+{
+  std::vector<heif_sample_aux_info_type> aux_info_types = track->track->get_sample_aux_info_types();
+  for (size_t i=0;i<aux_info_types.size();i++) {
+    out_types[i] = aux_info_types[i];
+  }
+}
+
+
+const char* heif_track_get_gimi_track_content_id(const heif_track* track)
+{
+  std::string contentId = track->track->get_track_info().gimi_track_content_id;
+  if (contentId.empty()) {
+    return nullptr;
+  }
+
+  char* id = new char[contentId.length() + 1];
+  strcpy(id, contentId.c_str());
+
+  return id;
+}
+
+
+const char* heif_image_get_gimi_sample_content_id(const heif_image* img)
+{
+  if (!img->image->has_gimi_sample_content_id()) {
+    return nullptr;
+  }
+
+  auto id_string = img->image->get_gimi_sample_content_id();
+  char* id = new char[id_string.length() + 1];
+  strcpy(id, id_string.c_str());
+
+  return id;
+}
+
+
+const char* heif_raw_sequence_sample_get_gimi_sample_content_id(const heif_raw_sequence_sample* sample)
+{
+  char* s = new char[sample->gimi_sample_content_id.size() + 1];
+  strcpy(s, sample->gimi_sample_content_id.c_str());
+  return s;
+}
+
+
+void heif_image_set_gimi_sample_content_id(heif_image* img, const char* contentID)
+{
+  if (contentID) {
+    img->image->set_gimi_sample_content_id(contentID);
+  }
+  else {
+    img->image->set_gimi_sample_content_id({});
+  }
+}
+
+
+void heif_raw_sequence_sample_set_gimi_sample_content_id(heif_raw_sequence_sample* sample, const char* contentID)
+{
+  if (contentID) {
+    sample->gimi_sample_content_id = contentID;
+  }
+  else {
+    sample->gimi_sample_content_id.clear();
+  }
+}
+
+
+int heif_raw_sequence_sample_has_tai_timestamp(const struct heif_raw_sequence_sample* sample)
+{
+  return sample->timestamp ? 1 : 0;
+}
+
+
+const struct heif_tai_timestamp_packet* heif_raw_sequence_sample_get_tai_timestamp(const struct heif_raw_sequence_sample* sample)
+{
+  if (!sample->timestamp) {
+    return nullptr;
+  }
+
+  return sample->timestamp;
+}
+
+
+void heif_raw_sequence_sample_set_tai_timestamp(struct heif_raw_sequence_sample* sample,
+                                                const struct heif_tai_timestamp_packet* timestamp)
+{
+  // release of timestamp in case we overwrite it
+  heif_tai_timestamp_packet_release(sample->timestamp);
+
+  sample->timestamp = heif_tai_timestamp_packet_alloc();
+  heif_tai_timestamp_packet_copy(sample->timestamp, timestamp);
+}
+
+
+const struct heif_tai_clock_info* heif_track_get_tai_clock_info_of_first_cluster(struct heif_track* track)
+{
+  auto first_taic = track->track->get_first_cluster_taic();
+  if (!first_taic) {
+    return nullptr;
+  }
+
+  return first_taic->get_tai_clock_info();
+}
+
+
+void heif_track_add_reference_to_track(heif_track* track, uint32_t reference_type, const heif_track* to_track)
+{
+  track->track->add_reference_to_track(reference_type, to_track->track->get_id());
+}
+
+
+size_t heif_track_get_number_of_track_reference_types(const heif_track* track)
+{
+  auto tref = track->track->get_tref_box();
+  if (!tref) {
+    return 0;
+  }
+
+  return tref->get_number_of_reference_types();
+}
+
+
+void heif_track_get_track_reference_types(const heif_track* track, uint32_t out_reference_types[])
+{
+  auto tref = track->track->get_tref_box();
+  if (!tref) {
+    return;
+  }
+
+  auto refTypes = tref->get_reference_types();
+  for (size_t i = 0; i < refTypes.size(); i++) {
+    out_reference_types[i] = refTypes[i];
+  }
+}
+
+
+size_t heif_track_get_number_of_track_reference_of_type(const heif_track* track, uint32_t reference_type)
+{
+  auto tref = track->track->get_tref_box();
+  if (!tref) {
+    return 0;
+  }
+
+  return tref->get_number_of_references_of_type(reference_type);
+}
+
+
+size_t heif_track_get_references_from_track(const heif_track* track, uint32_t reference_type, uint32_t out_to_track_id[])
+{
+  auto tref = track->track->get_tref_box();
+  if (!tref) {
+    return 0;
+  }
+
+  auto refs = tref->get_references(reference_type);
+  for (size_t i = 0; i < refs.size(); i++) {
+    out_to_track_id[i] = refs[i];
+  }
+
+  return refs.size();
+}
+
+
+size_t heif_track_find_referring_tracks(const heif_track* track, uint32_t reference_type, uint32_t out_track_id[], size_t array_size)
+{
+  size_t nFound = 0;
+
+  // iterate through all tracks
+
+  auto trackIDs = track->context->get_track_IDs();
+  for (auto id : trackIDs) {
+    // a track should never reference itself
+    if (id == track->track->get_id()) {
+      continue;
+    }
+
+    // get the other track object
+
+    auto other_trackResult = track->context->get_track(id);
+    if (other_trackResult.error) {
+      continue; // TODO: should we return an error in this case?
+    }
+
+    auto other_track = other_trackResult.value;
+
+    // get the references of the other track
+
+    auto tref = other_track->get_tref_box();
+    if (!tref) {
+      continue;
+    }
+
+    // if the other track has a reference that points to the current track, add the other track to the list
+
+    std::vector<uint32_t> refs = tref->get_references(reference_type);
+    for (uint32_t to_track : refs) {
+      if (to_track == track->track->get_id() && nFound < array_size) {
+        out_track_id[nFound++] = other_track->get_id();
+        break;
+      }
+    }
+
+    // quick exit path
+    if (nFound == array_size)
+      break;
+  }
+
+  return nFound;
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_sequences.h 1.20.1-1/libheif/api/libheif/heif_sequences.h
--- 1.19.8-1/libheif/api/libheif/heif_sequences.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_sequences.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,577 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_SEQUENCES_H
+#define LIBHEIF_HEIF_SEQUENCES_H
+
+#include "libheif/heif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// forward declaration of structs defined in heif_tai_timestamps.h
+typedef struct heif_tai_clock_info heif_tai_clock_info;
+typedef struct heif_tai_timestamp_packet heif_tai_timestamp_packet;
+
+
+// --- reading sequence tracks
+
+/**
+ * Check whether there is an image sequence in the HEIF file.
+ *
+ * @return A boolean whether there is an image sequence in the HEIF file.
+ */
+LIBHEIF_API
+int heif_context_has_sequence(const heif_context*);
+
+/**
+ * Get the timescale (clock ticks per second) for timing values in the sequence.
+ *
+ * @note Each track may have its independent timescale.
+ *
+ * @return Clock ticks per second. Returns 0 if there is no sequence in the file.
+ */
+LIBHEIF_API
+uint32_t heif_context_get_sequence_timescale(const heif_context*);
+
+/**
+ * Get the total duration of the sequence in timescale clock ticks.
+ * Use `heif_context_get_sequence_timescale()` to get the clock ticks per second.
+ *
+ * @return Sequence duration in clock ticks. Returns 0 if there is no sequence in the file.
+ */
+LIBHEIF_API
+uint64_t heif_context_get_sequence_duration(const heif_context*);
+
+
+// A track, which may be an image sequence, a video track or a metadata track.
+typedef struct heif_track heif_track;
+
+/**
+ * Free a `heif_track` object received from libheif.
+ * Passing NULL is ok.
+ */
+LIBHEIF_API
+void heif_track_release(heif_track*);
+
+/**
+ * Get the number of tracks in the HEIF file.
+ *
+ * @return Number of tracks or 0 if there is no sequence in the HEIF file.
+ */
+LIBHEIF_API
+int heif_context_number_of_sequence_tracks(const heif_context*);
+
+/**
+ * Returns the IDs for each of the tracks stored in the HEIF file.
+ * The output array must have heif_context_number_of_sequence_tracks() entries.
+ */
+LIBHEIF_API
+void heif_context_get_track_ids(const heif_context* ctx, uint32_t out_track_id_array[]);
+
+/**
+ * Get the ID of the passed track.
+ * The track ID will never be 0.
+ */
+LIBHEIF_API
+uint32_t heif_track_get_id(const heif_track* track);
+
+/**
+ * Get the heif_track object for the given track ID.
+ * If you pass `id=0`, the first visual track will be returned.
+ * If there is no track with the given ID or if 0 is passed and there is no visual track, NULL will be returned.
+ *
+ * @note Tracks never have a zero ID. This is why we can use this as a special value to find the first visual track.
+ *
+ * @param id Track id or 0 for the first visual track.
+ * @return heif_track object. You must free this after use.
+ */
+// Use id=0 for the first visual track.
+LIBHEIF_API
+heif_track* heif_context_get_track(const heif_context*, uint32_t id);
+
+
+typedef uint32_t heif_track_type;
+
+enum heif_track_type_4cc
+{
+  heif_track_type_video = heif_fourcc('v', 'i', 'd', 'e'),
+  heif_track_type_image_sequence = heif_fourcc('p', 'i', 'c', 't'),
+  heif_track_type_metadata = heif_fourcc('m', 'e', 't', 'a')
+};
+
+/**
+ * Get the four-cc track handler type.
+ * Typical codes are "vide" for video sequences, "pict" for image sequences, "meta" for metadata tracks.
+ * These are defined in heif_track_type_4cc, but files may also contain other types.
+ *
+ * @return four-cc handler type
+ */
+LIBHEIF_API
+heif_track_type heif_track_get_track_handler_type(const heif_track*);
+
+/**
+ * Get the timescale (clock ticks per second) for this track.
+ * Note that this can be different from the timescale used at sequence level.
+ *
+ * @return clock ticks per second
+ */
+LIBHEIF_API
+uint32_t heif_track_get_timescale(const heif_track*);
+
+
+// --- reading visual tracks
+
+/**
+ * Get the image resolution of the track.
+ * If the passed track is no visual track, an error is returned.
+ */
+LIBHEIF_API
+heif_error heif_track_get_image_resolution(const heif_track*, uint16_t* out_width, uint16_t* out_height);
+
+/**
+ * Decode the next image in the passed sequence track.
+ * If there is no more image in the sequence, `heif_error_End_of_sequence` is returned.
+ * The parameters `colorspace`, `chroma` and `options` are similar to heif_decode_image().
+ * If you want to let libheif decide the output colorspace and chroma, set these parameters
+ * to heif_colorspace_undefined / heif_chroma_undefined. Usually, libheif will return the
+ * image in the input colorspace, but it may also modify it for example when it has to rotate the image.
+ * If you want to get the image in a specific colorspace/chroma format, you can specify this
+ * and libheif will convert the image to match this format.
+ */
+LIBHEIF_API
+heif_error heif_track_decode_next_image(heif_track* track,
+                                        heif_image** out_img,
+                                        enum heif_colorspace colorspace,
+                                        enum heif_chroma chroma,
+                                        const heif_decoding_options* options);
+
+/**
+ * Get the image display duration in clock ticks of this track.
+ * Make sure to use the timescale of the track and not the timescale of the total sequence.
+ */
+LIBHEIF_API
+uint32_t heif_image_get_duration(const heif_image*);
+
+
+// --- reading metadata track samples
+
+/**
+ * Get the "sample entry type" of the first sample sample cluster in the track.
+ * In the case of metadata tracks, this will usually be "urim" for "URI Meta Sample Entry".
+ * The exact URI can then be obtained with 'heif_track_get_urim_sample_entry_uri_of_first_cluster'.
+ */
+LIBHEIF_API
+uint32_t heif_track_get_sample_entry_type_of_first_cluster(const heif_track*);
+
+/**
+ * Get the URI of the first sample cluster in an 'urim' track.
+ * Only call this for tracks with 'urim' sample entry types. It will return an error otherwise.
+ *
+ * @param out_uri A string with the URI will be returned. Free this string with `heif_string_release()`.
+ */
+LIBHEIF_API
+heif_error heif_track_get_urim_sample_entry_uri_of_first_cluster(const heif_track* track, const char** out_uri);
+
+
+/** Sequence sample object that can hold any raw byte data.
+ * Use this to store and read raw metadata samples.
+ */
+typedef struct heif_raw_sequence_sample heif_raw_sequence_sample;
+
+/**
+ * Get the next raw sample from the (metadata) sequence track.
+ * You have to free the returned sample with heif_raw_sequence_sample_release().
+ */
+LIBHEIF_API
+heif_error heif_track_get_next_raw_sequence_sample(heif_track*,
+                                                   heif_raw_sequence_sample** out_sample);
+
+/**
+ * Release a heif_raw_sequence_sample object.
+ * You may pass NULL.
+ */
+LIBHEIF_API
+void heif_raw_sequence_sample_release(heif_raw_sequence_sample*);
+
+/**
+ * Get a pointer to the data of the (metadata) sample.
+ * The data pointer stays valid until the heif_raw_sequence_sample object is released.
+ *
+ * @param out_array_size Size of the returned array (may be NULL).
+ */
+LIBHEIF_API
+const uint8_t* heif_raw_sequence_sample_get_data(const heif_raw_sequence_sample*, size_t* out_array_size);
+
+/**
+ * Return the size of the raw data contained in the sample.
+ * This is the same as returned through the 'out_array_size' parameter of 'heif_raw_sequence_sample_get_data()'.
+ */
+LIBHEIF_API
+size_t heif_raw_sequence_sample_get_data_size(const heif_raw_sequence_sample*);
+
+/**
+ * Get the sample duration in clock ticks of this track.
+ * Make sure to use the timescale of the track and not the timescale of the total sequence.
+ */
+LIBHEIF_API
+uint32_t heif_raw_sequence_sample_get_duration(const heif_raw_sequence_sample*);
+
+
+// --- writing sequences
+
+/**
+ * Set an independent global timescale for the sequence.
+ * If no timescale is set with this function, the timescale of the first track will be used.
+ */
+LIBHEIF_API
+void heif_context_set_sequence_timescale(heif_context*, uint32_t timescale);
+
+
+/**
+ * Specifies whether a 'sample auxiliary info' is stored with the samples.
+ * The difference between `heif_sample_aux_info_presence_optional` and `heif_sample_aux_info_presence_mandatory`
+ * is that `heif_sample_aux_info_presence_mandatory` will throw an error if the data is missing when writing a sample.
+ */
+enum heif_sample_aux_info_presence
+{
+  heif_sample_aux_info_presence_none = 0,
+  heif_sample_aux_info_presence_optional = 1,
+  heif_sample_aux_info_presence_mandatory = 2
+};
+
+
+typedef struct heif_track_options heif_track_options;
+
+/**
+ * Allocate track options object that is required to set options for a new track.
+ * When you create a new track, you can also pass a NULL heif_track_options pointer, in which case the default options are used.
+ */
+LIBHEIF_API
+heif_track_options* heif_track_options_alloc(void);
+
+LIBHEIF_API
+void heif_track_options_release(heif_track_options*);
+
+/**
+ * Set the track specific timescale. This is the number of clock ticks per second.
+ * The default is 90,000 Hz.
+ * @param timescale
+ */
+LIBHEIF_API
+void heif_track_options_set_timescale(heif_track_options*, uint32_t timescale);
+
+/**
+ * Set whether the aux-info data should be stored interleaved with the sequence samples.
+ * Default is: false.
+ *
+ * If 'true', the aux_info data blocks will be interleaved with the compressed image.
+ * This has the advantage that the aux_info is localized near the image data.
+ *
+ * If 'false', all aux_info will be written as one block after the compressed image data.
+ * This has the advantage that no aux_info offsets have to be written.
+ */
+LIBHEIF_API
+void heif_track_options_set_interleaved_sample_aux_infos(heif_track_options*, int interleaved_flag);
+
+LIBHEIF_API
+heif_error heif_track_options_enable_sample_tai_timestamps(heif_track_options*,
+                                                           const heif_tai_clock_info*,
+                                                           enum heif_sample_aux_info_presence);
+
+LIBHEIF_API
+void heif_track_options_enable_sample_gimi_content_ids(heif_track_options*,
+                                                       enum heif_sample_aux_info_presence);
+
+/**
+ * Set the GIMI format track ID string. If NULL is passed, no track ID is saved.
+ * @param track_id
+ */
+LIBHEIF_API
+void heif_track_options_set_gimi_track_id(heif_track_options*,
+                                          const char* track_id);
+
+
+// --- writing visual tracks
+
+// This structure is for future use. It is not defined yet.
+typedef struct heif_sequence_encoding_options
+{
+  uint8_t version;
+
+  // version 1 options
+
+  // Set this to the NCLX parameters to be used in the output images or set to NULL
+  // when the same parameters as in the input images should be used.
+  const heif_color_profile_nclx* output_nclx_profile;
+
+  heif_color_conversion_options color_conversion_options;
+} heif_sequence_encoding_options;
+
+LIBHEIF_API
+heif_sequence_encoding_options* heif_sequence_encoding_options_alloc(void);
+
+LIBHEIF_API
+void heif_sequence_encoding_options_release(heif_sequence_encoding_options*);
+
+/**
+ * Add a visual track to the sequence.
+ * The track ID is assigned automatically.
+ *
+ * @param width Image resolution width
+ * @param height Image resolution height
+ * @param track_type Has to be heif_track_type_video or heif_track_type_image_sequence
+ * @param track_options Optional track creation options. If NULL, default options will be used.
+ * @param encoding_options Options for sequence encoding. If NULL, default options will be used.
+ * @param out_track Output parameter to receive the track object for the just created track.
+ * @return
+ */
+LIBHEIF_API
+heif_error heif_context_add_visual_sequence_track(heif_context*,
+                                                  uint16_t width, uint16_t height,
+                                                  heif_track_type track_type,
+                                                  const heif_track_options* track_options,
+                                                  const heif_sequence_encoding_options* encoding_options,
+                                                  heif_track** out_track);
+
+/**
+ * Set the image display duration in the track's timescale units.
+ */
+LIBHEIF_API
+void heif_image_set_duration(heif_image*, uint32_t duration);
+
+/**
+ * Encode the image into a visual track.
+ * If the passed track is no visual track, an error will be returned.
+ *
+ * @param sequence_encoding_options Options for sequence encoding. If NULL, default options will be used.
+ */
+LIBHEIF_API
+heif_error heif_track_encode_sequence_image(heif_track*,
+                                            const heif_image* image,
+                                            heif_encoder* encoder,
+                                            const heif_sequence_encoding_options* sequence_encoding_options);
+
+// --- metadata tracks
+
+/**
+ * Add a metadata track.
+ * The track content type is specified by the 'uri' parameter.
+ * This will be created as a 'urim' "URI Meta Sample Entry".
+ *
+ * @param options Optional track creation options. If NULL, default options will be used.
+ */
+LIBHEIF_API
+heif_error heif_context_add_uri_metadata_sequence_track(heif_context*,
+                                                        const char* uri,
+                                                        const heif_track_options* options,
+                                                        heif_track** out_track);
+
+/**
+ * Allocate a new heif_raw_sequence_sample object.
+ * Free with heif_raw_sequence_sample_release().
+ */
+LIBHEIF_API
+heif_raw_sequence_sample* heif_raw_sequence_sample_alloc(void);
+
+/**
+ * Set the raw sequence sample data.
+ */
+LIBHEIF_API
+heif_error heif_raw_sequence_sample_set_data(heif_raw_sequence_sample*, const uint8_t* data, size_t size);
+
+/**
+ * Set the sample duration in track timescale units.
+ */
+LIBHEIF_API
+void heif_raw_sequence_sample_set_duration(heif_raw_sequence_sample*, uint32_t duration);
+
+/**
+ * Add a raw sequence sample (usually a metadata sample) to the (metadata) track.
+ */
+LIBHEIF_API
+heif_error heif_track_add_raw_sequence_sample(heif_track*,
+                                              const heif_raw_sequence_sample*);
+
+
+// --- sample auxiliary data
+
+/**
+ * Contains the type of sample auxiliary data assigned to the track samples.
+ */
+typedef struct heif_sample_aux_info_type
+{
+  uint32_t type;
+  uint32_t parameter;
+} heif_sample_aux_info_type;
+
+/**
+ * Returns how many different types of sample auxiliary data units are assigned to this track's samples.
+ */
+LIBHEIF_API
+int heif_track_get_number_of_sample_aux_infos(const heif_track*);
+
+/**
+ * Get get the list of sample auxiliary data types used in the track.
+ * The passed array has to have heif_track_get_number_of_sample_aux_infos() entries.
+ */
+LIBHEIF_API
+void heif_track_get_sample_aux_info_types(const heif_track*, heif_sample_aux_info_type out_types[]);
+
+
+// --- GIMI content IDs
+
+/**
+ * Get the GIMI content ID for the track (as a whole).
+ * If there is no content ID, nullptr is returned.
+ *
+ * @return The returned string has to be released with `heif_string_release()`.
+ */
+LIBHEIF_API
+const char* heif_track_get_gimi_track_content_id(const heif_track*);
+
+/**
+ * Get the GIMI content ID stored in the image sample.
+ * If there is no content ID, NULL is returned.
+ * @return
+ */
+LIBHEIF_API
+const char* heif_image_get_gimi_sample_content_id(const heif_image*);
+
+/**
+ * Get the GIMI content ID stored in the metadata sample.
+ * If there is no content ID, NULL is returned.
+ * @return
+ */
+LIBHEIF_API
+const char* heif_raw_sequence_sample_get_gimi_sample_content_id(const heif_raw_sequence_sample*);
+
+/**
+ * Set the GIMI content ID for an image sample. It will be stored as SAI.
+ * When passing NULL, a previously set ID will be removed.
+ */
+LIBHEIF_API
+void heif_image_set_gimi_sample_content_id(heif_image*, const char* contentID);
+
+/**
+ * Set the GIMI content ID for a (metadata) sample. It will be stored as SAI.
+ * When passing NULL, a previously set ID will be removed.
+ */
+LIBHEIF_API
+void heif_raw_sequence_sample_set_gimi_sample_content_id(heif_raw_sequence_sample*, const char* contentID);
+
+
+// --- TAI timestamps
+
+// Note: functions for setting timestamps on images are in heif_tai_timestamps.h
+
+/**
+ * Returns whether the raw (metadata) sample has a TAI timestamp attached to it (stored as SAI).
+ *
+ * @return boolean flag whether a TAI exists for this sample.
+ */
+LIBHEIF_API
+int heif_raw_sequence_sample_has_tai_timestamp(const heif_raw_sequence_sample*);
+
+/**
+ * Get the TAI timestamp of the (metadata) sample.
+ * If there is no timestamp assigned to it, NULL will be returned.
+ *
+ * @note You should NOT free the returned timestamp with 'heif_tai_timestamp_packet_release()'.
+ *       The returned struct stays valid until the heif_raw_sequence_sample is released.
+ */
+LIBHEIF_API
+const heif_tai_timestamp_packet* heif_raw_sequence_sample_get_tai_timestamp(const heif_raw_sequence_sample*);
+
+/**
+ * Set the TAI timestamp for a raw sequence sample.
+ * The timestamp will be copied, you can release it after calling this function.
+ */
+LIBHEIF_API
+void heif_raw_sequence_sample_set_tai_timestamp(heif_raw_sequence_sample* sample,
+                                                const heif_tai_timestamp_packet* timestamp);
+
+/**
+ * Returns the TAI clock info of the track.
+ * If there is no TAI clock info, NULL is returned.
+ * You should NOT free the returned heif_tai_clock_info.
+ * The structure stays valid until the heif_track object is released.
+ */
+LIBHEIF_API
+const heif_tai_clock_info* heif_track_get_tai_clock_info_of_first_cluster(heif_track*);
+
+
+// --- track references
+
+enum heif_track_reference_type
+{
+  heif_track_reference_type_description = heif_fourcc('c', 'd', 's', 'c'), // track_description
+  heif_track_reference_type_thumbnails = heif_fourcc('t', 'h', 'm', 'b'), // thumbnails
+  heif_track_reference_type_auxiliary = heif_fourcc('a', 'u', 'x', 'l') // auxiliary data (e.g. depth maps or alpha channel)
+};
+
+/**
+ * Add a reference between tracks.
+ * 'reference_type' can be one of the four-cc codes listed in heif_track_reference_type or any other type.
+ */
+LIBHEIF_API
+void heif_track_add_reference_to_track(heif_track*, uint32_t reference_type, const heif_track* to_track);
+
+/**
+ * Return the number of different reference types used in this track's tref box.
+ */
+LIBHEIF_API
+size_t heif_track_get_number_of_track_reference_types(const heif_track*);
+
+/**
+ * List the reference types used in this track.
+ * The passed array must have heif_track_get_number_of_track_reference_types() entries.
+ */
+LIBHEIF_API
+void heif_track_get_track_reference_types(const heif_track*, uint32_t out_reference_types[]);
+
+/**
+ * Get the number of references of the passed type.
+ */
+LIBHEIF_API
+size_t heif_track_get_number_of_track_reference_of_type(const heif_track*, uint32_t reference_type);
+
+/**
+ * List the track ids this track points to with the passed reference type.
+ * The passed array must have heif_track_get_number_of_track_reference_of_type() entries.
+ */
+LIBHEIF_API
+size_t heif_track_get_references_from_track(const heif_track*, uint32_t reference_type, uint32_t out_to_track_id[]);
+
+/**
+ * Find tracks that are referring to the current track through the passed reference_type.
+ * The found track IDs will be filled into the passed array, but no more than `array_size` entries will be filled.
+ *
+ * @return number of tracks found. If this is equal to 'array_size', you should ask again with a larger array size to be sure you got all tracks.
+ */
+LIBHEIF_API
+size_t heif_track_find_referring_tracks(const heif_track*, uint32_t reference_type, uint32_t out_track_id[], size_t array_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_tai_timestamps.cc 1.20.1-1/libheif/api/libheif/heif_tai_timestamps.cc
--- 1.19.8-1/libheif/api/libheif/heif_tai_timestamps.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_tai_timestamps.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,281 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libheif/heif_tai_timestamps.h>
+#include <libheif/api_structs.h>
+#include "box.h"
+#include "file.h"
+#include <memory>
+
+
+void initialize_heif_tai_clock_info(heif_tai_clock_info* taic)
+{
+  taic->version = 1;
+  taic->time_uncertainty = heif_tai_clock_info_time_uncertainty_unknown;
+  taic->clock_resolution = 0;
+  taic->clock_drift_rate = heif_tai_clock_info_clock_drift_rate_unknown;
+  taic->clock_type = heif_tai_clock_info_clock_type_unknown;
+}
+
+heif_tai_clock_info* heif_tai_clock_info_alloc()
+{
+  auto* taic = new heif_tai_clock_info;
+  initialize_heif_tai_clock_info(taic);
+
+  return taic;
+}
+
+
+void initialize_heif_tai_timestamp_packet(heif_tai_timestamp_packet* itai)
+{
+  itai->version = 1;
+  itai->tai_timestamp = 0;
+  itai->synchronization_state = false;
+  itai->timestamp_generation_failure = false;
+  itai->timestamp_is_modified = false;
+}
+
+heif_tai_timestamp_packet* heif_tai_timestamp_packet_alloc()
+{
+  auto* itai = new heif_tai_timestamp_packet;
+  initialize_heif_tai_timestamp_packet(itai);
+
+  return itai;
+}
+
+
+
+void heif_tai_timestamp_packet_copy(heif_tai_timestamp_packet* dst, const heif_tai_timestamp_packet* src)
+{
+  if (dst->version >= 1 && src->version >= 1) {
+    dst->tai_timestamp = src->tai_timestamp;
+    dst->synchronization_state = src->synchronization_state;
+    dst->timestamp_is_modified = src->timestamp_is_modified;
+    dst->timestamp_generation_failure = src->timestamp_generation_failure;
+  }
+
+  // in the future when copying with "src->version > dst->version",
+  // the remaining dst fields have to be filled with defaults
+}
+
+void heif_tai_clock_info_copy(heif_tai_clock_info* dst, const heif_tai_clock_info* src)
+{
+  if (dst->version >= 1 && src->version >= 1) {
+    dst->time_uncertainty = src->time_uncertainty;
+    dst->clock_resolution = src->clock_resolution;
+    dst->clock_drift_rate = src->clock_drift_rate;
+    dst->clock_type = src->clock_type;
+  }
+
+  // in the future when copying with "src->version > dst->version",
+  // the remaining dst fields have to be filled with defaults
+}
+
+
+struct heif_error heif_item_set_property_tai_clock_info(struct heif_context* ctx,
+                                                        heif_item_id itemId,
+                                                        const heif_tai_clock_info* clock,
+                                                        heif_property_id* out_propertyId)
+{
+  if (!ctx || !clock) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
+  }
+
+  // Check if itemId exists
+  auto file = ctx->context->get_heif_file();
+  if (!file->item_exists(itemId)) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "itemId does not exist"};
+  }
+
+  // make sure that we do not add two taic boxes to one image
+
+  if (auto img= ctx->context->get_image(itemId, false)) {
+    auto existing_taic = img->get_property<Box_taic>();
+    if (existing_taic) {
+      return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "item already has an taic property"};
+    }
+  }
+
+  // Create new taic (it will be deduplicated automatically in add_property())
+
+  auto taic = std::make_shared<Box_taic>();
+  taic->set_from_tai_clock_info(clock);
+
+  heif_property_id id = ctx->context->add_property(itemId, taic, false);
+
+  if (out_propertyId) {
+    *out_propertyId = id;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_item_get_property_tai_clock_info(const struct heif_context* ctx,
+                                                        heif_item_id itemId,
+                                                        heif_tai_clock_info** out_clock)
+{
+  if (!ctx) {
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL heif_context passed in"};
+  }
+  else if (!out_clock) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_clock_info passed in"};
+  }
+
+  *out_clock = nullptr;
+
+  // Check if itemId exists
+  auto file = ctx->context->get_heif_file();
+  if (!file->item_exists(itemId)) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item ID does not exist"};
+  }
+
+  // Check if taic exists for itemId
+  auto taic = file->get_property_for_item<Box_taic>(itemId);
+  if (!taic) {
+    // return NULL heif_tai_clock_info
+    return heif_error_success;
+  }
+
+  *out_clock = new heif_tai_clock_info;
+  **out_clock = *taic->get_tai_clock_info();
+
+  return heif_error_success;
+}
+
+
+void heif_tai_clock_info_release(struct heif_tai_clock_info* clock_info)
+{
+  delete clock_info;
+}
+
+
+void heif_tai_timestamp_packet_release(struct heif_tai_timestamp_packet* tai)
+{
+  delete tai;
+}
+
+
+struct heif_error heif_item_set_property_tai_timestamp(struct heif_context* ctx,
+                                                       heif_item_id itemId,
+                                                       const heif_tai_timestamp_packet* timestamp,
+                                                       heif_property_id* out_propertyId)
+{
+  if (!ctx) {
+    return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"};
+  }
+
+  // Check if itemId exists
+  auto file = ctx->context->get_heif_file();
+  if (!file->item_exists(itemId)) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item does not exist"};
+  }
+
+  // make sure that we do not add two TAI timestamps to one image
+
+  if (auto img= ctx->context->get_image(itemId, false)) {
+    auto existing_itai = img->get_property<Box_itai>();
+    if (existing_itai) {
+      return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "item already has an itai property"};
+    }
+  }
+
+  // Create new itai (it will be deduplicated automatically in add_property())
+
+  auto itai = std::make_shared<Box_itai>();
+  itai->set_from_tai_timestamp_packet(timestamp);
+
+  heif_property_id id = ctx->context->add_property(itemId, itai, false);
+
+  if (out_propertyId) {
+    *out_propertyId = id;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_item_get_property_tai_timestamp(const struct heif_context* ctx,
+                                                       heif_item_id itemId,
+                                                       struct heif_tai_timestamp_packet** out_timestamp)
+{
+  if (!ctx) {
+    return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"};
+  }
+  else if (!out_timestamp) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_timestamp_packet passed in"};
+  }
+
+  *out_timestamp = nullptr;
+
+  // Check if itemId exists
+  auto file = ctx->context->get_heif_file();
+  if (!file->item_exists(itemId)) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "item does not exist"};
+  }
+
+  // Check if itai exists for itemId
+  auto itai = file->get_property_for_item<Box_itai>(itemId);
+  if (!itai) {
+    // return NULL heif_tai_timestamp_packet;
+    return heif_error_success;
+  }
+
+  *out_timestamp = new heif_tai_timestamp_packet;
+  **out_timestamp = *itai->get_tai_timestamp_packet();
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_image_set_tai_timestamp(struct heif_image* img,
+                                               const struct heif_tai_timestamp_packet* timestamp)
+{
+  Error err = img->image->set_tai_timestamp(timestamp);
+  if (err) {
+    return err.error_struct(img->image.get());
+  }
+  else {
+    return heif_error_success;
+  }
+}
+
+
+struct heif_error heif_image_get_tai_timestamp(const struct heif_image* img,
+                                               struct heif_tai_timestamp_packet** out_timestamp)
+{
+  if (!out_timestamp) {
+    return {heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value, "NULL heif_tai_timestamp_packet passed in"};
+  }
+
+  *out_timestamp = nullptr;
+
+  auto* tai = img->image->get_tai_timestamp();
+  if (!tai) {
+    *out_timestamp = nullptr;
+    return heif_error_success;
+  }
+
+  *out_timestamp = new heif_tai_timestamp_packet;
+  **out_timestamp = *tai;
+
+  return heif_error_success;
+}
+
diff -pruN 1.19.8-1/libheif/api/libheif/heif_tai_timestamps.h 1.20.1-1/libheif/api/libheif/heif_tai_timestamps.h
--- 1.19.8-1/libheif/api/libheif/heif_tai_timestamps.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_tai_timestamps.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,202 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_TAI_TIMESTAMPS_H
+#define LIBHEIF_HEIF_TAI_TIMESTAMPS_H
+
+#include <libheif/heif.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct heif_tai_clock_info
+{
+  uint8_t version;
+
+  // --- version 1
+
+  // Standard deviation for timestamp generation process.
+  // May be `heif_tai_clock_info_time_uncertainty_unknown` if unknown.
+  uint64_t time_uncertainty;
+
+  // Receptor clock resolution in nanoseconds.
+  uint32_t clock_resolution;
+
+  // Clock drift rate in picoseconds/second when synchronization is stopped.
+  // Maybe `heif_tai_clock_info_clock_drift_rate_unknown` if unknown.
+  int32_t clock_drift_rate;
+
+  // Whether clock is synchronized to an atomic source,
+  // see the clock_type defines below.
+  uint8_t clock_type;
+} heif_tai_clock_info;
+
+#define heif_tai_clock_info_time_uncertainty_unknown UINT64_C(0xFFFFFFFFFFFFFFFF)
+#define heif_tai_clock_info_clock_drift_rate_unknown INT32_C(0x7FFFFFFF)
+#define heif_tai_clock_info_clock_type_unknown 0
+#define heif_tai_clock_info_clock_type_not_synchronized_to_atomic_source 1
+#define heif_tai_clock_info_clock_type_synchronized_to_atomic_source 2
+
+
+/**
+ * Allocate a new heif_tai_clock_info object and initialize with default values.
+ */
+LIBHEIF_API
+heif_tai_clock_info* heif_tai_clock_info_alloc(void);
+
+/**
+ * Copies the source object into the destination object.
+ * Only the fields that are present in both objects are copied.
+ * The version property has to be set in both structs.
+ */
+LIBHEIF_API
+void heif_tai_clock_info_copy(heif_tai_clock_info* dst, const heif_tai_clock_info* src);
+
+LIBHEIF_API
+void heif_tai_clock_info_release(heif_tai_clock_info* clock_info);
+
+
+typedef struct heif_tai_timestamp_packet
+{
+  uint8_t version;
+
+  // --- version 1
+
+  // number of nanoseconds since TAI epoch (1958-01-01T00:00:00.0)
+  uint64_t tai_timestamp;
+
+  // whether the remote and receiptor clocks are in sync
+  uint8_t synchronization_state;         // bool
+
+  // whether the receptor clock failed to generate a timestamp
+  uint8_t timestamp_generation_failure;  // bool
+
+  // whether the original clock value has been modified
+  uint8_t timestamp_is_modified;         // bool
+} heif_tai_timestamp_packet;
+
+/**
+ * Allocate a new heif_tai_timestamp_packet object and initialize with default values.
+ */
+LIBHEIF_API
+heif_tai_timestamp_packet* heif_tai_timestamp_packet_alloc(void);
+
+/**
+ * Copies the source object into the destination object.
+ * Only the fields that are present in both objects are copied.
+ * The version property has to be set in both structs.
+ */
+LIBHEIF_API
+void heif_tai_timestamp_packet_copy(heif_tai_timestamp_packet* dst, const heif_tai_timestamp_packet* src);
+
+LIBHEIF_API
+void heif_tai_timestamp_packet_release(heif_tai_timestamp_packet*);
+
+
+
+/**
+ * Creates a new clock info property if it doesn't exist yet.
+ * You can only add one tai_clock_info to an image.
+ *
+ * @param clock_info The TAI clock info to set for the item. This object will be copied.
+ * @param out_optional_propertyId Output parameter for the property ID of the tai_clock_info.
+ *                                This parameter may be NULL if the info is not required.
+ */
+LIBHEIF_API
+heif_error heif_item_set_property_tai_clock_info(heif_context* ctx,
+                                                 heif_item_id itemId,
+                                                 const heif_tai_clock_info* clock_info,
+                                                 heif_property_id* out_optional_propertyId);
+
+/**
+ * Get the heif_tai_clock_info attached to the item.
+ * This function allocates a new heif_tai_clock_info and returns it through out_clock.
+ *
+ * @param out_clock This parameter must not be nullptr. The object returned through this parameter must
+ *                  be released with heif_tai_clock_info_release().
+ *                  If no tai_clock_info property exists for the item, out_clock is set to NULL and
+ *                  no error is returned.
+ */
+LIBHEIF_API
+heif_error heif_item_get_property_tai_clock_info(const heif_context* ctx,
+                                                 heif_item_id itemId,
+                                                 heif_tai_clock_info** out_clock);
+
+
+/**
+ * Creates a new TAI timestamp property if it doesn't exist yet.
+ * You can only add one tai_timestamp to an image.
+ *
+ * @param timestamp The TAI timestamp to set for the item. This object will be copied.
+ * @param out_optional_propertyId Output parameter for the property ID of the TAI timestamp.
+ *                                This parameter may be NULL if the info is not required.
+ */
+LIBHEIF_API
+heif_error heif_item_set_property_tai_timestamp(heif_context* ctx,
+                                                heif_item_id itemId,
+                                                const heif_tai_timestamp_packet* timestamp,
+                                                heif_property_id* out_optional_propertyId);
+
+/**
+ * Get the heif_tai_timestamp_packet attached to the item.
+ * This function allocates a new heif_tai_timestamp_packet and returns it through out_timestamp.
+ *
+ * @param out_timestamp This parameter must not be NULL. The object returned through this parameter must
+ *                  be released with heif_tai_timestamp_packet_release().
+ *                  If no tai_timestamp_packet property exists for the item, *out_timestamp is set to NULL and
+ *                  no error is returned.
+ */
+LIBHEIF_API
+heif_error heif_item_get_property_tai_timestamp(const heif_context* ctx,
+                                                heif_item_id itemId,
+                                                heif_tai_timestamp_packet** out_timestamp);
+
+/**
+ * Attach a TAI timestamp to the image.
+ * The main use of this function is for image sequences, but it can also be used for still images.
+ * If used for still images, note that you also have to set the heif_tai_clock_info to the image item
+ * through heif_item_set_property_tai_clock_info().
+ *
+ * @param timestamp The TAI timestamp to set to the image. This object will be copied.
+ */
+LIBHEIF_API
+heif_error heif_image_set_tai_timestamp(heif_image* img,
+                                        const heif_tai_timestamp_packet* timestamp);
+
+/**
+ * Get the heif_tai_timestamp_packet attached to the image.
+ * The main use of this function is for image sequences, but it can also be used for still images.
+ * This function allocates a new heif_tai_timestamp_packet and returns it through out_timestamp.
+ *
+ * @param out_timestamp This parameter must not be NULL. The object returned through this parameter must
+ *                  be released with heif_tai_timestamp_packet_release().
+ *                  If no tai_timestamp_packet property exists for the image, *out_timestamp is set to NULL and
+ *                  no error is returned.
+ */
+LIBHEIF_API
+heif_error heif_image_get_tai_timestamp(const heif_image* img,
+                                        heif_tai_timestamp_packet** out_timestamp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //LIBHEIF_HEIF_TAI_TIMESTAMPS_H
diff -pruN 1.19.8-1/libheif/api/libheif/heif_tiling.cc 1.20.1-1/libheif/api/libheif/heif_tiling.cc
--- 1.19.8-1/libheif/api/libheif/heif_tiling.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_tiling.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,273 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_tiling.h"
+#include "api_structs.h"
+#include "image-items/grid.h"
+#include "image-items/tiled.h"
+
+#if WITH_UNCOMPRESSED_CODEC
+#include "image-items/unc_image.h"
+#endif
+
+#include <memory>
+#include <utility>
+#include <vector>
+#include <array>
+
+
+static struct heif_error error_null_parameter = {heif_error_Usage_error,
+                                                 heif_suberror_Null_pointer_argument,
+                                                 "NULL passed"};
+
+
+
+heif_error heif_image_handle_get_image_tiling(const struct heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* tiling)
+{
+  if (!handle || !tiling) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "NULL passed to heif_image_handle_get_image_tiling()"};
+  }
+
+  *tiling = handle->image->get_heif_image_tiling();
+
+  if (process_image_transformations) {
+    Error error = handle->image->process_image_transformations_on_tiling(*tiling);
+    if (error) {
+      return error.error_struct(handle->context.get());
+    }
+  }
+
+  return heif_error_ok;
+}
+
+
+struct heif_error heif_image_handle_get_grid_image_tile_id(const struct heif_image_handle* handle,
+                                                           int process_image_transformations,
+                                                           uint32_t tile_x, uint32_t tile_y,
+                                                           heif_item_id* tile_item_id)
+{
+  if (!handle || !tile_item_id) {
+    return { heif_error_Usage_error,
+             heif_suberror_Null_pointer_argument };
+  }
+
+  std::shared_ptr<ImageItem_Grid> gridItem = std::dynamic_pointer_cast<ImageItem_Grid>(handle->image);
+  if (!gridItem) {
+    return { heif_error_Usage_error,
+             heif_suberror_Unspecified,
+             "Image is no grid image" };
+  }
+
+  const ImageGrid& gridspec = gridItem->get_grid_spec();
+  if (tile_x >= gridspec.get_columns() || tile_y >= gridspec.get_rows()) {
+    return { heif_error_Usage_error,
+             heif_suberror_Unspecified,
+             "Grid tile index out of range" };
+  }
+
+  if (process_image_transformations) {
+    gridItem->transform_requested_tile_position_to_original_tile_position(tile_x, tile_y);
+  }
+
+  *tile_item_id = gridItem->get_grid_tiles()[tile_y * gridspec.get_columns() + tile_x];
+
+  return heif_error_ok;
+}
+
+
+struct heif_error heif_image_handle_decode_image_tile(const struct heif_image_handle* in_handle,
+                                                      struct heif_image** out_img,
+                                                      enum heif_colorspace colorspace,
+                                                      enum heif_chroma chroma,
+                                                      const struct heif_decoding_options* input_options,
+                                                      uint32_t x0, uint32_t y0)
+{
+  if (!in_handle) {
+    return error_null_parameter;
+  }
+
+  heif_item_id id = in_handle->image->get_id();
+
+  heif_decoding_options* dec_options = heif_decoding_options_alloc();
+  heif_decoding_options_copy(dec_options, input_options);
+
+  Result<std::shared_ptr<HeifPixelImage>> decodingResult = in_handle->context->decode_image(id,
+                                                                                            colorspace,
+                                                                                            chroma,
+                                                                                            *dec_options,
+                                                                                            true, x0,y0);
+  heif_decoding_options_free(dec_options);
+
+  if (decodingResult.error.error_code != heif_error_Ok) {
+    return decodingResult.error.error_struct(in_handle->image.get());
+  }
+
+  std::shared_ptr<HeifPixelImage> img = decodingResult.value;
+
+  *out_img = new heif_image();
+  (*out_img)->image = std::move(img);
+
+  return Error::Ok.error_struct(in_handle->image.get());
+}
+
+
+// --- encoding ---
+
+struct heif_error heif_context_encode_grid(struct heif_context* ctx,
+                                           struct heif_image** tiles,
+                                           uint16_t columns,
+                                           uint16_t rows,
+                                           struct heif_encoder* encoder,
+                                           const struct heif_encoding_options* input_options,
+                                           struct heif_image_handle** out_image_handle)
+{
+  if (!encoder || !tiles) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Null_pointer_argument).error_struct(ctx->context.get());
+  }
+  else if (rows == 0 || columns == 0) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
+  }
+
+  // TODO: Don't repeat this code from heif_context_encode_image()
+  heif_encoding_options* options = heif_encoding_options_alloc();
+
+  heif_color_profile_nclx nclx;
+  if (input_options) {
+    heif_encoding_options_copy(options, input_options);
+
+    if (options->output_nclx_profile == nullptr) {
+      auto input_nclx = tiles[0]->image->get_color_profile_nclx();
+      if (input_nclx) {
+        options->output_nclx_profile = &nclx;
+        nclx.version = 1;
+        nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries();
+        nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics();
+        nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients();
+        nclx.full_range_flag = input_nclx->get_full_range_flag();
+      }
+    }
+  }
+
+  // Convert heif_images to a vector of HeifPixelImages
+  std::vector<std::shared_ptr<HeifPixelImage>> pixel_tiles;
+  for (int i=0; i<rows*columns; i++) {
+    pixel_tiles.push_back(tiles[i]->image);
+  }
+
+  // Encode Grid
+  std::shared_ptr<ImageItem> out_grid;
+  auto addGridResult = ImageItem_Grid::add_and_encode_full_grid(ctx->context.get(),
+                                                                pixel_tiles,
+                                                                rows, columns,
+                                                                encoder,
+                                                                *options);
+  heif_encoding_options_free(options);
+
+  if (addGridResult.error) {
+    return addGridResult.error.error_struct(ctx->context.get());
+  }
+
+  out_grid = addGridResult.value;
+
+  // Mark as primary image
+  if (ctx->context->is_primary_image_set() == false) {
+    ctx->context->set_primary_image(out_grid);
+  }
+
+  if (out_image_handle) {
+    *out_image_handle = new heif_image_handle;
+    (*out_image_handle)->image = std::move(out_grid);
+    (*out_image_handle)->context = ctx->context;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_context_add_grid_image(struct heif_context* ctx,
+                                              uint32_t image_width,
+                                              uint32_t image_height,
+                                              uint32_t tile_columns,
+                                              uint32_t tile_rows,
+                                              const struct heif_encoding_options* encoding_options,
+                                              struct heif_image_handle** out_grid_image_handle)
+{
+  if (tile_rows == 0 || tile_columns == 0) {
+    return Error(heif_error_Usage_error,
+                 heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get());
+  }
+  else if (tile_rows > 0xFFFF || tile_columns > 0xFFFF) {
+    return heif_error{heif_error_Usage_error,
+                      heif_suberror_Invalid_image_size,
+                      "Number of tile rows/columns may not exceed 65535"};
+  }
+
+  auto generateGridItemResult = ImageItem_Grid::add_new_grid_item(ctx->context.get(),
+                                                                  image_width,
+                                                                  image_height,
+                                                                  static_cast<uint16_t>(tile_rows),
+                                                                  static_cast<uint16_t>(tile_columns),
+                                                                  encoding_options);
+  if (generateGridItemResult.error) {
+    return generateGridItemResult.error.error_struct(ctx->context.get());
+  }
+
+  if (out_grid_image_handle) {
+    *out_grid_image_handle = new heif_image_handle;
+    (*out_grid_image_handle)->image = generateGridItemResult.value;
+    (*out_grid_image_handle)->context = ctx->context;
+  }
+
+  return heif_error_success;
+}
+
+
+struct heif_error heif_context_add_image_tile(struct heif_context* ctx,
+                                              struct heif_image_handle* tiled_image,
+                                              uint32_t tile_x, uint32_t tile_y,
+                                              const struct heif_image* image,
+                                              struct heif_encoder* encoder)
+{
+  if (auto tili_image = std::dynamic_pointer_cast<ImageItem_Tiled>(tiled_image->image)) {
+    Error err = tili_image->add_image_tile(tile_x, tile_y, image->image, encoder);
+    return err.error_struct(ctx->context.get());
+  }
+#if WITH_UNCOMPRESSED_CODEC
+  else if (auto unci = std::dynamic_pointer_cast<ImageItem_uncompressed>(tiled_image->image)) {
+    Error err = unci->add_image_tile(tile_x, tile_y, image->image);
+    return err.error_struct(ctx->context.get());
+  }
+#endif
+  else if (auto grid_item = std::dynamic_pointer_cast<ImageItem_Grid>(tiled_image->image)) {
+    Error err = grid_item->add_image_tile(tile_x, tile_y, image->image, encoder);
+    return err.error_struct(ctx->context.get());
+  }
+  else {
+    return {
+        heif_error_Usage_error,
+        heif_suberror_Unspecified,
+        "Cannot add tile to a non-tiled image"
+    };
+  }
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_tiling.h 1.20.1-1/libheif/api/libheif/heif_tiling.h
--- 1.19.8-1/libheif/api/libheif/heif_tiling.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_tiling.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,137 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2017-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_TILING_H
+#define LIBHEIF_HEIF_TILING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libheif/heif_library.h>
+#include <libheif/heif_error.h>
+#include <libheif/heif_image.h>
+
+// forward declaration from other headers
+typedef struct heif_encoder heif_encoder;
+typedef struct heif_encoding_options heif_encoding_options;
+
+
+typedef struct heif_image_tiling
+{
+  int version;
+
+  // --- version 1
+
+  uint32_t num_columns;
+  uint32_t num_rows;
+  uint32_t tile_width;
+  uint32_t tile_height;
+
+  uint32_t image_width;
+  uint32_t image_height;
+
+  // Position of the top left tile.
+  // Usually, this is (0;0), but if a tiled image is rotated or cropped, it may be that the top left tile should be placed at a negative position.
+  // The offsets define this negative shift.
+  uint32_t top_offset;
+  uint32_t left_offset;
+
+  uint8_t number_of_extra_dimensions;  // 0 for normal images, 1 for volumetric (3D), ...
+  uint32_t extra_dimension_size[8];    // size of extra dimensions (first 8 dimensions)
+} heif_image_tiling;
+
+
+// --- decoding ---
+
+// If 'process_image_transformations' is true, this returns modified sizes.
+// If it is false, the top_offset and left_offset will always be (0;0).
+LIBHEIF_API
+heif_error heif_image_handle_get_image_tiling(const heif_image_handle* handle, int process_image_transformations, struct heif_image_tiling* out_tiling);
+
+
+// For grid images, return the image item ID of a specific grid tile.
+// If 'process_image_transformations' is true, the tile positions are given in the transformed image coordinate system and
+// are internally mapped to the original image tile positions.
+LIBHEIF_API
+heif_error heif_image_handle_get_grid_image_tile_id(const heif_image_handle* handle,
+                                                    int process_image_transformations,
+                                                    uint32_t tile_x, uint32_t tile_y,
+                                                    heif_item_id* out_tile_item_id);
+
+
+typedef struct heif_decoding_options heif_decoding_options;
+
+// The tile position is given in tile indices, not in pixel coordinates.
+// If the image transformations are processed (option->ignore_image_transformations==false), the tile position
+// is given in the transformed coordinates.
+LIBHEIF_API
+heif_error heif_image_handle_decode_image_tile(const heif_image_handle* in_handle,
+                                               heif_image** out_img,
+                                               enum heif_colorspace colorspace,
+                                               enum heif_chroma chroma,
+                                               const heif_decoding_options* options,
+                                               uint32_t tile_x, uint32_t tile_y);
+
+
+// --- encoding ---
+
+/**
+ * @brief Encodes an array of images into a grid.
+ *
+ * @param ctx The file context
+ * @param tiles User allocated array of images that will form the grid.
+ * @param rows The number of rows in the grid.
+ * @param columns The number of columns in the grid.
+ * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format()
+ * @param input_options Optional, may be nullptr.
+ * @param out_image_handle Returns a handle to the grid. The caller is responsible for freeing it.
+ * @return Returns an error if ctx, tiles, or encoder is nullptr. If rows or columns is 0.
+ */
+LIBHEIF_API
+heif_error heif_context_encode_grid(heif_context* ctx,
+                                    heif_image** tiles,
+                                    uint16_t rows,
+                                    uint16_t columns,
+                                    heif_encoder* encoder,
+                                    const heif_encoding_options* input_options,
+                                    heif_image_handle** out_image_handle);
+
+LIBHEIF_API
+heif_error heif_context_add_grid_image(heif_context* ctx,
+                                       uint32_t image_width,
+                                       uint32_t image_height,
+                                       uint32_t tile_columns,
+                                       uint32_t tile_rows,
+                                       const heif_encoding_options* encoding_options,
+                                       heif_image_handle** out_grid_image_handle);
+
+LIBHEIF_API
+heif_error heif_context_add_image_tile(heif_context* ctx,
+                                       heif_image_handle* tiled_image,
+                                       uint32_t tile_x, uint32_t tile_y,
+                                       const heif_image* image,
+                                       heif_encoder* encoder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/api/libheif/heif_uncompressed.cc 1.20.1-1/libheif/api/libheif/heif_uncompressed.cc
--- 1.19.8-1/libheif/api/libheif/heif_uncompressed.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_uncompressed.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,116 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "heif_uncompressed.h"
+#include "context.h"
+#include "api_structs.h"
+#include "image-items/unc_image.h"
+
+#include <array>
+#include <memory>
+#include <algorithm>
+
+
+struct heif_unci_image_parameters* heif_unci_image_parameters_alloc()
+{
+  auto* params = new heif_unci_image_parameters();
+
+  params->version = 1;
+
+  // --- version 1
+
+  params->image_width = 0;
+  params->image_height = 0;
+
+  // TODO: should we define that tile size = 0 means no tiling?
+  params->tile_width = 0;
+  params->tile_height = 0;
+
+  params->compression = heif_unci_compression_off;
+
+  return params;
+}
+
+
+void heif_unci_image_parameters_copy(struct heif_unci_image_parameters* dst,
+                                     const struct heif_unci_image_parameters* src)
+{
+  if (src == nullptr || dst == nullptr) {
+    return;
+  }
+
+  int min_version = std::min(src->version, dst->version);
+
+  switch (min_version) {
+    case 1:
+      dst->image_width = src->image_width;
+      dst->image_height = src->image_height;
+      dst->tile_width = src->tile_width;
+      dst->tile_height = src->tile_height;
+      dst->compression = src->compression;
+      break;
+  }
+}
+
+
+void heif_unci_image_parameters_release(struct heif_unci_image_parameters* params)
+{
+  delete params;
+}
+
+
+struct heif_error heif_context_add_empty_unci_image(struct heif_context* ctx,
+                                                    const struct heif_unci_image_parameters* parameters,
+                                                    const struct heif_encoding_options* encoding_options,
+                                                    const heif_image* prototype,
+                                                    struct heif_image_handle** out_unci_image_handle)
+{
+#if WITH_UNCOMPRESSED_CODEC
+  if (prototype == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "prototype image is NULL"};
+  }
+
+  if (out_unci_image_handle == nullptr) {
+    return {heif_error_Usage_error,
+            heif_suberror_Null_pointer_argument,
+            "out_unci_image_handle image is NULL"};
+  }
+
+  Result<std::shared_ptr<ImageItem_uncompressed>> unciImageResult;
+  unciImageResult = ImageItem_uncompressed::add_unci_item(ctx->context.get(), parameters, encoding_options, prototype->image);
+
+  if (unciImageResult.error != Error::Ok) {
+    return unciImageResult.error.error_struct(ctx->context.get());
+  }
+
+  assert(out_unci_image_handle);
+  *out_unci_image_handle = new heif_image_handle;
+  (*out_unci_image_handle)->image = unciImageResult.value;
+  (*out_unci_image_handle)->context = ctx->context;
+
+  return heif_error_success;
+#else
+  return {heif_error_Unsupported_feature,
+          heif_suberror_Unspecified,
+          "support for uncompressed images (ISO23001-17) has been disabled."};
+#endif
+}
diff -pruN 1.19.8-1/libheif/api/libheif/heif_uncompressed.h 1.20.1-1/libheif/api/libheif/heif_uncompressed.h
--- 1.19.8-1/libheif/api/libheif/heif_uncompressed.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/api/libheif/heif_uncompressed.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,109 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024-2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_UNCOMPRESSED_H
+#define LIBHEIF_HEIF_UNCOMPRESSED_H
+
+#include "libheif/heif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* @file heif_uncompressed.h
+ * @brief Functions for adding ISO 23001-17 (uncompressed) images to a HEIF file.
+ *        Despite its name, this is not limited to uncompressed images.
+ *        It is also possible to add images with lossless compression methods.
+ *        See heif_metadata_compression for more information.
+ */
+
+// --- 'unci' images
+
+// This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
+// contain the allowed values.
+enum heif_unci_compression
+{
+  heif_unci_compression_off = 0,
+  //heif_unci_compression_auto = 1,
+  //heif_unci_compression_unknown = 2, // only used when reading unknown method from input file
+  heif_unci_compression_deflate = 3,
+  heif_unci_compression_zlib = 4,
+  heif_unci_compression_brotli = 5
+};
+
+
+typedef struct heif_unci_image_parameters
+{
+  int version;
+
+  // --- version 1
+
+  uint32_t image_width;
+  uint32_t image_height;
+
+  uint32_t tile_width;
+  uint32_t tile_height;
+
+  enum heif_unci_compression compression;
+
+  // TODO: interleave type, padding
+} heif_unci_image_parameters;
+
+LIBHEIF_API
+heif_unci_image_parameters* heif_unci_image_parameters_alloc(void);
+
+LIBHEIF_API
+void heif_unci_image_parameters_copy(heif_unci_image_parameters* dst,
+                                     const heif_unci_image_parameters* src);
+
+LIBHEIF_API
+void heif_unci_image_parameters_release(heif_unci_image_parameters*);
+
+
+/*
+ * This adds an empty iso23001-17 (uncompressed) image to the HEIF file.
+ * The actual image data is added later using heif_context_add_image_tile().
+ * If you do not need tiling, you can use heif_context_encode_image() instead.
+ * However, this will by default disable any compression and any control about
+ * the data layout.
+ *
+ * @param ctx The file context
+ * @param parameters The parameters for the image, must not be NULL.
+ * @param encoding_options Optional, may be NULL.
+ * @param prototype An image with the same channel configuration as the image data
+ *                  that will be later inserted. The image size need not match this.
+ *                  Must not be NULL.
+ * @param out_unci_image_handle Returns a handle to the image. The caller is responsible for freeing it.
+ *                  Must not be NULL because this is required to fill in image data.
+ * @return Returns an error if the passed parameters are incorrect.
+ *         If ISO23001-17 images are not supported, returns heif_error_Unsupported_feature.
+ */
+LIBHEIF_API
+heif_error heif_context_add_empty_unci_image(heif_context* ctx,
+                                             const heif_unci_image_parameters* parameters,
+                                             const heif_encoding_options* encoding_options,
+                                             const heif_image* prototype,
+                                             heif_image_handle** out_unci_image_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -pruN 1.19.8-1/libheif/bitstream.cc 1.20.1-1/libheif/bitstream.cc
--- 1.19.8-1/libheif/bitstream.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/bitstream.cc	2025-07-02 13:05:31.000000000 +0000
@@ -360,7 +360,7 @@ float BitstreamRange::read_float32()
   // TODO: I am not sure this works everywhere as there seem to be systems where
   //       the float byte order is different from the integer endianness
   //       https://en.wikipedia.org/wiki/Endianness#Floating_point
-  int i = read32();
+  uint32_t i = read32();
   float f;
   memcpy(&f, &i, sizeof(float));
   return f;
@@ -395,12 +395,13 @@ std::string BitstreamRange::read_string(
     return std::string();
   }
 
+  auto istr = get_istream();
+
   for (;;) {
     if (!prepare_read(1)) {
       return std::string();
     }
 
-    auto istr = get_istream();
     char c;
     bool success = istr->read(&c, 1);
 
@@ -421,6 +422,40 @@ std::string BitstreamRange::read_string(
 }
 
 
+std::string BitstreamRange::read_fixed_string(int len)
+{
+  std::string str;
+
+  if (!prepare_read(len)) {
+    return std::string();
+  }
+
+  auto istr = get_istream();
+
+  uint8_t n;
+  bool success = istr->read(&n, 1);
+  if (!success || n > len - 1) {
+    return {};
+  }
+
+  for (int i = 0; i < n; i++) {
+    char c;
+    success = istr->read(&c, 1);
+
+    if (!success) {
+      set_eof_while_reading();
+      return std::string();
+    }
+
+    str += (char) c;
+  }
+
+  istr->seek_cur(len-n-1);
+
+  return str;
+}
+
+
 bool BitstreamRange::read(uint8_t* data, size_t n)
 {
   if (!prepare_read(n)) {
@@ -846,6 +881,28 @@ void StreamWriter::write(const std::stri
 }
 
 
+void StreamWriter::write_fixed_string(std::string s, size_t len)
+{
+  size_t required_size = m_position + len;
+
+  if (required_size > m_data.size()) {
+    m_data.resize(required_size);
+  }
+
+  size_t n_chars = std::min(s.length(), len - 1);
+  assert(n_chars <= 255);
+  m_data[m_position++] = static_cast<uint8_t>(n_chars);
+
+  for (size_t i = 0; i < s.size() && i < len - 1; i++) {
+    m_data[m_position++] = s[i];
+  }
+
+  for (size_t i = s.size(); i < len - 1; i++) {
+    m_data[m_position++] = 0;
+  }
+}
+
+
 void StreamWriter::write(const std::vector<uint8_t>& vec)
 {
   size_t required_size = m_position + vec.size();
diff -pruN 1.19.8-1/libheif/bitstream.h 1.20.1-1/libheif/bitstream.h
--- 1.19.8-1/libheif/bitstream.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/bitstream.h	2025-07-02 13:05:31.000000000 +0000
@@ -206,7 +206,32 @@ public:
       }
     }
     else {
-      return std::numeric_limits<uint64_t>::max();
+      auto result = m_func_table->wait_for_file_size(end_pos, m_userdata);
+      if (result == heif_reader_grow_status_size_reached) {
+        return end_pos;
+      }
+      else {
+        uint64_t pos = m_func_table->get_position(m_userdata);
+        return bisect_filesize(pos,end_pos);
+      }
+    }
+  }
+
+  uint64_t bisect_filesize(uint64_t mini, uint64_t maxi) {
+    // mini - <= filesize
+    // maxi - >  filesize
+
+    if (maxi == mini + 1) {
+      return mini;
+    }
+
+    uint64_t pos = (mini + maxi) / 2;
+    auto result = m_func_table->wait_for_file_size(pos, m_userdata);
+    if (result == heif_reader_grow_status_size_reached) {
+      return bisect_filesize(pos, maxi);
+    }
+    else {
+      return bisect_filesize(mini, pos);
     }
   }
 
@@ -278,6 +303,10 @@ public:
 
   std::string read_string();
 
+  // A string stored with a fixed number of bytes. The first byte contains the string length and the extra bytes
+  // are filled with a padding 0.
+  std::string read_fixed_string(int len);
+
   bool read(uint8_t* data, size_t n);
 
   bool prepare_read(size_t nBytes);
@@ -461,6 +490,8 @@ public:
 
   void write(const std::string&);
 
+  void write_fixed_string(std::string s, size_t len);
+
   void write(const std::vector<uint8_t>&);
 
   void write(const StreamWriter&);
diff -pruN 1.19.8-1/libheif/box.cc 1.20.1-1/libheif/box.cc
--- 1.19.8-1/libheif/box.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/box.cc	2025-07-02 13:05:31.000000000 +0000
@@ -31,6 +31,7 @@
 #include "codecs/avc_boxes.h"
 #include "codecs/avif_boxes.h"
 #include "image-items/tiled.h"
+#include "sequences/seq_boxes.h"
 
 #include <iomanip>
 #include <utility>
@@ -390,7 +391,15 @@ Error Box::write_header(StreamWriter& wr
 std::string BoxHeader::dump(Indent& indent) const
 {
   std::ostringstream sstr;
-  sstr << indent << "Box: " << get_type_string() << " -----\n";
+  sstr << indent << "Box: " << get_type_string();
+  const char* debug_name = debug_box_name();
+  if (debug_name) {
+    sstr << " ----- (" << debug_name << ")\n";
+  }
+  else {
+    sstr << " -----\n";
+  }
+
   sstr << indent << "size: " << get_box_size() << "   (header size: " << get_header_size() << ")\n";
 
   return sstr.str();
@@ -529,14 +538,26 @@ Error Box::read(BitstreamRange& range, s
       box = std::make_shared<Box_hvcC>();
       break;
 
+    case fourcc("hvc1"):
+      box = std::make_shared<Box_hvc1>();
+      break;
+
     case fourcc("av1C"):
       box = std::make_shared<Box_av1C>();
       break;
 
+    case fourcc("av01"):
+      box = std::make_shared<Box_av01>();
+      break;
+
     case fourcc("vvcC"):
       box = std::make_shared<Box_vvcC>();
       break;
 
+    case fourcc("vvc1"):
+      box = std::make_shared<Box_vvc1>();
+      break;
+
     case fourcc("idat"):
       box = std::make_shared<Box_idat>();
       break;
@@ -621,6 +642,10 @@ Error Box::read(BitstreamRange& range, s
       box = std::make_shared<Box_jpgC>();
       break;
 
+    case fourcc("mjpg"):
+      box = std::make_shared<Box_mjpg>();
+      break;
+
 #if WITH_UNCOMPRESSED_CODEC
     case fourcc("cmpd"):
       box = std::make_shared<Box_cmpd>();
@@ -641,10 +666,14 @@ Error Box::read(BitstreamRange& range, s
     case fourcc("cpat"):
       box = std::make_shared<Box_cpat>();
       break;
+
+    case fourcc("uncv"):
+      box = std::make_shared<Box_uncv>();
+      break;
 #endif
 
     // --- JPEG 2000
-      
+
     case fourcc("j2kH"):
       box = std::make_shared<Box_j2kH>();
       break;
@@ -665,15 +694,18 @@ Error Box::read(BitstreamRange& range, s
       box = std::make_shared<Box_j2kL>();
       break;
 
+    case fourcc("j2ki"):
+      box = std::make_shared<Box_j2ki>();
+      break;
+
 
     // --- mski
-      
+
     case fourcc("mskC"):
       box = std::make_shared<Box_mskC>();
       break;
 
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-      // --- TAI timestamps
+    // --- TAI timestamps
 
     case fourcc("itai"):
       box = std::make_shared<Box_itai>();
@@ -682,7 +714,6 @@ Error Box::read(BitstreamRange& range, s
     case fourcc("taic"):
       box = std::make_shared<Box_taic>();
       break;
-#endif
 
     // --- AVC (H.264)
 
@@ -690,6 +721,10 @@ Error Box::read(BitstreamRange& range, s
       box = std::make_shared<Box_avcC>();
       break;
 
+    case fourcc("avc1"):
+      box = std::make_shared<Box_avc1>();
+      break;
+
 #if HEIF_ENABLE_EXPERIMENTAL_FEATURES
     case fourcc("tilC"):
       box = std::make_shared<Box_tilC>();
@@ -719,6 +754,108 @@ Error Box::read(BitstreamRange& range, s
       }
       break;
 
+    // --- sequences
+
+    case fourcc("moov"):
+      box = std::make_shared<Box_moov>();
+      break;
+
+    case fourcc("mvhd"):
+      box = std::make_shared<Box_mvhd>();
+      break;
+
+    case fourcc("trak"):
+      box = std::make_shared<Box_trak>();
+      break;
+
+    case fourcc("tkhd"):
+      box = std::make_shared<Box_tkhd>();
+      break;
+
+    case fourcc("mdia"):
+      box = std::make_shared<Box_mdia>();
+      break;
+
+    case fourcc("mdhd"):
+      box = std::make_shared<Box_mdhd>();
+      break;
+
+    case fourcc("minf"):
+      box = std::make_shared<Box_minf>();
+      break;
+
+    case fourcc("vmhd"):
+      box = std::make_shared<Box_vmhd>();
+      break;
+
+    case fourcc("stbl"):
+      box = std::make_shared<Box_stbl>();
+      break;
+
+    case fourcc("stsd"):
+      box = std::make_shared<Box_stsd>();
+      break;
+
+    case fourcc("stts"):
+      box = std::make_shared<Box_stts>();
+      break;
+
+    case fourcc("stsc"):
+      box = std::make_shared<Box_stsc>();
+      break;
+
+    case fourcc("stco"):
+      box = std::make_shared<Box_stco>();
+      break;
+
+    case fourcc("stsz"):
+      box = std::make_shared<Box_stsz>();
+      break;
+
+    case fourcc("stss"):
+      box = std::make_shared<Box_stss>();
+      break;
+
+    case fourcc("ccst"):
+      box = std::make_shared<Box_ccst>();
+      break;
+
+    case fourcc("sbgp"):
+      box = std::make_shared<Box_sbgp>();
+      break;
+
+    case fourcc("sgpd"):
+      box = std::make_shared<Box_sgpd>();
+      break;
+
+    case fourcc("btrt"):
+      box = std::make_shared<Box_btrt>();
+      break;
+
+    case fourcc("saiz"):
+      box = std::make_shared<Box_saiz>();
+      break;
+
+    case fourcc("saio"):
+      box = std::make_shared<Box_saio>();
+      break;
+
+    case fourcc("urim"):
+      box = std::make_shared<Box_URIMetaSampleEntry>();
+      break;
+
+    case fourcc("uri "):
+      box = std::make_shared<Box_uri>();
+      break;
+
+    case fourcc("nmhd"):
+      box = std::make_shared<Box_nmhd>();
+      break;
+
+    case fourcc("tref"):
+      box = std::make_shared<Box_tref>();
+      break;
+
     default:
       box = std::make_shared<Box_other>(hdr.get_short_type());
       break;
@@ -988,6 +1125,16 @@ void Box::derive_box_version_recursive()
 }
 
 
+void Box::patch_file_pointers_recursively(StreamWriter& writer, size_t offset)
+{
+  patch_file_pointers(writer, offset);
+
+  for (auto& child : m_children) {
+    child->patch_file_pointers_recursively(writer, offset);
+  }
+}
+
+
 Error Box_other::parse(BitstreamRange& range, const heif_security_limits* limits)
 {
   if (has_fixed_box_size()) {
@@ -1053,8 +1200,8 @@ std::string Box_other::dump(Indent& inde
   }
 
   sstr << write_raw_data_as_hex(m_data.data(), len,
-                                "data: ",
-                                "      ");
+                                indent.get_string() + "data: ",
+                                indent.get_string() + "      ");
 
   return sstr.str();
 }
@@ -1996,6 +2143,8 @@ Error Box_iloc::write(StreamWriter& writ
   writer.skip(nSkip);
   prepend_header(writer, box_start);
 
+  patch_iloc_header(writer); // Write iloc box. If there is an mdat, it will later be overwritten.
+
   return Error::Ok;
 }
 
@@ -2580,7 +2729,7 @@ Error Box_clli::write(StreamWriter& writ
 Box_mdcv::Box_mdcv()
 {
   set_short_type(fourcc("mdcv"));
-  
+
   memset(&mdcv, 0, sizeof(heif_mastering_display_colour_volume));
 }
 
@@ -4642,30 +4791,44 @@ Error Box_cmex::write(StreamWriter& writ
 }
 
 
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
-std::string Box_taic::dump(Indent& indent) const {
+std::string Box_taic::dump(const heif_tai_clock_info& info, Indent& indent)
+{
   std::ostringstream sstr;
-  sstr << Box::dump(indent);
-  sstr << indent << "time_uncertainty: " << m_time_uncertainty << "\n";
-  sstr << indent << "clock_resolution: " << m_clock_resolution << "\n";
+  sstr << indent << "time_uncertainty: " << info.time_uncertainty << "\n";
+  sstr << indent << "clock_resolution: " << info.clock_resolution << "\n";
   sstr << indent << "clock_drift_rate: ";
-  if (heif_is_tai_clock_info_drift_rate_undefined(m_clock_drift_rate)) {
+  if (info.clock_drift_rate == heif_tai_clock_info_clock_drift_rate_unknown) {
     sstr << "undefined\n";
   }
   else {
-    sstr << m_clock_drift_rate << "\n";
+    sstr << info.clock_drift_rate << "\n";
   }
 
-  sstr << indent << "clock_type: " << static_cast<int>(m_clock_type) << "\n";
+  sstr << indent << "clock_type: " << int(info.clock_type) << " ";
+  switch (info.clock_type) {
+    case heif_tai_clock_info_clock_type_unknown: sstr << "(unknown)\n"; break;
+    case heif_tai_clock_info_clock_type_synchronized_to_atomic_source: sstr << "(synchronized to atomic source)\n"; break;
+    case heif_tai_clock_info_clock_type_not_synchronized_to_atomic_source: sstr << "(not synchronized to atomic source)\n"; break;
+    default: sstr << "(illegal value)\n"; break;;
+  }
+  return sstr.str();
+}
+
+
+std::string Box_taic::dump(Indent& indent) const {
+  std::ostringstream sstr;
+  sstr << Box::dump(indent);
+  sstr << dump(m_info, indent);
+
   return sstr.str();
 }
 
 Error Box_taic::write(StreamWriter& writer) const {
   size_t box_start = reserve_box_header_space(writer);
-  writer.write64(m_time_uncertainty);
-  writer.write32(m_clock_resolution);
-  writer.write32(m_clock_drift_rate);
-  writer.write8(m_clock_type);
+  writer.write64(m_info.time_uncertainty);
+  writer.write32(m_info.clock_resolution);
+  writer.write32(m_info.clock_drift_rate);
+  writer.write8(m_info.clock_type);
 
   prepend_header(writer, box_start);
 
@@ -4675,34 +4838,127 @@ Error Box_taic::write(StreamWriter& writ
 Error Box_taic::parse(BitstreamRange& range, const heif_security_limits*) {
   parse_full_box_header(range);
 
-  m_time_uncertainty = range.read64();
-  m_clock_resolution = range.read32();
+  m_info.time_uncertainty = range.read64();
+  m_info.clock_resolution = range.read32();
 
-  m_clock_drift_rate = range.read32s();
-  m_clock_type = range.read8();
+  m_info.clock_drift_rate = range.read32s();
+  m_info.clock_type = range.read8();
   return range.get_error();
 }
 
+
+bool operator==(const heif_tai_clock_info& a,
+                const heif_tai_clock_info& b)
+{
+  return a.version == b.version &&
+         a.time_uncertainty == b.time_uncertainty &&
+         a.clock_resolution == b.clock_resolution &&
+         a.clock_drift_rate == b.clock_drift_rate &&
+         a.clock_type == b.clock_type;
+}
+
+bool Box_taic::operator==(const Box& other) const
+{
+  const auto* other_taic = dynamic_cast<const Box_taic*>(&other);
+  if (other_taic == nullptr) {
+    return false;
+  }
+
+  return m_info == other_taic->m_info;
+}
+
+
 std::string Box_itai::dump(Indent& indent) const {
   std::ostringstream sstr;
   sstr << Box::dump(indent);
-  sstr << indent << "tai_timestamp: " << m_tai_timestamp << "\n";
-  sstr << indent << "synchronization_state: " << m_synchronization_state << "\n";
-  sstr << indent << "timestamp_generation_failure: " << m_timestamp_generation_failure << "\n";
-  sstr << indent << "timestamp_is_modified: " << m_timestamp_is_modified << "\n";
+  sstr << indent << "tai_timestamp: " << m_timestamp.tai_timestamp << "\n";
+  sstr << indent << "synchronization_state: " << int(m_timestamp.synchronization_state) << "\n";
+  sstr << indent << "timestamp_generation_failure: " << int(m_timestamp.timestamp_generation_failure) << "\n";
+  sstr << indent << "timestamp_is_modified: " << int(m_timestamp.timestamp_is_modified) << "\n";
   return sstr.str();
 }
 
-Error Box_itai::write(StreamWriter& writer) const {
-  size_t box_start = reserve_box_header_space(writer);
-  writer.write64(m_tai_timestamp);
+
+std::vector<uint8_t> Box_itai::encode_tai_to_bitstream(const heif_tai_timestamp_packet* tai)
+{
+  StreamWriter writer;
+  writer.write64(tai->tai_timestamp);
 
   uint8_t status_bits = 0;
-  status_bits |= m_synchronization_state ? (1 << 7) : 0;
-  status_bits |= m_timestamp_generation_failure ? (1 << 6) : 0;
-  status_bits |= m_timestamp_is_modified ? (1 << 5) : 0;
+  status_bits |= tai->synchronization_state ? (1 << 7) : 0;
+  status_bits |= tai->timestamp_generation_failure ? (1 << 6) : 0;
+  status_bits |= tai->timestamp_is_modified ? (1 << 5) : 0;
 
   writer.write8(status_bits);
+
+  return writer.get_data();
+}
+
+bool operator==(const heif_tai_timestamp_packet& a,
+                const heif_tai_timestamp_packet& b)
+{
+  return a.version == b.version &&
+         a.tai_timestamp == b.tai_timestamp &&
+         a.synchronization_state == b.synchronization_state &&
+         a.timestamp_generation_failure == b.timestamp_generation_failure &&
+         a.timestamp_is_modified == b.timestamp_is_modified;
+}
+
+bool Box_itai::operator==(const Box& other) const
+{
+  const auto* other_itai = dynamic_cast<const Box_itai*>(&other);
+  if (other_itai == nullptr) {
+    return false;
+  }
+
+  return m_timestamp == other_itai->m_timestamp;
+}
+
+
+
+uint64_t uint8_vector_to_uint64_BE(const uint8_t* data)
+{
+  uint64_t value = ((static_cast<uint64_t>(data[0]) << 56) |
+                    (static_cast<uint64_t>(data[1]) << 48) |
+                    (static_cast<uint64_t>(data[2]) << 40) |
+                    (static_cast<uint64_t>(data[3]) << 32) |
+                    (static_cast<uint64_t>(data[4]) << 24) |
+                    (static_cast<uint64_t>(data[5]) << 16) |
+                    (static_cast<uint64_t>(data[6]) << 8) |
+                    (static_cast<uint64_t>(data[7]) << 0));
+
+  return value;
+}
+
+
+Result<heif_tai_timestamp_packet> Box_itai::decode_tai_from_vector(const std::vector<uint8_t>& data)
+{
+  if (data.size() != 9) {
+    return Error{heif_error_Invalid_input,
+                 heif_suberror_Unspecified,
+                 "Wrong size of TAI timestamp data"};
+  }
+
+  uint8_t status_bits = data[8];
+
+  heif_tai_timestamp_packet tai;
+  tai.version = 1;
+  tai.tai_timestamp = uint8_vector_to_uint64_BE(data.data());
+  tai.synchronization_state = !!(status_bits & 0x80);
+  tai.timestamp_generation_failure = !!(status_bits & 0x40);
+  tai.timestamp_is_modified = !!(status_bits & 0x20);
+
+  return tai;
+}
+
+
+Error Box_itai::write(StreamWriter& writer) const {
+  size_t box_start = reserve_box_header_space(writer);
+
+  std::vector<uint8_t> tai_data = encode_tai_to_bitstream(&m_timestamp);
+
+  writer.write(tai_data);
+
   prepend_header(writer, box_start);
   return Error::Ok;
 }
@@ -4710,14 +4966,15 @@ Error Box_itai::write(StreamWriter& writ
 Error Box_itai::parse(BitstreamRange& range, const heif_security_limits*) {
   parse_full_box_header(range);
 
-  m_tai_timestamp = range.read64();
+  m_timestamp.version = 1;
+  m_timestamp.tai_timestamp = range.read64();
 
   uint8_t status_bits = range.read8();
 
-  m_synchronization_state = !!(status_bits & 0x80);
-  m_timestamp_generation_failure = !!(status_bits & 0x40);
-  m_timestamp_is_modified = !!(status_bits & 0x20);
+  m_timestamp.synchronization_state = !!(status_bits & 0x80);
+  m_timestamp.timestamp_generation_failure = !!(status_bits & 0x40);
+  m_timestamp.timestamp_is_modified = !!(status_bits & 0x20);
 
   return range.get_error();
 }
-#endif
+
diff -pruN 1.19.8-1/libheif/box.h 1.20.1-1/libheif/box.h
--- 1.19.8-1/libheif/box.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/box.h	2025-07-02 13:05:31.000000000 +0000
@@ -26,6 +26,7 @@
 #include "libheif/heif.h"
 #include "libheif/heif_experimental.h"
 #include "libheif/heif_properties.h"
+#include "libheif/heif_tai_timestamps.h"
 #include <cinttypes>
 #include <cstddef>
 
@@ -127,6 +128,8 @@ public:
 
   std::string get_type_string() const;
 
+  virtual const char* debug_box_name() const { return nullptr; }
+
   void set_short_type(uint32_t type) { m_type = type; }
 
 
@@ -193,6 +196,10 @@ public:
 
   void derive_box_version_recursive();
 
+  virtual void patch_file_pointers(StreamWriter&, size_t offset) {}
+
+  void patch_file_pointers_recursively(StreamWriter&, size_t offset);
+
   std::string dump(Indent&) const override;
 
   template<typename T> [[nodiscard]] std::shared_ptr<T> get_child_box() const
@@ -399,6 +406,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "File Type"; }
+
   bool has_compatible_brand(uint32_t brand) const;
 
   std::vector<uint32_t> list_brands() const { return m_compatible_brands; }
@@ -437,6 +446,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Free Space"; }
+
   Error write(StreamWriter& writer) const override;
 
 protected:
@@ -454,6 +465,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Metadata"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -469,6 +482,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Handler Reference"; }
+
   uint32_t get_handler_type() const { return m_handler_type; }
 
   void set_handler_type(uint32_t handler) { m_handler_type = handler; }
@@ -498,6 +513,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Primary Item"; }
+
   heif_item_id get_item_ID() const { return m_item_ID; }
 
   void set_item_ID(heif_item_id id) { m_item_ID = id; }
@@ -525,6 +542,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Location"; }
+
   struct Extent
   {
     uint64_t index = 0;
@@ -624,6 +643,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Info Entry"; }
+
   bool is_hidden_item() const { return m_hidden_item; }
 
   void set_hidden_item(bool hidden);
@@ -684,6 +705,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Information"; }
+
   void derive_box_version() override;
 
   Error write(StreamWriter& writer) const override;
@@ -706,6 +729,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Properties"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -735,6 +760,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Property Container"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -760,6 +787,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Image Spatial Extents"; }
+
   Error write(StreamWriter& writer) const override;
 
   bool operator==(const Box& other) const override;
@@ -785,6 +814,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Property Association"; }
+
   struct PropertyAssociation
   {
     bool essential;
@@ -838,6 +869,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Image Properties for Auxiliary Images"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 
@@ -863,6 +896,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Image Rotation"; }
+
   int get_rotation_ccw() const { return m_rotation; }
 
   // Only these multiples of 90 are allowed: 0, 90, 180, 270.
@@ -898,6 +933,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Image Mirroring"; }
+
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::ignorable; }
 
 protected:
@@ -924,6 +961,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Clean Aperture"; }
+
   int left_rounded(uint32_t image_width) const;  // first column
   int right_rounded(uint32_t image_width) const; // last column that is part of the cropped image
   int top_rounded(uint32_t image_height) const;   // first row
@@ -973,6 +1012,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Reference"; }
+
   bool has_references(heif_item_id itemID) const;
 
   std::vector<heif_item_id> get_references(heif_item_id itemID, uint32_t ref_type) const;
@@ -1002,6 +1043,8 @@ class Box_idat : public Box
 public:
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item Data"; }
+
   Error read_data(const std::shared_ptr<StreamReader>& istr,
                   uint64_t start, uint64_t length,
                   std::vector<uint8_t>& out_data,
@@ -1039,6 +1082,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Groups List"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -1079,6 +1124,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Stereo pair"; }
+
   heif_item_id get_left_image() const { return entity_ids[0]; }
   heif_item_id get_right_image() const { return entity_ids[1]; }
 
@@ -1098,6 +1145,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Image pyramid group"; }
+
   Error write(StreamWriter& writer) const override;
 
   struct LayerInfo {
@@ -1138,6 +1187,8 @@ class Box_dinf : public Box
 public:
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Data Information"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -1148,6 +1199,8 @@ class Box_dref : public FullBox
 public:
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Data Reference"; }
+
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 };
@@ -1158,6 +1211,8 @@ class Box_url : public FullBox
 public:
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Data Entry URL"; }
+
   bool is_same_file() const { return m_location.empty(); }
 
 protected:
@@ -1185,6 +1240,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Pixel Information"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1210,6 +1267,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Pixel Aspect Ratio"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1231,6 +1290,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Layer Selection"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1255,6 +1316,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Content Light Level Information"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1273,6 +1336,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Master Display Colour Volume"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1291,6 +1356,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Ambient Viewing Environment"; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1328,6 +1395,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  // TODO const char* debug_box_name() const override { return ""; }
+
   Error write(StreamWriter& writer) const override;
 
   [[nodiscard]] parse_error_fatality get_parse_error_fatality() const override { return parse_error_fatality::optional; }
@@ -1407,6 +1476,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Camera Intrinsic Matrix"; }
+
   RelativeIntrinsicMatrix get_intrinsic_matrix() const { return m_matrix; }
 
   void set_intrinsic_matrix(RelativeIntrinsicMatrix matrix);
@@ -1462,6 +1533,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Camera Extrinsic Matrix"; }
+
   ExtrinsicMatrix get_extrinsic_matrix() const { return m_matrix; }
 
   Error set_extrinsic_matrix(ExtrinsicMatrix matrix);
@@ -1514,6 +1587,8 @@ public:
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "User Description"; }
+
   Error write(StreamWriter& writer) const override;
 
   /**
@@ -1591,17 +1666,25 @@ private:
 };
 
 
-#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+void initialize_heif_tai_clock_info(heif_tai_clock_info* taic);
+void initialize_heif_tai_timestamp_packet(heif_tai_timestamp_packet* itai);
+
+
 class Box_taic : public FullBox
 {
 public:
   Box_taic()
   {
     set_short_type(fourcc("taic"));
+    initialize_heif_tai_clock_info(&m_info);
   }
 
+  static std::string dump(const heif_tai_clock_info& info, Indent&);
+
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "TAI Clock Information"; }
+
   Error write(StreamWriter& writer) const override;
 
   /**
@@ -1610,7 +1693,7 @@ public:
    * The standard deviation measurement uncertainty in nanoseconds
    * for the timestamp generation process. 
    */
-  void set_time_uncertainty(uint64_t time_uncertainty) { m_time_uncertainty = time_uncertainty;}
+  void set_time_uncertainty(uint64_t time_uncertainty) { m_info.time_uncertainty = time_uncertainty;}
   
   /**
    * clock_resolution.
@@ -1618,7 +1701,7 @@ public:
    * Specifies the resolution of the receptor clock in nanoseconds.
    * For example, a microsecond clock has a clock_resolution of 1000.
    */
-  void set_clock_resolution(uint32_t clock_resolution) { m_clock_resolution = clock_resolution; }
+  void set_clock_resolution(uint32_t clock_resolution) { m_info.clock_resolution = clock_resolution; }
   
   /**
    * clock_drift_rate.
@@ -1626,7 +1709,7 @@ public:
    * The difference between the synchronized and unsynchronized
    * time, over a period of one second. 
    */
-  void set_clock_drift_rate(int32_t clock_drift_rate) { m_clock_drift_rate = clock_drift_rate; }
+  void set_clock_drift_rate(int32_t clock_drift_rate) { m_info.clock_drift_rate = clock_drift_rate; }
   
   /**
    * clock_type.
@@ -1635,76 +1718,104 @@ public:
    * 1 = The clock does not synchronize to an atomic source of absolute TAI time
    * 2 = The clock can synchronize to an atomic source of absolute TAI time
    */
-  void set_clock_type(uint8_t clock_type) { m_clock_type = clock_type; }
+  void set_clock_type(uint8_t clock_type) { m_info.clock_type = clock_type; }
 
-  uint64_t get_time_uncertainty() const { return m_time_uncertainty; }
+  uint64_t get_time_uncertainty() const { return m_info.time_uncertainty; }
   
-  uint32_t get_clock_resolution() const { return m_clock_resolution; }
+  uint32_t get_clock_resolution() const { return m_info.clock_resolution; }
   
-  int32_t get_clock_drift_rate() const { return m_clock_drift_rate; }
+  int32_t get_clock_drift_rate() const { return m_info.clock_drift_rate; }
   
-  uint8_t get_clock_type() const { return m_clock_type; }
+  uint8_t get_clock_type() const { return m_info.clock_type; }
+
+  void set_from_tai_clock_info(const heif_tai_clock_info* info) {
+    heif_tai_clock_info_copy(&m_info, info);
+  }
+
+  const heif_tai_clock_info* get_tai_clock_info() const
+  {
+    return &m_info;
+  }
+
+  bool operator==(const Box& other) const override;
 
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 
 private:
-  uint64_t m_time_uncertainty = heif_tai_clock_info_unknown_time_uncertainty;
-  uint32_t m_clock_resolution = 0;
-  int32_t m_clock_drift_rate = heif_tai_clock_info_unknown_drift_rate;
-  uint8_t m_clock_type = 0;
+  heif_tai_clock_info m_info;
 };
 
+bool operator==(const heif_tai_clock_info& a,
+                const heif_tai_clock_info& b);
 
-class Box_itai : public FullBox 
+
+class Box_itai : public FullBox
 {
 public:
   Box_itai()
   {
     set_short_type(fourcc("itai"));
+    initialize_heif_tai_timestamp_packet(&m_timestamp);
   }
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "Item TAI Timestamp"; }
+
   Error write(StreamWriter& writer) const override;
 
+  static std::vector<uint8_t> encode_tai_to_bitstream(const heif_tai_timestamp_packet*);
+
+  static Result<heif_tai_timestamp_packet> decode_tai_from_vector(const std::vector<uint8_t>&);
+
   /**
    * The number of nanoseconds since the TAI epoch of 1958-01-01T00:00:00.0Z.
    */
-  void set_tai_timestamp(uint64_t timestamp) { m_tai_timestamp = timestamp; }
+  void set_tai_timestamp(uint64_t timestamp) { m_timestamp.tai_timestamp = timestamp; }
 
   /**
   * synchronization_state (0=unsynchronized, 1=synchronized)
   */
-  void set_synchronization_state(bool state) { m_synchronization_state = state; }
+  void set_synchronization_state(bool state) { m_timestamp.synchronization_state = state; }
 
   /**
   * timestamp_generation_failure (0=generated, 1=failed)
   */
-  void set_timestamp_generation_failure(bool failure) { m_timestamp_generation_failure = failure; }
+  void set_timestamp_generation_failure(bool failure) { m_timestamp.timestamp_generation_failure = failure; }
 
   /**
    * timestamp_is_modified (0=original 1=modified)
    */
-  void set_timestamp_is_modified(bool is_modified) { m_timestamp_is_modified = is_modified; }
+  void set_timestamp_is_modified(bool is_modified) { m_timestamp.timestamp_is_modified = is_modified; }
 
-  uint64_t get_tai_timestamp() const { return m_tai_timestamp; }
+  uint64_t get_tai_timestamp() const { return m_timestamp.tai_timestamp; }
 
-  bool get_synchronization_state() const { return m_synchronization_state; }
+  bool get_synchronization_state() const { return m_timestamp.synchronization_state; }
 
-  bool get_timestamp_generation_failure() const { return m_timestamp_generation_failure; }
+  bool get_timestamp_generation_failure() const { return m_timestamp.timestamp_generation_failure; }
 
-  bool get_timestamp_is_modified() const { return m_timestamp_is_modified; }
+  bool get_timestamp_is_modified() const { return m_timestamp.timestamp_is_modified; }
+
+  void set_from_tai_timestamp_packet(const heif_tai_timestamp_packet* tai) {
+    heif_tai_timestamp_packet_copy(&m_timestamp, tai);
+  }
+
+  const heif_tai_timestamp_packet* get_tai_timestamp_packet() const
+  {
+    return &m_timestamp;
+  }
+
+  bool operator==(const Box& other) const override;
 
 protected:
   Error parse(BitstreamRange& range, const heif_security_limits*) override;
 
 private:
-  uint64_t m_tai_timestamp = 0;
-  bool m_synchronization_state = false;
-  bool m_timestamp_generation_failure = false;
-  bool m_timestamp_is_modified = false;
+  heif_tai_timestamp_packet m_timestamp;
 };
-#endif
+
+bool operator==(const heif_tai_timestamp_packet& a,
+                const heif_tai_timestamp_packet& b);
 
 #endif
\ No newline at end of file
diff -pruN 1.19.8-1/libheif/brands.cc 1.20.1-1/libheif/brands.cc
--- 1.19.8-1/libheif/brands.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/brands.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,172 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "brands.h"
+#include "file.h"
+#include "sequences/track_visual.h"
+#include <utility>
+
+
+static bool check_mif1(const HeifContext* ctx)
+{
+  auto file = ctx->get_heif_file();
+
+  auto meta = file->get_meta_box();
+  if (!meta || meta->get_version() != 0) {
+    return false;
+  }
+
+  auto hdlr = meta->get_child_box<Box_hdlr>();
+  if (!hdlr || hdlr->get_version() != 0) {
+    return false;
+  }
+
+  auto iloc = meta->get_child_box<Box_iloc>();
+  if (!iloc || iloc->get_version() > 2) {
+    return false;
+  }
+
+  auto iinf = meta->get_child_box<Box_iinf>();
+  if (!iinf || iinf->get_version() > 1) {
+    return false;
+  }
+
+  auto infe = iinf->get_child_box<Box_infe>();
+  if (!infe || infe->get_version() < 2 || infe->get_version() > 3) {
+    return false;
+  }
+
+  auto pitm = meta->get_child_box<Box_pitm>();
+  if (!pitm || pitm->get_version() > 1) {
+    return false;
+  }
+
+  auto iprp = meta->get_child_box<Box_iprp>();
+  if (!iprp) {
+    return false;
+  }
+
+  return true;
+}
+
+
+std::vector<std::shared_ptr<const ImageItem>> get_primary_and_alternative_images(const HeifContext* ctx)
+{
+  auto img = ctx->get_primary_image(false);
+  if (img) {
+    return {std::move(img)};
+  }
+  else {
+    return {};
+  }
+}
+
+
+std::vector<heif_brand2> compute_compatible_brands(const HeifContext* ctx, heif_brand2* out_main_brand)
+{
+  std::vector<heif_brand2> compatible_brands;
+
+  heif_brand2 dummy;
+  if (out_main_brand == nullptr) {
+    out_main_brand = &dummy; // so that we do not have to check for NULL out_main_brand in the following
+  }
+
+  *out_main_brand = 0;
+
+  // --- "mif" brands
+
+  bool is_mif1 = check_mif1(ctx);
+
+  if (is_mif1) {
+    compatible_brands.push_back(heif_brand2_mif1);
+    *out_main_brand = heif_brand2_mif1;
+  }
+
+  bool is_structural_image = is_mif1;
+
+
+  // --- image brand
+
+  std::vector<std::shared_ptr<const ImageItem>> images = get_primary_and_alternative_images(ctx);
+
+  bool miaf_compatible = true;
+
+  for (auto& img : images) {
+    heif_brand2 brand = img->get_compatible_brand();
+    if (brand != 0 &&
+        is_structural_image &&
+        std::find(compatible_brands.begin(),
+                  compatible_brands.end(),
+                  brand) == compatible_brands.end()) {
+      compatible_brands.push_back(brand);
+    }
+
+    if (!img->is_miaf_compatible()) {
+      miaf_compatible = false;
+    }
+  }
+
+  // --- "miaf"
+
+  if (miaf_compatible && is_structural_image) {
+    compatible_brands.push_back(heif_brand2_miaf);
+  }
+
+
+  // --- main brand is first image brand
+
+  if (!images.empty()) {
+    heif_brand2 brand = images[0]->get_compatible_brand();
+    if (brand != 0) {
+      *out_main_brand = brand;
+    }
+  }
+
+
+  // --- --- sequences
+
+  if (ctx->has_sequence()) {
+    compatible_brands.push_back(heif_brand2_msf1);
+    compatible_brands.push_back(heif_brand2_iso8);
+
+    auto track_result = ctx->get_track(0);
+    assert(!track_result.error);
+
+    std::shared_ptr<const Track> track = track_result.value;
+    std::shared_ptr<const Track_Visual> visual_track = std::dynamic_pointer_cast<const Track_Visual>(track);
+
+    heif_brand2 track_brand = visual_track->get_compatible_brand();
+    if (track_brand != 0) {
+      compatible_brands.push_back(track_brand);
+
+      if (*out_main_brand == 0) {
+        *out_main_brand = track_brand;
+      }
+    }
+
+    // if we don't have a track brand, use at least the sequence structural brand
+    if (*out_main_brand == 0) {
+      *out_main_brand = heif_brand2_msf1;
+    }
+  }
+
+  return compatible_brands;
+}
+
diff -pruN 1.19.8-1/libheif/brands.h 1.20.1-1/libheif/brands.h
--- 1.19.8-1/libheif/brands.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/brands.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,32 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_BRANDS_H
+#define LIBHEIF_BRANDS_H
+
+#include "context.h"
+#include "error.h"
+
+#include <memory>
+#include <vector>
+
+std::vector<heif_brand2> compute_compatible_brands(const HeifContext* ctx, heif_brand2* out_main_brand);
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/avc_boxes.h 1.20.1-1/libheif/codecs/avc_boxes.h
--- 1.19.8-1/libheif/codecs/avc_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/avc_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -28,6 +28,7 @@
 #include <string>
 #include <memory>
 #include "image-items/image_item.h"
+#include "sequences/seq_boxes.h"
 
 
 class Box_avcC : public Box {
@@ -90,4 +91,14 @@ private:
   std::vector< std::vector<uint8_t> > m_sps_ext;
 };
 
+
+
+class Box_avc1 : public Box_VisualSampleEntry
+{
+public:
+  Box_avc1()
+  {
+    set_short_type(fourcc("avc1"));
+  }
+};
 #endif
diff -pruN 1.19.8-1/libheif/codecs/avif_boxes.h 1.20.1-1/libheif/codecs/avif_boxes.h
--- 1.19.8-1/libheif/codecs/avif_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/avif_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -31,6 +31,7 @@
 #include "box.h"
 #include "error.h"
 #include "image-items/image_item.h"
+#include "sequences/seq_boxes.h"
 
 
 class Box_av1C : public Box
@@ -114,6 +115,15 @@ private:
 };
 
 
+class Box_av01 : public Box_VisualSampleEntry
+{
+public:
+  Box_av01()
+  {
+    set_short_type(fourcc("av01"));
+  }
+};
+
 class Box_a1op : public Box
 {
 public:
diff -pruN 1.19.8-1/libheif/codecs/avif_enc.cc 1.20.1-1/libheif/codecs/avif_enc.cc
--- 1.19.8-1/libheif/codecs/avif_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/avif_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,94 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "avif_enc.h"
+#include "avif_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+
+#include <string>
+
+
+Result<Encoder::CodedImageData> Encoder_AVIF::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                     struct heif_encoder* encoder,
+                                                     const struct heif_encoding_options& options,
+                                                     enum heif_image_input_class input_class)
+{
+  Encoder::CodedImageData codedImage;
+
+  Box_av1C::configuration config;
+
+  // Fill preliminary av1C in case we cannot parse the sequence_header() correctly in the code below.
+  // TODO: maybe we can remove this later.
+  fill_av1C_configuration(&config, image);
+
+  heif_image c_api_image;
+  c_api_image.image = image;
+
+  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
+  if (err.code) {
+    return Error(err.code,
+                 err.subcode,
+                 err.message);
+  }
+
+  for (;;) {
+    uint8_t* data;
+    int size;
+
+    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
+
+    bool found_config = fill_av1C_configuration_from_stream(&config, data, size);
+    (void) found_config;
+
+    if (data == nullptr) {
+      break;
+    }
+
+    codedImage.append(data, size);
+  }
+
+  auto av1C = std::make_shared<Box_av1C>();
+  av1C->set_configuration(config);
+  codedImage.properties.push_back(av1C);
+
+  codedImage.codingConstraints.intra_pred_used = true;
+  codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames
+
+  return codedImage;
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_AVIF::get_sample_description_box(const CodedImageData& data) const
+{
+  auto av01 = std::make_shared<Box_av01>();
+  av01->get_VisualSampleEntry().compressorname = "AVIF";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("av1C")) {
+      av01->append_child_box(prop);
+      return av01;
+    }
+  }
+
+  assert(false); // no av1C generated
+  return nullptr;
+}
diff -pruN 1.19.8-1/libheif/codecs/avif_enc.h 1.20.1-1/libheif/codecs/avif_enc.h
--- 1.19.8-1/libheif/codecs/avif_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/avif_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_AVIF_H
+#define HEIF_ENCODER_AVIF_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_AVIF : public Encoder {
+public:
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/decoder.cc 1.20.1-1/libheif/codecs/decoder.cc
--- 1.19.8-1/libheif/codecs/decoder.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/decoder.cc	2025-07-02 13:05:31.000000000 +0000
@@ -38,8 +38,11 @@
 #include "vvc_boxes.h"
 #include "jpeg_boxes.h"
 #include "jpeg2000_boxes.h"
-#include "codecs/uncompressed/unc_dec.h"
 
+#if WITH_UNCOMPRESSED_CODEC
+#include "codecs/uncompressed/unc_dec.h"
+#include "codecs/uncompressed/unc_boxes.h"
+#endif
 
 void DataExtent::set_from_image_item(std::shared_ptr<HeifFile> file, heif_item_id item)
 {
@@ -49,6 +52,15 @@ void DataExtent::set_from_image_item(std
 }
 
 
+void DataExtent::set_file_range(std::shared_ptr<HeifFile> file, uint64_t offset, uint32_t size)
+{
+  m_file = std::move(file);
+  m_source = Source::FileRange;
+  m_offset = offset;
+  m_size = size;
+}
+
+
 Result<std::vector<uint8_t>*> DataExtent::read_data() const
 {
   if (!m_raw.empty()) {
@@ -64,8 +76,11 @@ Result<std::vector<uint8_t>*> DataExtent
     }
   }
   else {
-    // sequence
-    assert(false); // TODO
+    // file range
+    Error err = m_file->append_data_from_file_range(m_raw, m_offset, m_size);
+    if (err) {
+      return err;
+    }
   }
 
   return &m_raw;
@@ -84,16 +99,20 @@ Result<std::vector<uint8_t>> DataExtent:
     // TODO: cache data
 
     // image
-    Error err = m_file->append_data_from_iloc(m_item_id, m_raw, 0, size);
+    Error err = m_file->append_data_from_iloc(m_item_id, data, offset, size);
     if (err) {
       return err;
     }
     return data;
   }
   else {
-    // sequence
-    assert(false); // TODO
-    return Error::Ok;
+    // file range
+    Error err = m_file->append_data_from_file_range(data, m_offset, m_size);
+    if (err) {
+      return err;
+    }
+
+    return data;
   }
 }
 
@@ -131,7 +150,8 @@ std::shared_ptr<Decoder> Decoder::alloc_
     case fourcc("unci"): {
       auto uncC = item->get_property<Box_uncC>();
       auto cmpd = item->get_property<Box_cmpd>();
-      return std::make_shared<Decoder_uncompressed>(uncC,cmpd);
+      auto ispe = item->get_property<Box_ispe>();
+      return std::make_shared<Decoder_uncompressed>(uncC,cmpd,ispe);
     }
 #endif
     case fourcc("mski"): {
@@ -143,6 +163,59 @@ std::shared_ptr<Decoder> Decoder::alloc_
 }
 
 
+std::shared_ptr<Decoder> Decoder::alloc_for_sequence_sample_description_box(std::shared_ptr<const Box_VisualSampleEntry> sample_description_box)
+{
+  std::string compressor = sample_description_box->get_VisualSampleEntry_const().compressorname;
+  uint32_t sampleType = sample_description_box->get_short_type();
+
+  switch (sampleType) {
+    case fourcc("hvc1"): {
+      auto hvcC = sample_description_box->get_child_box<Box_hvcC>();
+      return std::make_shared<Decoder_HEVC>(hvcC);
+    }
+
+    case fourcc("av01"): {
+      auto av1C = sample_description_box->get_child_box<Box_av1C>();
+      return std::make_shared<Decoder_AVIF>(av1C);
+    }
+
+    case fourcc("vvc1"): {
+      auto vvcC = sample_description_box->get_child_box<Box_vvcC>();
+      return std::make_shared<Decoder_VVC>(vvcC);
+    }
+
+    case fourcc("avc1"): {
+      auto avcC = sample_description_box->get_child_box<Box_avcC>();
+      return std::make_shared<Decoder_AVC>(avcC);
+    }
+
+#if WITH_UNCOMPRESSED_CODEC
+    case fourcc("uncv"): {
+      auto uncC = sample_description_box->get_child_box<Box_uncC>();
+      auto cmpd = sample_description_box->get_child_box<Box_cmpd>();
+      auto ispe = std::make_shared<Box_ispe>();
+      ispe->set_size(sample_description_box->get_VisualSampleEntry_const().width,
+                     sample_description_box->get_VisualSampleEntry_const().height);
+      return std::make_shared<Decoder_uncompressed>(uncC, cmpd, ispe);
+    }
+#endif
+
+    case fourcc("j2ki"): {
+      auto j2kH = sample_description_box->get_child_box<Box_j2kH>();
+      return std::make_shared<Decoder_JPEG2000>(j2kH);
+    }
+
+    case fourcc("mjpg"): {
+      auto jpgC = sample_description_box->get_child_box<Box_jpgC>();
+      return std::make_shared<Decoder_JPEG>(jpgC);
+    }
+
+    default:
+      return nullptr;
+  }
+}
+
+
 Result<std::vector<uint8_t>> Decoder::get_compressed_data() const
 {
   // --- get the compressed image data
@@ -170,7 +243,8 @@ Result<std::vector<uint8_t>> Decoder::ge
 
 
 Result<std::shared_ptr<HeifPixelImage>>
-Decoder::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options)
+Decoder::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options,
+                                                  const struct heif_security_limits* limits)
 {
   const struct heif_decoder_plugin* decoder_plugin = get_decoder(get_compression_format(), options.decoder_id);
   if (!decoder_plugin) {
@@ -182,7 +256,7 @@ Decoder::decode_single_frame_from_compre
 
   if (decoder_plugin->new_decoder == nullptr) {
     return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed,
-                 "Cannot decode with a dummy decoder plugins.");
+                 "Cannot decode with a dummy decoder plugin.");
   }
 
   void* decoder;
@@ -212,9 +286,19 @@ Decoder::decode_single_frame_from_compre
 
   heif_image* decoded_img = nullptr;
 
-  err = decoder_plugin->decode_image(decoder, &decoded_img);
-  if (err.code != heif_error_Ok) {
-    return Error(err.code, err.subcode, err.message);
+  if (decoder_plugin->plugin_api_version >= 4 &&
+      decoder_plugin->decode_next_image != nullptr) {
+
+    err = decoder_plugin->decode_next_image(decoder, &decoded_img, limits);
+    if (err.code != heif_error_Ok) {
+      return Error::from_heif_error(err);
+    }
+  }
+  else {
+    err = decoder_plugin->decode_image(decoder, &decoded_img);
+    if (err.code != heif_error_Ok) {
+      return Error::from_heif_error(err);
+    }
   }
 
   if (!decoded_img) {
diff -pruN 1.19.8-1/libheif/codecs/decoder.h 1.20.1-1/libheif/codecs/decoder.h
--- 1.19.8-1/libheif/codecs/decoder.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/decoder.h	2025-07-02 13:05:31.000000000 +0000
@@ -39,7 +39,7 @@
 struct DataExtent
 {
   std::shared_ptr<HeifFile> m_file;
-  enum class Source : uint8_t { Raw, Image, Sequence } m_source = Source::Raw;
+  enum class Source : uint8_t { Raw, Image, FileRange } m_source = Source::Raw;
 
   // --- raw data
   mutable std::vector<uint8_t> m_raw; // also for cached data
@@ -47,11 +47,14 @@ struct DataExtent
   // --- image
   heif_item_id m_item_id = 0;
 
-  // --- sequence
-  // TODO
+  // --- file range
+  uint64_t m_offset = 0;
+  uint32_t m_size = 0;
 
   void set_from_image_item(std::shared_ptr<HeifFile> file, heif_item_id item);
 
+  void set_file_range(std::shared_ptr<HeifFile> file, uint64_t offset, uint32_t size);
+
   Result<std::vector<uint8_t>*> read_data() const;
 
   Result<std::vector<uint8_t>> read_data(uint64_t offset, uint64_t size) const;
@@ -63,6 +66,8 @@ class Decoder
 public:
   static std::shared_ptr<Decoder> alloc_for_infe_type(const ImageItem* item);
 
+  static std::shared_ptr<Decoder> alloc_for_sequence_sample_description_box(std::shared_ptr<const class Box_VisualSampleEntry> sample_description_box);
+
 
   virtual ~Decoder() = default;
 
@@ -70,6 +75,8 @@ public:
 
   void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); }
 
+  const DataExtent& get_data_extent() const { return m_data_extent; }
+
   // --- information about the image format
 
   [[nodiscard]] virtual int get_luma_bits_per_pixel() const = 0;
@@ -87,7 +94,8 @@ public:
   // --- decoding
 
   virtual Result<std::shared_ptr<HeifPixelImage>>
-  decode_single_frame_from_compressed_data(const struct heif_decoding_options& options);
+  decode_single_frame_from_compressed_data(const struct heif_decoding_options& options,
+                                           const struct heif_security_limits* limits);
 
 private:
   DataExtent m_data_extent;
diff -pruN 1.19.8-1/libheif/codecs/encoder.cc 1.20.1-1/libheif/codecs/encoder.cc
--- 1.19.8-1/libheif/codecs/encoder.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/encoder.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,164 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "codecs/encoder.h"
+
+#include "error.h"
+#include "context.h"
+#include "plugin_registry.h"
+#include "libheif/api_structs.h"
+#include "color-conversion/colorconversion.h"
+
+
+void Encoder::CodedImageData::append(const uint8_t* data, size_t size)
+{
+  bitstream.insert(bitstream.end(), data, data + size);
+}
+
+
+void Encoder::CodedImageData::append_with_4bytes_size(const uint8_t* data, size_t size)
+{
+  assert(size <= 0xFFFFFFFF);
+
+  uint8_t size_field[4];
+  size_field[0] = (uint8_t) ((size >> 24) & 0xFF);
+  size_field[1] = (uint8_t) ((size >> 16) & 0xFF);
+  size_field[2] = (uint8_t) ((size >> 8) & 0xFF);
+  size_field[3] = (uint8_t) ((size >> 0) & 0xFF);
+
+  bitstream.insert(bitstream.end(), size_field, size_field + 4);
+  bitstream.insert(bitstream.end(), data, data + size);
+}
+
+
+
+// TODO: remove me, moved to encoder.cc
+static std::shared_ptr<color_profile_nclx> compute_target_nclx_profile(const std::shared_ptr<HeifPixelImage>& image, const heif_color_profile_nclx* output_nclx_profile)
+{
+  auto target_nclx_profile = std::make_shared<color_profile_nclx>();
+
+  // If there is an output NCLX specified, use that.
+  if (output_nclx_profile) {
+    target_nclx_profile->set_from_heif_color_profile_nclx(output_nclx_profile);
+  }
+    // Otherwise, if there is an input NCLX, keep that.
+  else if (auto input_nclx = image->get_color_profile_nclx()) {
+    *target_nclx_profile = *input_nclx;
+  }
+    // Otherwise, just use the defaults (set below)
+  else {
+    target_nclx_profile->set_undefined();
+  }
+
+  target_nclx_profile->replace_undefined_values_with_sRGB_defaults();
+
+  return target_nclx_profile;
+}
+
+
+// TODO: remove me, moved to encoder.cc
+static bool nclx_profile_matches_spec(heif_colorspace colorspace,
+                                      std::shared_ptr<const color_profile_nclx> image_nclx,
+                                      const struct heif_color_profile_nclx* spec_nclx)
+{
+  if (colorspace != heif_colorspace_YCbCr) {
+    return true;
+  }
+
+  // No target specification -> always matches
+  if (!spec_nclx) {
+    return true;
+  }
+
+  if (!image_nclx) {
+    // if no input nclx is specified, compare against default one
+    image_nclx = std::make_shared<color_profile_nclx>();
+  }
+
+  if (image_nclx->get_full_range_flag() != (spec_nclx->full_range_flag == 0 ? false : true)) {
+    return false;
+  }
+
+  if (image_nclx->get_matrix_coefficients() != spec_nclx->matrix_coefficients) {
+    return false;
+  }
+
+  // TODO: are the colour primaries relevant for matrix-coefficients != 12,13 ?
+  //       If not, we should skip this test for anything else than matrix-coefficients != 12,13.
+  if (image_nclx->get_colour_primaries() != spec_nclx->color_primaries) {
+    return false;
+  }
+
+  return true;
+}
+
+
+extern void fill_default_color_conversion_options_ext(heif_color_conversion_options_ext& options);
+
+Result<std::shared_ptr<HeifPixelImage>> Encoder::convert_colorspace_for_encoding(const std::shared_ptr<HeifPixelImage>& image,
+                                                                                 struct heif_encoder* encoder,
+                                                                                 const struct heif_encoding_options& options,
+                                                                                 const heif_security_limits* security_limits)
+{
+  const heif_color_profile_nclx* output_nclx_profile;
+
+  if (const auto* nclx = get_forced_output_nclx()) {
+    output_nclx_profile = nclx;
+  } else {
+    output_nclx_profile = options.output_nclx_profile;
+  }
+
+
+  heif_colorspace colorspace = image->get_colorspace();
+  heif_chroma chroma = image->get_chroma_format();
+
+  if (encoder->plugin->plugin_api_version >= 2) {
+    encoder->plugin->query_input_colorspace2(encoder->encoder, &colorspace, &chroma);
+  }
+  else {
+    encoder->plugin->query_input_colorspace(&colorspace, &chroma);
+  }
+
+
+  // If output format forces an NCLX, use that. Otherwise use user selected NCLX.
+
+  std::shared_ptr<color_profile_nclx> target_nclx_profile = compute_target_nclx_profile(image, output_nclx_profile);
+
+  // --- convert colorspace
+
+  std::shared_ptr<HeifPixelImage> output_image;
+
+  if (colorspace == image->get_colorspace() &&
+      chroma == image->get_chroma_format() &&
+      nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), output_nclx_profile)) {
+    return image;
+  }
+
+
+  // @TODO: use color profile when converting
+  int output_bpp = 0; // same as input
+
+  //auto target_nclx = std::make_shared<color_profile_nclx>();
+  //target_nclx->set_from_heif_color_profile_nclx(target_heif_nclx);
+
+  return convert_colorspace(image, colorspace, chroma, target_nclx_profile,
+                            output_bpp, options.color_conversion_options, nullptr,
+                            security_limits);
+}
diff -pruN 1.19.8-1/libheif/codecs/encoder.h 1.20.1-1/libheif/codecs/encoder.h
--- 1.19.8-1/libheif/codecs/encoder.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/encoder.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,76 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_H
+#define HEIF_ENCODER_H
+
+#include "libheif/heif.h"
+#include "error.h"
+#include "libheif/heif_plugin.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "sequences/seq_boxes.h"
+
+class HeifPixelImage;
+
+class Box;
+
+
+class Encoder {
+public:
+  virtual ~Encoder() = default;
+
+  struct CodedImageData {
+    std::vector<std::shared_ptr<Box>> properties;
+    std::vector<uint8_t> bitstream;
+    CodingConstraints codingConstraints;
+
+    // If 0, the encoded size is equal to the input size.
+    uint32_t encoded_image_width = 0;
+    uint32_t encoded_image_height = 0;
+
+    bool is_sync_frame = true; // TODO: set in encoder
+
+    void append(const uint8_t* data, size_t size);
+
+    void append_with_4bytes_size(const uint8_t* data, size_t size);
+  };
+
+  // If the output format requires a specific nclx (like JPEG), return this. Otherwise, return NULL.
+  virtual const heif_color_profile_nclx* get_forced_output_nclx() const { return nullptr; }
+
+  Result<std::shared_ptr<HeifPixelImage>> convert_colorspace_for_encoding(const std::shared_ptr<HeifPixelImage>& image,
+                                                                          struct heif_encoder* encoder,
+                                                                          const struct heif_encoding_options& options,
+                                                                          const heif_security_limits* security_limits);
+
+  virtual Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                        struct heif_encoder* encoder,
+                                        const struct heif_encoding_options& options,
+                                        enum heif_image_input_class input_class) { return {}; }
+
+  virtual std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const { return {}; }
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/hevc_boxes.cc 1.20.1-1/libheif/codecs/hevc_boxes.cc
--- 1.19.8-1/libheif/codecs/hevc_boxes.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/hevc_boxes.cc	2025-07-02 13:05:31.000000000 +0000
@@ -30,45 +30,42 @@
 #include <iomanip>
 #include <string>
 #include <utility>
+#include <algorithm>
 #include <libheif/api_structs.h>
 
 
-Error Box_hvcC::parse(BitstreamRange& range, const heif_security_limits* limits)
+Error HEVCDecoderConfigurationRecord::parse(BitstreamRange& range, const heif_security_limits* limits)
 {
-  //parse_full_box_header(range);
-
   uint8_t byte;
 
-  auto& c = m_configuration; // abbreviation
-
-  c.configuration_version = range.read8();
+  configuration_version = range.read8();
   byte = range.read8();
-  c.general_profile_space = (byte >> 6) & 3;
-  c.general_tier_flag = (byte >> 5) & 1;
-  c.general_profile_idc = (byte & 0x1F);
+  general_profile_space = (byte >> 6) & 3;
+  general_tier_flag = (byte >> 5) & 1;
+  general_profile_idc = (byte & 0x1F);
 
-  c.general_profile_compatibility_flags = range.read32();
+  general_profile_compatibility_flags = range.read32();
 
   for (int i = 0; i < 6; i++) {
     byte = range.read8();
 
     for (int b = 0; b < 8; b++) {
-      c.general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1;
+      general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1;
     }
   }
 
-  c.general_level_idc = range.read8();
-  c.min_spatial_segmentation_idc = range.read16() & 0x0FFF;
-  c.parallelism_type = range.read8() & 0x03;
-  c.chroma_format = range.read8() & 0x03;
-  c.bit_depth_luma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
-  c.bit_depth_chroma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
-  c.avg_frame_rate = range.read16();
+  general_level_idc = range.read8();
+  min_spatial_segmentation_idc = range.read16() & 0x0FFF;
+  parallelism_type = range.read8() & 0x03;
+  chroma_format = range.read8() & 0x03;
+  bit_depth_luma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
+  bit_depth_chroma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
+  avg_frame_rate = range.read16();
 
   byte = range.read8();
-  c.constant_frame_rate = (byte >> 6) & 0x03;
-  c.num_temporal_layers = (byte >> 3) & 0x07;
-  c.temporal_id_nested = (byte >> 2) & 1;
+  constant_frame_rate = (byte >> 6) & 0x03;
+  num_temporal_layers = (byte >> 3) & 0x07;
+  temporal_id_nested = (byte >> 2) & 1;
 
   m_length_size = static_cast<uint8_t>((byte & 0x03) + 1);
 
@@ -112,6 +109,93 @@ Error Box_hvcC::parse(BitstreamRange& ra
 }
 
 
+Error HEVCDecoderConfigurationRecord::write(StreamWriter& writer) const
+{
+  writer.write8(configuration_version);
+
+  writer.write8((uint8_t) (((general_profile_space & 3) << 6) |
+                           ((general_tier_flag & 1) << 5) |
+                           (general_profile_idc & 0x1F)));
+
+  writer.write32(general_profile_compatibility_flags);
+
+  for (int i = 0; i < 6; i++) {
+    uint8_t byte = 0;
+
+    for (int b = 0; b < 8; b++) {
+      if (general_constraint_indicator_flags[i * 8 + b]) {
+        byte |= 1;
+      }
+
+      byte = (uint8_t) (byte << 1);
+    }
+
+    writer.write8(byte);
+  }
+
+  writer.write8(general_level_idc);
+  writer.write16((min_spatial_segmentation_idc & 0x0FFF) | 0xF000);
+  writer.write8(parallelism_type | 0xFC);
+  writer.write8(chroma_format | 0xFC);
+  writer.write8((uint8_t) ((bit_depth_luma - 8) | 0xF8));
+  writer.write8((uint8_t) ((bit_depth_chroma - 8) | 0xF8));
+  writer.write16(avg_frame_rate);
+
+  writer.write8((uint8_t) (((constant_frame_rate & 0x03) << 6) |
+                           ((num_temporal_layers & 0x07) << 3) |
+                           ((temporal_id_nested & 1) << 2) |
+                           ((m_length_size - 1) & 0x03)));
+
+  size_t nArrays = m_nal_array.size();
+  if (nArrays > 0xFF) {
+    // TODO: error: too many NAL units
+  }
+
+  writer.write8((uint8_t) nArrays);
+
+  for (const HEVCDecoderConfigurationRecord::NalArray& array : m_nal_array) {
+
+    writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) |
+                             (array.m_NAL_unit_type & 0x3F)));
+
+    size_t nUnits = array.m_nal_units.size();
+    if (nUnits > 0xFFFF) {
+      // TODO: error: too many NAL units
+    }
+
+    writer.write16((uint16_t) nUnits);
+
+    for (const std::vector<uint8_t>& nal_unit : array.m_nal_units) {
+      writer.write16((uint16_t) nal_unit.size());
+      writer.write(nal_unit);
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+bool HEVCDecoderConfigurationRecord::get_general_profile_compatibility_flag(int idx) const
+{
+  return general_profile_compatibility_flags & (UINT32_C(0x80000000) >> idx);
+}
+
+
+bool HEVCDecoderConfigurationRecord::is_profile_compatibile(Profile profile) const
+{
+  return (general_profile_idc == profile ||
+          get_general_profile_compatibility_flag(profile));
+}
+
+
+Error Box_hvcC::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  //parse_full_box_header(range);
+
+  return m_configuration.parse(range, limits);
+}
+
+
 std::string Box_hvcC::dump(Indent& indent) const
 {
   std::ostringstream sstr;
@@ -134,7 +218,7 @@ std::string Box_hvcC::dump(Indent& inden
 
   sstr << indent << "general_constraint_indicator_flags: ";
   int cnt = 0;
-  for (int i = 0; i < configuration::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) {
+  for (int i = 0; i < HEVCDecoderConfigurationRecord::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) {
     bool b = c.general_constraint_indicator_flags[i];
 
     sstr << (b ? 1 : 0);
@@ -171,9 +255,9 @@ std::string Box_hvcC::dump(Indent& inden
        << indent << "constant_frame_rate: " << ((int) c.constant_frame_rate) << "\n"
        << indent << "num_temporal_layers: " << ((int) c.num_temporal_layers) << "\n"
        << indent << "temporal_id_nested: " << ((int) c.temporal_id_nested) << "\n"
-       << indent << "length_size: " << ((int) m_length_size) << "\n";
+       << indent << "length_size: " << ((int) c.m_length_size) << "\n";
 
-  for (const auto& array : m_nal_array) {
+  for (const auto& array : c.m_nal_array) {
     sstr << indent << "<array>\n";
 
     indent++;
@@ -199,7 +283,7 @@ std::string Box_hvcC::dump(Indent& inden
 
 bool Box_hvcC::get_headers(std::vector<uint8_t>* dest) const
 {
-  for (const auto& array : m_nal_array) {
+  for (const auto& array : m_configuration.m_nal_array) {
     for (const auto& unit : array.m_nal_units) {
 
       dest->push_back((unit.size() >> 24) & 0xFF);
@@ -223,12 +307,12 @@ bool Box_hvcC::get_headers(std::vector<u
 
 void Box_hvcC::append_nal_data(const std::vector<uint8_t>& nal)
 {
-  NalArray array;
+  HEVCDecoderConfigurationRecord::NalArray array;
   array.m_array_completeness = 0;
   array.m_NAL_unit_type = uint8_t(nal[0] >> 1);
   array.m_nal_units.push_back(nal);
 
-  m_nal_array.push_back(array);
+  m_configuration.m_nal_array.push_back(array);
 }
 
 void Box_hvcC::append_nal_data(const uint8_t* data, size_t size)
@@ -237,79 +321,73 @@ void Box_hvcC::append_nal_data(const uin
   nal.resize(size);
   memcpy(nal.data(), data, size);
 
-  NalArray array;
-  array.m_array_completeness = 0;
-  array.m_NAL_unit_type = uint8_t(nal[0] >> 1);
-  array.m_nal_units.push_back(std::move(nal));
-
-  m_nal_array.push_back(array);
-}
+  for (auto& nal_array : m_configuration.m_nal_array) {
+    if (nal_array.m_NAL_unit_type == uint8_t(nal[0] >> 1)) {
 
+      // kvazaar may send the same headers multiple times. Filter out the identical copies.
 
-Error Box_hvcC::write(StreamWriter& writer) const
-{
-  size_t box_start = reserve_box_header_space(writer);
+      for (auto& nal_unit : nal_array.m_nal_units) {
 
-  const auto& c = m_configuration; // abbreviation
-
-  writer.write8(c.configuration_version);
+        // Note: sometimes kvazaar even sends the same packet twice, but with an extra zero byte.
+        //       We detect this by comparing only the common length. This is correct since each NAL
+        //       packet must be complete and thus, if a packet is longer than another complete packet,
+        //       its extra data must be superfluous.
+        //
+        // Example:
+        //| | | <array>
+        //| | | | array_completeness: 1
+        //| | | | NAL_unit_type: 34
+        //| | | | 44 01 c1 71 82 99 20 00
+        //| | | | 44 01 c1 71 82 99 20
+
+        // Check whether packets have similar content.
+
+        size_t common_length = std::min(nal_unit.size(), nal.size());
+        bool similar = true;
+        for (size_t i = 0; i < common_length; i++) {
+          if (nal_unit[i] != nal[i]) {
+            similar = false;
+            break;
+          }
+        }
 
-  writer.write8((uint8_t) (((c.general_profile_space & 3) << 6) |
-                           ((c.general_tier_flag & 1) << 5) |
-                           (c.general_profile_idc & 0x1F)));
+        if (similar) {
+          // If they are similar, keep the smaller one.
 
-  writer.write32(c.general_profile_compatibility_flags);
+          if (nal_unit.size() > nal.size()) {
+            nal_unit = std::move(nal);
+          }
 
-  for (int i = 0; i < 6; i++) {
-    uint8_t byte = 0;
+          // Exit. Do not add a copy of the packet.
 
-    for (int b = 0; b < 8; b++) {
-      if (c.general_constraint_indicator_flags[i * 8 + b]) {
-        byte |= 1;
+          return;
+        }
       }
 
-      byte = (uint8_t) (byte << 1);
-    }
-
-    writer.write8(byte);
-  }
+      nal_array.m_nal_units.push_back(std::move(nal));
 
-  writer.write8(c.general_level_idc);
-  writer.write16((c.min_spatial_segmentation_idc & 0x0FFF) | 0xF000);
-  writer.write8(c.parallelism_type | 0xFC);
-  writer.write8(c.chroma_format | 0xFC);
-  writer.write8((uint8_t) ((c.bit_depth_luma - 8) | 0xF8));
-  writer.write8((uint8_t) ((c.bit_depth_chroma - 8) | 0xF8));
-  writer.write16(c.avg_frame_rate);
-
-  writer.write8((uint8_t) (((c.constant_frame_rate & 0x03) << 6) |
-                           ((c.num_temporal_layers & 0x07) << 3) |
-                           ((c.temporal_id_nested & 1) << 2) |
-                           ((m_length_size - 1) & 0x03)));
-
-  size_t nArrays = m_nal_array.size();
-  if (nArrays > 0xFF) {
-    // TODO: error: too many NAL units
+      return;
+    }
   }
 
-  writer.write8((uint8_t) nArrays);
+  HEVCDecoderConfigurationRecord::NalArray array;
+  array.m_array_completeness = 1;
+  array.m_NAL_unit_type = uint8_t(nal[0] >> 1);
+  array.m_nal_units.push_back(std::move(nal));
 
-  for (const NalArray& array : m_nal_array) {
+  m_configuration.m_nal_array.push_back(array);
+}
 
-    writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) |
-                             (array.m_NAL_unit_type & 0x3F)));
 
-    size_t nUnits = array.m_nal_units.size();
-    if (nUnits > 0xFFFF) {
-      // TODO: error: too many NAL units
-    }
+Error Box_hvcC::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
 
-    writer.write16((uint16_t) nUnits);
+  const auto& c = m_configuration; // abbreviation
 
-    for (const std::vector<uint8_t>& nal_unit : array.m_nal_units) {
-      writer.write16((uint16_t) nal_unit.size());
-      writer.write(nal_unit);
-    }
+  Error err = c.write(writer);
+  if (err) {
+    return err;
   }
 
   prepend_header(writer, box_start);
@@ -519,7 +597,7 @@ static std::vector<uint8_t> remove_start
 
 
 Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size,
-                                       Box_hvcC::configuration* config,
+                                       HEVCDecoderConfigurationRecord* config,
                                        int* width, int* height)
 {
   // remove start-code emulation bytes from SPS header stream
diff -pruN 1.19.8-1/libheif/codecs/hevc_boxes.h 1.20.1-1/libheif/codecs/hevc_boxes.h
--- 1.19.8-1/libheif/codecs/hevc_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/hevc_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -29,11 +29,66 @@
 #include <string>
 #include <vector>
 #include "image-items/image_item.h"
+#include "sequences/seq_boxes.h"
 
 
-class Box_hvcC : public Box
+struct HEVCDecoderConfigurationRecord
 {
+  uint8_t configuration_version;
+  uint8_t general_profile_space;
+  bool general_tier_flag;
+  uint8_t general_profile_idc;
+  uint32_t general_profile_compatibility_flags;
+
+  static const int NUM_CONSTRAINT_INDICATOR_FLAGS = 48;
+  std::bitset<NUM_CONSTRAINT_INDICATOR_FLAGS> general_constraint_indicator_flags;
+
+  uint8_t general_level_idc;
+
+  uint16_t min_spatial_segmentation_idc;
+  uint8_t parallelism_type;
+  uint8_t chroma_format;
+  uint8_t bit_depth_luma;
+  uint8_t bit_depth_chroma;
+  uint16_t avg_frame_rate;
+
+  uint8_t constant_frame_rate;
+  uint8_t num_temporal_layers;
+  uint8_t temporal_id_nested;
+  uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths
+
+  struct NalArray
+  {
+    uint8_t m_array_completeness;
+    uint8_t m_NAL_unit_type;
+
+    std::vector<std::vector<uint8_t> > m_nal_units;
+  };
+
+  enum Profile {
+    Profile_Main = 1,
+    Profile_Main10 = 2,
+    Profile_MainStillPicture = 3,
+    Profile_RExt = 4,
+    Profile_HighThroughput = 5,
+    Profile_ScreenCoding = 9,
+    Profile_HighTHroughputScreenCoding = 11
+  };
+
+  std::vector<NalArray> m_nal_array;
+
+  Error parse(BitstreamRange& range, const heif_security_limits* limits);
+
+  Error write(StreamWriter& writer) const;
+
+  bool get_general_profile_compatibility_flag(int idx) const;
 
+  bool is_profile_compatibile(Profile) const;
+};
+
+
+class Box_hvcC : public Box
+{
 // allow access to protected parse() method
 friend class Box_mini;
 
@@ -45,39 +100,18 @@ public:
 
   bool is_essential() const override { return true; }
 
-  struct configuration
-  {
-    uint8_t configuration_version;
-    uint8_t general_profile_space;
-    bool general_tier_flag;
-    uint8_t general_profile_idc;
-    uint32_t general_profile_compatibility_flags;
-
-    static const int NUM_CONSTRAINT_INDICATOR_FLAGS = 48;
-    std::bitset<NUM_CONSTRAINT_INDICATOR_FLAGS> general_constraint_indicator_flags;
-
-    uint8_t general_level_idc;
-
-    uint16_t min_spatial_segmentation_idc;
-    uint8_t parallelism_type;
-    uint8_t chroma_format;
-    uint8_t bit_depth_luma;
-    uint8_t bit_depth_chroma;
-    uint16_t avg_frame_rate;
-
-    uint8_t constant_frame_rate;
-    uint8_t num_temporal_layers;
-    uint8_t temporal_id_nested;
-  };
-
 
   std::string dump(Indent&) const override;
 
+  const char* debug_box_name() const override { return "HEVC Configuration Item"; }
+
   bool get_headers(std::vector<uint8_t>* dest) const;
 
-  void set_configuration(const configuration& config) { m_configuration = config; }
+  void set_configuration(const HEVCDecoderConfigurationRecord& config) { m_configuration = config; }
+
+  const HEVCDecoderConfigurationRecord& get_configuration() const { return m_configuration; }
 
-  const configuration& get_configuration() const { return m_configuration; }
+  HEVCDecoderConfigurationRecord& get_configuration() { return m_configuration; }
 
   void append_nal_data(const std::vector<uint8_t>& nal);
 
@@ -89,20 +123,21 @@ protected:
   Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
 
 private:
-  struct NalArray
-  {
-    uint8_t m_array_completeness;
-    uint8_t m_NAL_unit_type;
-
-    std::vector<std::vector<uint8_t> > m_nal_units;
-  };
+  HEVCDecoderConfigurationRecord m_configuration;
+};
 
-  configuration m_configuration;
-  uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths
 
-  std::vector<NalArray> m_nal_array;
+class Box_hvc1 : public Box_VisualSampleEntry
+{
+public:
+  Box_hvc1()
+  {
+    set_short_type(fourcc("hvc1"));
+  }
 };
 
+
+
 class SEIMessage
 {
 public:
@@ -122,7 +157,7 @@ Error decode_hevc_aux_sei_messages(const
 
 
 Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size,
-                                       Box_hvcC::configuration* inout_config,
+                                       HEVCDecoderConfigurationRecord* inout_config,
                                        int* width, int* height);
 
 #endif
diff -pruN 1.19.8-1/libheif/codecs/hevc_enc.cc 1.20.1-1/libheif/codecs/hevc_enc.cc
--- 1.19.8-1/libheif/codecs/hevc_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/hevc_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,129 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hevc_enc.h"
+#include "hevc_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+
+#include <string>
+
+
+Result<Encoder::CodedImageData> Encoder_HEVC::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                     struct heif_encoder* encoder,
+                                                     const struct heif_encoding_options& options,
+                                                     enum heif_image_input_class input_class)
+{
+  CodedImageData codedImage;
+
+  auto hvcC = std::make_shared<Box_hvcC>();
+
+  heif_image c_api_image;
+  c_api_image.image = image;
+
+  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
+  if (err.code) {
+    return Error(err.code,
+                 err.subcode,
+                 err.message);
+  }
+
+  int encoded_width = 0;
+  int encoded_height = 0;
+
+  for (;;) {
+    uint8_t* data;
+    int size;
+
+    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
+
+    if (data == nullptr) {
+      break;
+    }
+
+
+    const uint8_t NAL_SPS = 33;
+
+    if ((data[0] >> 1) == NAL_SPS) {
+      parse_sps_for_hvcC_configuration(data, size, &hvcC->get_configuration(), &encoded_width, &encoded_height);
+
+      codedImage.encoded_image_width = encoded_width;
+      codedImage.encoded_image_height = encoded_height;
+    }
+
+    switch (data[0] >> 1) {
+      case 0x20:
+      case 0x21:
+      case 0x22:
+        hvcC->append_nal_data(data, size);
+        break;
+
+      default:
+        codedImage.append_with_4bytes_size(data, size);
+        // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size);
+    }
+  }
+
+  if (!encoded_width || !encoded_height) {
+    return Error(heif_error_Encoder_plugin_error,
+                 heif_suberror_Invalid_image_size);
+  }
+
+  codedImage.properties.push_back(hvcC);
+
+
+  // Make sure that the encoder plugin works correctly and the encoded image has the correct size.
+
+  if (encoder->plugin->plugin_api_version >= 3 &&
+      encoder->plugin->query_encoded_size != nullptr) {
+    uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height();
+
+    encoder->plugin->query_encoded_size(encoder->encoder,
+                                        image->get_width(), image->get_height(),
+                                        &check_encoded_width,
+                                        &check_encoded_height);
+
+    assert((int)check_encoded_width == encoded_width);
+    assert((int)check_encoded_height == encoded_height);
+  }
+
+  codedImage.codingConstraints.intra_pred_used = true;
+  codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames
+
+  return codedImage;
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_HEVC::get_sample_description_box(const CodedImageData& data) const
+{
+  auto hvc1 = std::make_shared<Box_hvc1>();
+  hvc1->get_VisualSampleEntry().compressorname = "HEVC";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("hvcC")) {
+      hvc1->append_child_box(prop);
+      return hvc1;
+    }
+  }
+
+  assert(false); // no hvcC generated
+  return nullptr;
+}
diff -pruN 1.19.8-1/libheif/codecs/hevc_enc.h 1.20.1-1/libheif/codecs/hevc_enc.h
--- 1.19.8-1/libheif/codecs/hevc_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/hevc_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_HEVC_H
+#define HEIF_ENCODER_HEVC_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_HEVC : public Encoder {
+public:
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/jpeg2000_boxes.h 1.20.1-1/libheif/codecs/jpeg2000_boxes.h
--- 1.19.8-1/libheif/codecs/jpeg2000_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg2000_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -516,4 +516,14 @@ private:
     size_t cursor = 0;
 };
 
+
+class Box_j2ki : public Box_VisualSampleEntry
+{
+public:
+  Box_j2ki()
+  {
+    set_short_type(fourcc("j2ki"));
+  }
+};
+
 #endif // LIBHEIF_JPEG2000_BOXES_H
diff -pruN 1.19.8-1/libheif/codecs/jpeg2000_enc.cc 1.20.1-1/libheif/codecs/jpeg2000_enc.cc
--- 1.19.8-1/libheif/codecs/jpeg2000_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg2000_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,105 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "jpeg2000_enc.h"
+#include "jpeg2000_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+
+#include <string>
+
+
+Result<Encoder::CodedImageData> Encoder_JPEG2000::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                         struct heif_encoder* encoder,
+                                                         const struct heif_encoding_options& options,
+                                                         enum heif_image_input_class input_class)
+{
+  Encoder::CodedImageData codedImageData;
+
+  heif_image c_api_image;
+  c_api_image.image = image;
+
+  encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
+
+  // get compressed data
+  for (;;) {
+    uint8_t* data;
+    int size;
+
+    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
+
+    if (data == nullptr) {
+      break;
+    }
+
+    codedImageData.append(data, size);
+  }
+
+  // add 'j2kH' property
+  auto j2kH = std::make_shared<Box_j2kH>();
+
+  // add 'cdef' to 'j2kH'
+  auto cdef = std::make_shared<Box_cdef>();
+  cdef->set_channels(image->get_colorspace());
+  j2kH->append_child_box(cdef);
+
+  codedImageData.properties.push_back(j2kH);
+
+  codedImageData.codingConstraints.intra_pred_used = false;
+  codedImageData.codingConstraints.all_ref_pics_intra = true;
+  codedImageData.codingConstraints.max_ref_per_pic = 0;
+
+  return codedImageData;
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_JPEG2000::get_sample_description_box(const CodedImageData& data) const
+{
+  auto j2ki = std::make_shared<Box_j2ki>();
+  j2ki->get_VisualSampleEntry().compressorname = "JPEG2000";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("j2kH")) {
+      j2ki->append_child_box(prop);
+      return j2ki;
+    }
+  }
+
+  assert(false); // no hvcC generated
+  return nullptr;
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_HTJ2K::get_sample_description_box(const CodedImageData& data) const
+{
+  auto j2ki = std::make_shared<Box_j2ki>();
+  j2ki->get_VisualSampleEntry().compressorname = "HTJ2K";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("j2kH")) {
+      j2ki->append_child_box(prop);
+      return j2ki;
+    }
+  }
+
+  assert(false); // no hvcC generated
+  return nullptr;
+}
diff -pruN 1.19.8-1/libheif/codecs/jpeg2000_enc.h 1.20.1-1/libheif/codecs/jpeg2000_enc.h
--- 1.19.8-1/libheif/codecs/jpeg2000_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg2000_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,53 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_JPEG2000_H
+#define HEIF_ENCODER_JPEG2000_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_JPEG2000 : public Encoder {
+public:
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+class Encoder_HTJ2K : public Encoder_JPEG2000 {
+public:
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/jpeg_boxes.cc 1.20.1-1/libheif/codecs/jpeg_boxes.cc
--- 1.19.8-1/libheif/codecs/jpeg_boxes.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg_boxes.cc	2025-07-02 13:05:31.000000000 +0000
@@ -76,4 +76,3 @@ Error Box_jpgC::parse(BitstreamRange& ra
   range.read(m_data.data(), nBytes);
   return range.get_error();
 }
-
diff -pruN 1.19.8-1/libheif/codecs/jpeg_boxes.h 1.20.1-1/libheif/codecs/jpeg_boxes.h
--- 1.19.8-1/libheif/codecs/jpeg_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -26,6 +26,7 @@
 #include <vector>
 #include "image-items/image_item.h"
 #include <memory>
+#include "sequences/seq_boxes.h"
 
 
 class Box_jpgC : public Box
@@ -51,4 +52,15 @@ private:
   std::vector<uint8_t> m_data;
 };
 
+
+class Box_mjpg : public Box_VisualSampleEntry
+{
+public:
+  Box_mjpg()
+  {
+    set_short_type(fourcc("mjpg"));
+  }
+};
+
+
 #endif // LIBHEIF_JPEG_BOXES_H
diff -pruN 1.19.8-1/libheif/codecs/jpeg_enc.cc 1.20.1-1/libheif/codecs/jpeg_enc.cc
--- 1.19.8-1/libheif/codecs/jpeg_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,131 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "jpeg_enc.h"
+#include "jpeg_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+#include <cstring>
+
+#include <string>
+
+
+static uint8_t JPEG_SOS = 0xDA;
+
+
+const heif_color_profile_nclx* Encoder_JPEG::get_forced_output_nclx() const
+{
+  // JPEG always uses CCIR-601
+
+  static heif_color_profile_nclx target_heif_nclx;
+  target_heif_nclx.version = 1;
+  target_heif_nclx.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
+  target_heif_nclx.color_primaries = heif_color_primaries_ITU_R_BT_601_6;
+  target_heif_nclx.transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_601_6;
+  target_heif_nclx.full_range_flag = true;
+
+  return &target_heif_nclx;
+}
+
+
+Result<Encoder::CodedImageData> Encoder_JPEG::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                     struct heif_encoder* encoder,
+                                                     const struct heif_encoding_options& options,
+                                                     enum heif_image_input_class input_class)
+{
+  Encoder::CodedImageData codedImage;
+
+
+  heif_image c_api_image;
+  c_api_image.image = image;
+
+  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
+  if (err.code) {
+    return Error(err.code,
+                 err.subcode,
+                 err.message);
+  }
+
+  std::vector<uint8_t> vec;
+
+  for (;;) {
+    uint8_t* data;
+    int size;
+
+    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
+
+    if (data == nullptr) {
+      break;
+    }
+
+    size_t oldsize = vec.size();
+    vec.resize(oldsize + size);
+    memcpy(vec.data() + oldsize, data, size);
+  }
+
+#if 0
+  // Optional: split the JPEG data into a jpgC box and the actual image data.
+  // Currently disabled because not supported yet in other decoders.
+  if (false) {
+    size_t pos = find_jpeg_marker_start(vec, JPEG_SOS);
+    if (pos > 0) {
+      std::vector<uint8_t> jpgC_data(vec.begin(), vec.begin() + pos);
+      auto jpgC = std::make_shared<Box_jpgC>();
+      jpgC->set_data(jpgC_data);
+
+      auto ipma_box = m_heif_file->get_ipma_box();
+      int index = m_heif_file->get_ipco_box()->find_or_append_child_box(jpgC);
+      ipma_box->add_property_for_item_ID(image_id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
+
+      std::vector<uint8_t> image_data(vec.begin() + pos, vec.end());
+      vec = std::mo ve(image_data);
+    }
+  }
+#endif
+  (void) JPEG_SOS;
+
+  codedImage.bitstream = std::move(vec);
+
+#if 0
+  // TODO: extract 'jpgC' header data
+#endif
+
+  codedImage.codingConstraints.intra_pred_used = false;
+  codedImage.codingConstraints.all_ref_pics_intra = true;
+  codedImage.codingConstraints.max_ref_per_pic = 0;
+
+  return {codedImage};
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_JPEG::get_sample_description_box(const CodedImageData& data) const
+{
+  auto mjpg = std::make_shared<Box_mjpg>();
+  mjpg->get_VisualSampleEntry().compressorname = "JPEG";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("jpgC")) {
+      mjpg->append_child_box(prop);
+    }
+  }
+
+  return mjpg;
+}
diff -pruN 1.19.8-1/libheif/codecs/jpeg_enc.h 1.20.1-1/libheif/codecs/jpeg_enc.h
--- 1.19.8-1/libheif/codecs/jpeg_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/jpeg_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,49 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_JPEG_H
+#define HEIF_ENCODER_JPEG_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_JPEG : public Encoder {
+public:
+  const heif_color_profile_nclx* get_forced_output_nclx() const override;
+
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_abstract.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_abstract.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_abstract.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_abstract.cc	2025-07-02 13:05:31.000000000 +0000
@@ -39,9 +39,11 @@
 #include "unc_boxes.h"
 #include "unc_codec.h"
 #include "decoder_abstract.h"
+#include "codecs/decoder.h"
+#include "codecs/uncompressed/unc_codec.h"
 
 
-AbstractDecoder::AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr<Box_cmpd> cmpd, const std::shared_ptr<Box_uncC> uncC) :
+AbstractDecoder::AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr<const Box_cmpd> cmpd, const std::shared_ptr<const Box_uncC> uncC) :
     m_width(width),
     m_height(height),
     m_cmpd(std::move(cmpd)),
@@ -164,27 +166,28 @@ AbstractDecoder::ChannelListEntry Abstra
 }
 
 
-// generic compression and uncompressed, per 23001-17
-const Error AbstractDecoder::get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID,
+const Error AbstractDecoder::get_compressed_image_data_uncompressed(const DataExtent& dataExtent,
+                                                                    const UncompressedImageCodec::unci_properties& properties,
                                                                     std::vector<uint8_t>* data,
                                                                     uint64_t range_start_offset, uint64_t range_size,
                                                                     uint32_t tile_idx,
                                                                     const Box_iloc::Item* item) const
 {
-  auto image = context->get_image(ID, false);
-  if (!image) {
-    return {heif_error_Invalid_input,
-            heif_suberror_Nonexisting_item_referenced};
-  }
-
   // --- get codec configuration
 
-  std::shared_ptr<const Box_cmpC> cmpC_box = image->get_property<const Box_cmpC>();
-  std::shared_ptr<const Box_icef> icef_box = image->get_property<const Box_icef>();
+  std::shared_ptr<const Box_cmpC> cmpC_box = properties.cmpC;
+  std::shared_ptr<const Box_icef> icef_box = properties.icef;
 
   if (!cmpC_box) {
     // assume no generic compression
-    return context->get_heif_file()->append_data_from_iloc(ID, *data, range_start_offset, range_size);
+    auto readResult = dataExtent.read_data(range_start_offset, range_size);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    data->insert(data->end(), readResult.value.begin(), readResult.value.end());
+
+    return Error::Ok;
   }
 
   if (icef_box && cmpC_box->get_compressed_unit_type() == heif_cmpC_compressed_unit_type_image_tile) {
@@ -197,33 +200,35 @@ const Error AbstractDecoder::get_compres
 
     const auto unit = units[tile_idx];
 
-    // get all data and decode all
-    std::vector<uint8_t> compressed_bytes;
-    Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes, unit.unit_offset, unit.unit_size);
-    if (err) {
-      return err;
+    // get data needed for one tile
+    Result<std::vector<uint8_t>> readingResult = dataExtent.read_data(unit.unit_offset, unit.unit_size);
+    if (readingResult.error) {
+      return readingResult.error;
     }
 
+    const std::vector<uint8_t>& compressed_bytes = readingResult.value;
+
     // decompress only the unit
-    err = do_decompress_data(cmpC_box, compressed_bytes, data);
+    Error err = do_decompress_data(cmpC_box, compressed_bytes, data);
     if (err) {
       return err;
     }
   }
   else if (icef_box) {
     // get all data and decode all
-    std::vector<uint8_t> compressed_bytes;
-    Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size);
-    if (err) {
-      return err;
+    Result<std::vector<uint8_t>*> readResult = dataExtent.read_data();
+    if (readResult.error) {
+      return readResult.error;
     }
 
+    const std::vector<uint8_t>& compressed_bytes = *readResult.value;
+
     for (Box_icef::CompressedUnitInfo unit_info : icef_box->get_units()) {
       auto unit_start = compressed_bytes.begin() + unit_info.unit_offset;
       auto unit_end = unit_start + unit_info.unit_size;
       std::vector<uint8_t> compressed_unit_data = std::vector<uint8_t>(unit_start, unit_end);
       std::vector<uint8_t> uncompressed_unit_data;
-      err = do_decompress_data(cmpC_box, std::move(compressed_unit_data), &uncompressed_unit_data);
+      Error err = do_decompress_data(cmpC_box, std::move(compressed_unit_data), &uncompressed_unit_data);
       if (err) {
         return err;
       }
@@ -236,14 +241,15 @@ const Error AbstractDecoder::get_compres
   }
   else {
     // get all data and decode all
-    std::vector<uint8_t> compressed_bytes;
-    Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size);
-    if (err) {
-      return err;
+    Result<std::vector<uint8_t>*> readResult = dataExtent.read_data();
+    if (readResult.error) {
+      return readResult.error;
     }
 
+    std::vector<uint8_t> compressed_bytes = *readResult.value;
+
     // Decode as a single blob
-    err = do_decompress_data(cmpC_box, compressed_bytes, data);
+    Error err = do_decompress_data(cmpC_box, compressed_bytes, data);
     if (err) {
       return err;
     }
@@ -256,6 +262,7 @@ const Error AbstractDecoder::get_compres
   return Error::Ok;
 }
 
+
 const Error AbstractDecoder::do_decompress_data(std::shared_ptr<const Box_cmpC>& cmpC_box,
                                                 std::vector<uint8_t> compressed_data,
                                                 std::vector<uint8_t>* data) const
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_abstract.h 1.20.1-1/libheif/codecs/uncompressed/decoder_abstract.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_abstract.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_abstract.h	2025-07-02 13:05:31.000000000 +0000
@@ -122,8 +122,8 @@ class AbstractDecoder
 public:
   virtual ~AbstractDecoder() = default;
 
-  virtual Error decode_tile(const HeifContext* context,
-                            heif_item_id item_id,
+  virtual Error decode_tile(const DataExtent& dataExtent,
+                            const UncompressedImageCodec::unci_properties& properties,
                             std::shared_ptr<HeifPixelImage>& img,
                             uint32_t out_x0, uint32_t out_y0,
                             uint32_t image_width, uint32_t image_height,
@@ -133,13 +133,13 @@ public:
 
 protected:
   AbstractDecoder(uint32_t width, uint32_t height,
-                  const std::shared_ptr<Box_cmpd> cmpd,
-                  const std::shared_ptr<Box_uncC> uncC);
+                  const std::shared_ptr<const Box_cmpd> cmpd,
+                  const std::shared_ptr<const Box_uncC> uncC);
 
   const uint32_t m_width;
   const uint32_t m_height;
-  const std::shared_ptr<Box_cmpd> m_cmpd;
-  const std::shared_ptr<Box_uncC> m_uncC;
+  const std::shared_ptr<const Box_cmpd> m_cmpd;
+  const std::shared_ptr<const Box_uncC> m_uncC;
   // TODO: see if we can make this const
   uint32_t m_tile_height;
   uint32_t m_tile_width;
@@ -191,7 +191,8 @@ protected:
   void processComponentTileRow(ChannelListEntry& entry, UncompressedBitReader& srcBits, uint64_t dst_offset);
 
   // generic compression and uncompressed, per 23001-17
-  const Error get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID,
+  const Error get_compressed_image_data_uncompressed(const DataExtent& dataExtent,
+                                                     const UncompressedImageCodec::unci_properties& properties,
                                                      std::vector<uint8_t>* data,
                                                      uint64_t range_start_offset, uint64_t range_size,
                                                      uint32_t tile_idx,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_component_interleave.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_component_interleave.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_component_interleave.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_component_interleave.cc	2025-07-02 13:05:31.000000000 +0000
@@ -26,8 +26,8 @@
 #include <vector>
 
 
-Error ComponentInterleaveDecoder::decode_tile(const HeifContext* context,
-                                              heif_item_id image_id,
+Error ComponentInterleaveDecoder::decode_tile(const DataExtent& dataExtent,
+                                              const UncompressedImageCodec::unci_properties& properties,
                                               std::shared_ptr<HeifPixelImage>& img,
                                               uint32_t out_x0, uint32_t out_y0,
                                               uint32_t image_width, uint32_t image_height,
@@ -68,7 +68,7 @@ Error ComponentInterleaveDecoder::decode
 
   std::vector<uint8_t> src_data;
   //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size);
-  Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
+  Error err = get_compressed_image_data_uncompressed(dataExtent, properties, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
   if (err) {
     return err;
   }
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_component_interleave.h 1.20.1-1/libheif/codecs/uncompressed/decoder_component_interleave.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_component_interleave.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_component_interleave.h	2025-07-02 13:05:31.000000000 +0000
@@ -30,12 +30,12 @@ class ComponentInterleaveDecoder : publi
 {
 public:
   ComponentInterleaveDecoder(uint32_t width, uint32_t height,
-                             std::shared_ptr<Box_cmpd> cmpd,
-                             std::shared_ptr<Box_uncC> uncC) :
+                             std::shared_ptr<const Box_cmpd> cmpd,
+                             std::shared_ptr<const Box_uncC> uncC) :
       AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {}
 
-  Error decode_tile(const HeifContext* context,
-                    heif_item_id image_id,
+  Error decode_tile(const DataExtent& dataExtent,
+                    const UncompressedImageCodec::unci_properties& properties,
                     std::shared_ptr<HeifPixelImage>& img,
                     uint32_t out_x0, uint32_t out_y0,
                     uint32_t image_width, uint32_t image_height,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_mixed_interleave.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_mixed_interleave.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_mixed_interleave.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_mixed_interleave.cc	2025-07-02 13:05:31.000000000 +0000
@@ -27,8 +27,8 @@
 #include <vector>
 
 
-Error MixedInterleaveDecoder::decode_tile(const HeifContext* context,
-                                          heif_item_id image_id,
+Error MixedInterleaveDecoder::decode_tile(const DataExtent& dataExtent,
+                                          const UncompressedImageCodec::unci_properties& properties,
                                           std::shared_ptr<HeifPixelImage>& img,
                                           uint32_t out_x0, uint32_t out_y0,
                                           uint32_t image_width, uint32_t image_height,
@@ -77,7 +77,7 @@ Error MixedInterleaveDecoder::decode_til
   // --- read required file range
 
   std::vector<uint8_t> src_data;
-  Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, tile_size, tileIdx, nullptr);
+  Error err = get_compressed_image_data_uncompressed(dataExtent, properties, &src_data, tile_start_offset, tile_size, tileIdx, nullptr);
   //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, tile_size);
   if (err) {
     return err;
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_mixed_interleave.h 1.20.1-1/libheif/codecs/uncompressed/decoder_mixed_interleave.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_mixed_interleave.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_mixed_interleave.h	2025-07-02 13:05:31.000000000 +0000
@@ -29,11 +29,11 @@
 class MixedInterleaveDecoder : public AbstractDecoder
 {
 public:
-  MixedInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr<Box_cmpd> cmpd, std::shared_ptr<Box_uncC> uncC) :
+  MixedInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr<const Box_cmpd> cmpd, std::shared_ptr<const Box_uncC> uncC) :
       AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {}
 
-  Error decode_tile(const HeifContext* context,
-                    heif_item_id image_id,
+  Error decode_tile(const DataExtent& dataExtent,
+                    const UncompressedImageCodec::unci_properties& properties,
                     std::shared_ptr<HeifPixelImage>& img,
                     uint32_t out_x0, uint32_t out_y0,
                     uint32_t image_width, uint32_t image_height,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_pixel_interleave.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_pixel_interleave.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_pixel_interleave.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_pixel_interleave.cc	2025-07-02 13:05:31.000000000 +0000
@@ -26,8 +26,8 @@
 #include <vector>
 
 
-Error PixelInterleaveDecoder::decode_tile(const HeifContext* context,
-                                          heif_item_id image_id,
+Error PixelInterleaveDecoder::decode_tile(const DataExtent& dataExtent,
+                                          const UncompressedImageCodec::unci_properties& properties,
                                           std::shared_ptr<HeifPixelImage>& img,
                                           uint32_t out_x0, uint32_t out_y0,
                                           uint32_t image_width, uint32_t image_height,
@@ -82,7 +82,7 @@ Error PixelInterleaveDecoder::decode_til
   // --- read required file range
 
   std::vector<uint8_t> src_data;
-  Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
+  Error err = get_compressed_image_data_uncompressed(dataExtent, properties, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
   //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size);
   if (err) {
     return err;
@@ -95,6 +95,7 @@ Error PixelInterleaveDecoder::decode_til
   return Error::Ok;
 }
 
+
 void PixelInterleaveDecoder::processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0)
 {
   for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) {
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_pixel_interleave.h 1.20.1-1/libheif/codecs/uncompressed/decoder_pixel_interleave.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_pixel_interleave.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_pixel_interleave.h	2025-07-02 13:05:31.000000000 +0000
@@ -48,11 +48,11 @@
 class PixelInterleaveDecoder : public AbstractDecoder
 {
 public:
-  PixelInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr<Box_cmpd> cmpd, std::shared_ptr<Box_uncC> uncC) :
+  PixelInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr<const Box_cmpd> cmpd, std::shared_ptr<const Box_uncC> uncC) :
       AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {}
 
-  Error decode_tile(const HeifContext* context,
-                    heif_item_id image_id,
+  Error decode_tile(const DataExtent& dataExtent,
+                    const UncompressedImageCodec::unci_properties& properties,
                     std::shared_ptr<HeifPixelImage>& img,
                     uint32_t out_x0, uint32_t out_y0,
                     uint32_t image_width, uint32_t image_height,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_row_interleave.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_row_interleave.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_row_interleave.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_row_interleave.cc	2025-07-02 13:05:31.000000000 +0000
@@ -26,8 +26,8 @@
 #include <vector>
 
 
-Error RowInterleaveDecoder::decode_tile(const HeifContext* context,
-                                        heif_item_id image_id,
+Error RowInterleaveDecoder::decode_tile(const DataExtent& dataExtent,
+                                        const UncompressedImageCodec::unci_properties& properties,
                                         std::shared_ptr<HeifPixelImage>& img,
                                         uint32_t out_x0, uint32_t out_y0,
                                         uint32_t image_width, uint32_t image_height,
@@ -83,7 +83,7 @@ Error RowInterleaveDecoder::decode_tile(
   // --- read required file range
 
   std::vector<uint8_t> src_data;
-  Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
+  Error err = get_compressed_image_data_uncompressed(dataExtent, properties, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr);
   //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size);
   if (err) {
     return err;
@@ -96,6 +96,7 @@ Error RowInterleaveDecoder::decode_tile(
   return Error::Ok;
 }
 
+
 void RowInterleaveDecoder::processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0)
 {
   for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) {
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_row_interleave.h 1.20.1-1/libheif/codecs/uncompressed/decoder_row_interleave.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_row_interleave.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_row_interleave.h	2025-07-02 13:05:31.000000000 +0000
@@ -30,11 +30,12 @@ class RowInterleaveDecoder : public Abst
 {
 public:
   RowInterleaveDecoder(uint32_t width, uint32_t height,
-                       std::shared_ptr<Box_cmpd> cmpd, std::shared_ptr<Box_uncC> uncC) :
+                       std::shared_ptr<const Box_cmpd> cmpd, std::shared_ptr<const Box_uncC> uncC) :
       AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {}
 
-  Error decode_tile(const HeifContext* context,
-                    heif_item_id image_id,
+
+  Error decode_tile(const DataExtent& dataExtent,
+                    const UncompressedImageCodec::unci_properties& properties,
                     std::shared_ptr<HeifPixelImage>& img,
                     uint32_t out_x0, uint32_t out_y0,
                     uint32_t image_width, uint32_t image_height,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc 1.20.1-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc	2025-07-02 13:05:31.000000000 +0000
@@ -28,8 +28,8 @@
 #include <vector>
 
 
-Error TileComponentInterleaveDecoder::decode_tile(const HeifContext* context,
-                                                  heif_item_id image_id,
+Error TileComponentInterleaveDecoder::decode_tile(const DataExtent& dataExtent,
+                                                  const UncompressedImageCodec::unci_properties& properties,
                                                   std::shared_ptr<HeifPixelImage>& img,
                                                   uint32_t out_x0, uint32_t out_y0,
                                                   uint32_t image_width, uint32_t image_height,
@@ -104,7 +104,7 @@ Error TileComponentInterleaveDecoder::de
     uint64_t tile_start_offset = component_start_offset + channel_tile_size[entry.channel] * tileIdx;
 
     std::vector<uint8_t> src_data;
-    Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, channel_tile_size[entry.channel], tileIdx, nullptr);
+    Error err = get_compressed_image_data_uncompressed(dataExtent, properties, &src_data, tile_start_offset, channel_tile_size[entry.channel], tileIdx, nullptr);
     //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, channel_tile_size[entry.channel]);
     if (err) {
       return err;
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.h 1.20.1-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.h
--- 1.19.8-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/decoder_tile_component_interleave.h	2025-07-02 13:05:31.000000000 +0000
@@ -30,11 +30,11 @@ class TileComponentInterleaveDecoder : p
 {
 public:
   TileComponentInterleaveDecoder(uint32_t width, uint32_t height,
-                                 std::shared_ptr<Box_cmpd> cmpd, std::shared_ptr<Box_uncC> uncC) :
+                                 std::shared_ptr<const Box_cmpd> cmpd, std::shared_ptr<const Box_uncC> uncC) :
       AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) {}
 
-  Error decode_tile(const HeifContext* context,
-                    heif_item_id image_id,
+  Error decode_tile(const DataExtent& dataExtent,
+                    const UncompressedImageCodec::unci_properties& properties,
                     std::shared_ptr<HeifPixelImage>& img,
                     uint32_t out_x0, uint32_t out_y0,
                     uint32_t image_width, uint32_t image_height,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_boxes.h 1.20.1-1/libheif/codecs/uncompressed/unc_boxes.h
--- 1.19.8-1/libheif/codecs/uncompressed/unc_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -25,6 +25,7 @@
 #include "box.h"
 #include "bitstream.h"
 #include "unc_types.h"
+#include "sequences/seq_boxes.h"
 
 #include <cstdint>
 #include <string>
@@ -364,4 +365,15 @@ protected:
   std::vector<PatternComponent> m_components;
 };
 
+
+class Box_uncv : public Box_VisualSampleEntry
+{
+public:
+  Box_uncv()
+  {
+    set_short_type(fourcc("uncv"));
+  }
+};
+
+
 #endif //LIBHEIF_UNC_BOXES_H
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_codec.cc 1.20.1-1/libheif/codecs/uncompressed/unc_codec.cc
--- 1.19.8-1/libheif/codecs/uncompressed/unc_codec.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_codec.cc	2025-07-02 13:05:31.000000000 +0000
@@ -445,7 +445,7 @@ bool map_uncompressed_component_to_chann
 
 
 
-static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr<Box_cmpd>& cmpd, const std::shared_ptr<Box_uncC>& uncC)
+static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr<const Box_cmpd>& cmpd, const std::shared_ptr<const Box_uncC>& uncC)
 {
   switch (uncC->get_interleave_type()) {
     case interleave_mode_component:
@@ -521,9 +521,12 @@ Error UncompressedImageCodec::decode_unc
             heif_suberror_Nonexisting_item_referenced};
   }
 
-  std::shared_ptr<Box_ispe> ispe = image->get_property<Box_ispe>();
-  std::shared_ptr<Box_cmpd> cmpd = image->get_property<Box_cmpd>();
-  std::shared_ptr<Box_uncC> uncC = image->get_property<Box_uncC>();
+  UncompressedImageCodec::unci_properties properties;
+  properties.fill_from_image_item(image);
+
+  auto ispe = properties.ispe;
+  auto uncC = properties.uncC;
+  auto cmpd = properties.cmpd;
 
   Error error = check_header_validity(ispe, cmpd, uncC);
   if (error) {
@@ -552,7 +555,10 @@ Error UncompressedImageCodec::decode_unc
 
   decoder->buildChannelList(img);
 
-  Error result = decoder->decode_tile(context, ID, img, 0, 0,
+  DataExtent dataExtent;
+  dataExtent.set_from_image_item(file, ID);
+
+  Error result = decoder->decode_tile(dataExtent, properties, img, 0, 0,
                                       ispe->get_width(), ispe->get_height(),
                                       tile_x0, tile_y0);
   delete decoder;
@@ -615,6 +621,7 @@ Error UncompressedImageCodec::check_head
 }
 
 
+// TODO: this should be deprecated and replaced with the function taking unci_properties/DataExtent
 Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context,
                                                         heif_item_id ID,
                                                         std::shared_ptr<HeifPixelImage>& img)
@@ -633,9 +640,12 @@ Error UncompressedImageCodec::decode_unc
             heif_suberror_Nonexisting_item_referenced};
   }
 
-  std::shared_ptr<Box_ispe> ispe = image->get_property<Box_ispe>();
-  std::shared_ptr<Box_cmpd> cmpd = image->get_property<Box_cmpd>();
-  std::shared_ptr<Box_uncC> uncC = image->get_property<Box_uncC>();
+  UncompressedImageCodec::unci_properties properties;
+  properties.fill_from_image_item(image);
+
+  auto ispe = properties.ispe;
+  auto uncC = properties.uncC;
+  auto cmpd = properties.cmpd;
 
   error = check_header_validity(ispe, cmpd, uncC);
   if (error) {
@@ -679,9 +689,12 @@ Error UncompressedImageCodec::decode_unc
   uint32_t tile_width = width / uncC->get_number_of_tile_columns();
   uint32_t tile_height = height / uncC->get_number_of_tile_rows();
 
+  DataExtent dataExtent;
+  dataExtent.set_from_image_item(context->get_heif_file(), ID);
+
   for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height)
     for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) {
-      error = decoder->decode_tile(context, ID, img, tile_x0, tile_y0,
+      error = decoder->decode_tile(dataExtent, properties, img, tile_x0, tile_y0,
                                    width, height,
                                    tile_x0 / tile_width, tile_y0 / tile_height);
       if (error) {
@@ -695,6 +708,87 @@ Error UncompressedImageCodec::decode_unc
   return Error::Ok;
 }
 
+
+void UncompressedImageCodec::unci_properties::fill_from_image_item(const std::shared_ptr<const ImageItem>& image)
+{
+  ispe = image->get_property<Box_ispe>();
+  cmpd = image->get_property<Box_cmpd>();
+  uncC = image->get_property<Box_uncC>();
+  cmpC = image->get_property<Box_cmpC>();
+  icef = image->get_property<Box_icef>();
+}
+
+
+Result<std::shared_ptr<HeifPixelImage>>
+UncompressedImageCodec::decode_uncompressed_image(const UncompressedImageCodec::unci_properties& properties,
+                                                  const DataExtent& extent,
+                                                  const heif_security_limits* securityLimits)
+{
+  std::shared_ptr<HeifPixelImage> img;
+
+  const std::shared_ptr<const Box_ispe>& ispe = properties.ispe;
+  const std::shared_ptr<const Box_cmpd>& cmpd = properties.cmpd;
+  const std::shared_ptr<const Box_uncC>& uncC = properties.uncC;
+
+  Error error = check_header_validity(ispe, cmpd, uncC);
+  if (error) {
+    return error;
+  }
+
+  // check if we support the type of image
+
+  error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO
+  if (error) {
+    return error;
+  }
+
+  assert(ispe);
+  uint32_t width = ispe->get_width();
+  uint32_t height = ispe->get_height();
+  error = check_for_valid_image_size(securityLimits, width, height);
+  if (error) {
+    return error;
+  }
+
+  Result<std::shared_ptr<HeifPixelImage>> createImgResult = create_image(cmpd, uncC, width, height, securityLimits);
+  if (createImgResult.error) {
+    return createImgResult.error;
+  }
+  else {
+    img = *createImgResult;
+  }
+
+  AbstractDecoder* decoder = makeDecoder(width, height, cmpd, uncC);
+  if (decoder == nullptr) {
+    std::stringstream sstr;
+    sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet";
+    return Error(heif_error_Unsupported_feature,
+                 heif_suberror_Unsupported_data_version,
+                 sstr.str());
+  }
+
+  decoder->buildChannelList(img);
+
+  uint32_t tile_width = width / uncC->get_number_of_tile_columns();
+  uint32_t tile_height = height / uncC->get_number_of_tile_rows();
+
+  for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height)
+    for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) {
+      error = decoder->decode_tile(extent, properties, img, tile_x0, tile_y0,
+                                   width, height,
+                                   tile_x0 / tile_width, tile_y0 / tile_height);
+      if (error) {
+        delete decoder;
+        return error;
+      }
+    }
+
+  //Error result = decoder->decode(source_data, img);
+  delete decoder;
+  return img;
+}
+
+
 Error fill_cmpd_and_uncC(std::shared_ptr<Box_cmpd>& cmpd,
                          std::shared_ptr<Box_uncC>& uncC,
                          const std::shared_ptr<const HeifPixelImage>& image,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_codec.h 1.20.1-1/libheif/codecs/uncompressed/unc_codec.h
--- 1.19.8-1/libheif/codecs/uncompressed/unc_codec.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_codec.h	2025-07-02 13:05:31.000000000 +0000
@@ -25,6 +25,11 @@
 #include "pixelimage.h"
 #include "file.h"
 #include "context.h"
+#include "libheif/heif_uncompressed.h"
+
+#if WITH_UNCOMPRESSED_CODEC
+#include "unc_boxes.h"
+#endif
 
 #include <cstdint>
 #include <string>
@@ -60,6 +65,22 @@ public:
                                               std::shared_ptr<HeifPixelImage>& img,
                                               uint32_t tile_x0, uint32_t tile_y0);
 
+  struct unci_properties {
+    std::shared_ptr<const Box_ispe> ispe;
+    std::shared_ptr<const Box_cmpd> cmpd;
+    std::shared_ptr<const Box_uncC> uncC;
+    std::shared_ptr<const Box_cmpC> cmpC;
+    std::shared_ptr<const Box_icef> icef;
+    // ...
+
+    void fill_from_image_item(const std::shared_ptr<const ImageItem>&);
+  };
+
+  static Result<std::shared_ptr<HeifPixelImage>> decode_uncompressed_image(const unci_properties& properties,
+                                                                           const struct DataExtent& extent,
+                                                                           const heif_security_limits*);
+
+
   static Error get_heif_chroma_uncompressed(const std::shared_ptr<const Box_uncC>& uncC,
                                             const std::shared_ptr<const Box_cmpd>& cmpd,
                                             heif_chroma* out_chroma,
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_dec.cc 1.20.1-1/libheif/codecs/uncompressed/unc_dec.cc
--- 1.19.8-1/libheif/codecs/uncompressed/unc_dec.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_dec.cc	2025-07-02 13:05:31.000000000 +0000
@@ -161,3 +161,25 @@ bool Decoder_uncompressed::has_alpha_com
 
   return has_alpha;
 }
+
+
+Result<std::shared_ptr<HeifPixelImage>>
+Decoder_uncompressed::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options,
+                                                               const struct heif_security_limits* limits)
+{
+  UncompressedImageCodec::unci_properties properties;
+  properties.uncC = m_uncC;
+  properties.cmpd = m_cmpd;
+  properties.ispe = m_ispe;
+
+  auto decodeResult = UncompressedImageCodec::decode_uncompressed_image(properties,
+                                                          get_data_extent(),
+                                                          limits);
+
+  if (decodeResult.error) {
+    return decodeResult.error;
+  }
+  else {
+    return decodeResult.value;
+  }
+}
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_dec.h 1.20.1-1/libheif/codecs/uncompressed/unc_dec.h
--- 1.19.8-1/libheif/codecs/uncompressed/unc_dec.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_dec.h	2025-07-02 13:05:31.000000000 +0000
@@ -37,7 +37,8 @@ class Decoder_uncompressed : public Deco
 {
 public:
   explicit Decoder_uncompressed(const std::shared_ptr<const Box_uncC>& uncC,
-                                const std::shared_ptr<const Box_cmpd>& cmpd) : m_uncC(uncC), m_cmpd(cmpd) {}
+                                const std::shared_ptr<const Box_cmpd>& cmpd,
+                                const std::shared_ptr<const Box_ispe>& ispe) : m_uncC(uncC), m_cmpd(cmpd), m_ispe(ispe) {}
 
   heif_compression_format get_compression_format() const override { return heif_compression_uncompressed; }
 
@@ -51,9 +52,14 @@ public:
 
   Result<std::vector<uint8_t>> read_bitstream_configuration_data() const override;
 
+  Result<std::shared_ptr<HeifPixelImage>>
+  decode_single_frame_from_compressed_data(const struct heif_decoding_options& options,
+                                           const struct heif_security_limits* limits) override;
+
 private:
   const std::shared_ptr<const Box_uncC> m_uncC;
   const std::shared_ptr<const Box_cmpd> m_cmpd;
+  const std::shared_ptr<const Box_ispe> m_ispe;
 };
 
 #endif
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_enc.cc 1.20.1-1/libheif/codecs/uncompressed/unc_enc.cc
--- 1.19.8-1/libheif/codecs/uncompressed/unc_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,74 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "unc_enc.h"
+#include "unc_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+#include <cstring>
+#include <image-items/unc_image.h>
+
+#include <string>
+
+
+Result<Encoder::CodedImageData> Encoder_uncompressed::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                     struct heif_encoder* encoder,
+                                                     const struct heif_encoding_options& options,
+                                                     enum heif_image_input_class input_class)
+{
+  Encoder::CodedImageData codedImage;
+
+  Result<Encoder::CodedImageData> codingResult = ImageItem_uncompressed::encode_static(image, options);
+  if (codingResult.error) {
+    return codingResult;
+  }
+
+  codedImage = std::move(codingResult.value);
+
+  // codedImage.bitstream = std::move(vec);
+
+  codedImage.codingConstraints.intra_pred_used = false;
+  codedImage.codingConstraints.all_ref_pics_intra = true;
+  codedImage.codingConstraints.max_ref_per_pic = 0;
+
+  return {codedImage};
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_uncompressed::get_sample_description_box(const CodedImageData& data) const
+{
+  auto uncv = std::make_shared<Box_uncv>();
+  uncv->get_VisualSampleEntry().compressorname = "iso23001-17";
+
+  for (auto prop : data.properties) {
+    switch (prop->get_short_type()) {
+      case fourcc("cmpd"):
+      case fourcc("uncC"):
+      case fourcc("cmpC"):
+      case fourcc("icef"):
+      case fourcc("cpat"):
+        uncv->append_child_box(prop);
+      break;
+    }
+  }
+
+  return uncv;
+}
diff -pruN 1.19.8-1/libheif/codecs/uncompressed/unc_enc.h 1.20.1-1/libheif/codecs/uncompressed/unc_enc.h
--- 1.19.8-1/libheif/codecs/uncompressed/unc_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/uncompressed/unc_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_UNCOMPRESSED_H
+#define HEIF_ENCODER_UNCOMPRESSED_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_uncompressed : public Encoder {
+public:
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/codecs/vvc_boxes.h 1.20.1-1/libheif/codecs/vvc_boxes.h
--- 1.19.8-1/libheif/codecs/vvc_boxes.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/codecs/vvc_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -26,6 +26,7 @@
 #include <vector>
 #include "image-items/image_item.h"
 #include <memory>
+#include "sequences/seq_boxes.h"
 
 
 class Box_vvcC : public FullBox
@@ -101,6 +102,16 @@ private:
 };
 
 
+class Box_vvc1 : public Box_VisualSampleEntry
+{
+public:
+  Box_vvc1()
+  {
+    set_short_type(fourcc("vvc1"));
+  }
+};
+
+
 Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size,
                                        Box_vvcC::configuration* inout_config,
                                        int* width, int* height);
diff -pruN 1.19.8-1/libheif/codecs/vvc_enc.cc 1.20.1-1/libheif/codecs/vvc_enc.cc
--- 1.19.8-1/libheif/codecs/vvc_enc.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/vvc_enc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,114 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "vvc_enc.h"
+#include "vvc_boxes.h"
+#include "error.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+
+#include <string>
+
+
+Result<Encoder::CodedImageData> Encoder_VVC::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                    struct heif_encoder* encoder,
+                                                    const struct heif_encoding_options& options,
+                                                    enum heif_image_input_class input_class)
+{
+  Encoder::CodedImageData codedImage;
+
+  auto vvcC = std::make_shared<Box_vvcC>();
+  codedImage.properties.push_back(vvcC);
+
+
+  heif_image c_api_image;
+  c_api_image.image = image;
+
+  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
+  if (err.code) {
+    return Error(err.code,
+                 err.subcode,
+                 err.message);
+  }
+
+  int encoded_width = 0;
+  int encoded_height = 0;
+
+  for (;;) {
+    uint8_t* data;
+    int size;
+
+    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, NULL);
+
+    if (data == NULL) {
+      break;
+    }
+
+
+    const uint8_t NAL_SPS = 15;
+
+    uint8_t nal_type = 0;
+    if (size>=2) {
+      nal_type = (data[1] >> 3) & 0x1F;
+    }
+
+    if (nal_type == NAL_SPS) {
+      Box_vvcC::configuration config;
+
+      parse_sps_for_vvcC_configuration(data, size, &config, &encoded_width, &encoded_height);
+
+      vvcC->set_configuration(config);
+    }
+
+    switch (nal_type) {
+      case 14: // VPS
+      case 15: // SPS
+      case 16: // PPS
+        vvcC->append_nal_data(data, size);
+        break;
+
+      default:
+        codedImage.append_with_4bytes_size(data, size);
+    }
+  }
+
+  codedImage.codingConstraints.intra_pred_used = true;
+  codedImage.codingConstraints.all_ref_pics_intra = true; // TODO: change when we use predicted frames
+  codedImage.codingConstraints.max_ref_per_pic = 0;
+
+  return codedImage;
+}
+
+
+std::shared_ptr<class Box_VisualSampleEntry> Encoder_VVC::get_sample_description_box(const CodedImageData& data) const
+{
+  auto vvc1 = std::make_shared<Box_vvc1>();
+  vvc1->get_VisualSampleEntry().compressorname = "VVC";
+
+  for (auto prop : data.properties) {
+    if (prop->get_short_type() == fourcc("vvcC")) {
+      vvc1->append_child_box(prop);
+      return vvc1;
+    }
+  }
+
+  assert(false); // no hvcC generated
+  return nullptr;
+}
diff -pruN 1.19.8-1/libheif/codecs/vvc_enc.h 1.20.1-1/libheif/codecs/vvc_enc.h
--- 1.19.8-1/libheif/codecs/vvc_enc.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/codecs/vvc_enc.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,47 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEIF_ENCODER_VVC_H
+#define HEIF_ENCODER_VVC_H
+
+#include "libheif/heif.h"
+#include "box.h"
+#include "error.h"
+#include "file.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "codecs/encoder.h"
+
+
+class Encoder_VVC : public Encoder {
+public:
+  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                struct heif_encoder* encoder,
+                                const struct heif_encoding_options& options,
+                                enum heif_image_input_class input_class) override;
+
+  std::shared_ptr<class Box_VisualSampleEntry> get_sample_description_box(const CodedImageData&) const override;
+};
+
+
+#endif
diff -pruN 1.19.8-1/libheif/color-conversion/alpha.cc 1.20.1-1/libheif/color-conversion/alpha.cc
--- 1.19.8-1/libheif/color-conversion/alpha.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/alpha.cc	2025-07-02 13:05:31.000000000 +0000
@@ -18,13 +18,15 @@
  * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <cstdint>
 #include "alpha.h"
 
 
 std::vector<ColorStateWithCost>
 Op_drop_alpha_plane::state_after_conversion(const ColorState& input_state,
                                             const ColorState& target_state,
-                                            const heif_color_conversion_options& options) const
+                                            const heif_color_conversion_options& options,
+                                            const heif_color_conversion_options_ext& options_ext) const
 {
   // only drop alpha plane if it is not needed in output
 
@@ -37,6 +39,10 @@ Op_drop_alpha_plane::state_after_convers
     return {};
   }
 
+  if (options_ext.alpha_composition_mode != heif_alpha_composition_mode_none) {
+    return {};
+  }
+
   std::vector<ColorStateWithCost> states;
 
   ColorState output_state;
@@ -57,6 +63,7 @@ Op_drop_alpha_plane::convert_colorspace(
                                         const ColorState& input_state,
                                         const ColorState& target_state,
                                         const heif_color_conversion_options& options,
+                                        const heif_color_conversion_options_ext& options_ext,
                                         const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
@@ -81,3 +88,202 @@ Op_drop_alpha_plane::convert_colorspace(
 
   return outimg;
 }
+
+
+template<class Pixel>
+std::vector<ColorStateWithCost>
+Op_flatten_alpha_plane<Pixel>::state_after_conversion(const ColorState& input_state,
+                                                      const ColorState& target_state,
+                                                      const heif_color_conversion_options& options,
+                                                      const heif_color_conversion_options_ext& options_ext) const
+{
+  bool hdr = !std::is_same<Pixel, uint8_t>::value;
+
+  // TODO: this Op only works when all channels are either HDR or all are SDR.
+  //       But there is currently no easy way to check that.
+
+  if ((input_state.bits_per_pixel > 8) != hdr) {
+    return {};
+  }
+
+  // only drop alpha plane if it is not needed in output
+
+  if ((input_state.chroma != heif_chroma_monochrome &&
+       input_state.chroma != heif_chroma_420 &&
+       input_state.chroma != heif_chroma_422 &&
+       input_state.chroma != heif_chroma_444) ||
+      input_state.has_alpha == false ||
+      target_state.has_alpha == true) {
+    return {};
+  }
+
+  if (options_ext.alpha_composition_mode == heif_alpha_composition_mode_none) {
+    return {};
+  }
+
+  std::vector<ColorStateWithCost> states;
+
+  ColorState output_state;
+
+  // --- drop alpha plane
+
+  output_state = input_state;
+  output_state.has_alpha = false;
+
+  states.emplace_back(output_state, SpeedCosts_Trivial);
+
+  return states;
+}
+
+
+template<class Pixel>
+Result<std::shared_ptr<HeifPixelImage>>
+Op_flatten_alpha_plane<Pixel>::convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input_raw,
+                                                  const ColorState& input_state,
+                                                  const ColorState& target_state,
+                                                  const heif_color_conversion_options& options,
+                                                  const heif_color_conversion_options_ext& options_ext,
+                                                  const heif_security_limits* limits) const
+{
+  std::shared_ptr<const HeifPixelImage> input = input_raw;
+
+  std::shared_ptr<color_profile_nclx> nclx = std::make_shared<color_profile_nclx>();
+  *nclx = input_state.nclx_profile;
+  heif_color_conversion_options_ext options_ext_skip_alpha = options_ext;
+  options_ext_skip_alpha.alpha_composition_mode = heif_alpha_composition_mode_none;
+
+  if (options_ext.alpha_composition_mode != heif_alpha_composition_mode_none) {
+    Result<std::shared_ptr<const HeifPixelImage>> convInput = ::convert_colorspace(input,
+                                                                                   heif_colorspace_RGB,
+                                                                                   heif_chroma_444,
+                                                                                   nclx,
+                                                                                   input_state.bits_per_pixel,
+                                                                                   options, &options_ext_skip_alpha,
+                                                                                   limits);
+    if (convInput.error) {
+      return convInput.error;
+    }
+    else {
+      input = convInput.value;
+    }
+  }
+
+  uint32_t width = input->get_width();
+  uint32_t height = input->get_height();
+
+  auto outimg = std::make_shared<HeifPixelImage>();
+
+  outimg->create(width, height,
+                 input->get_colorspace(),
+                 input->get_chroma_format());
+
+  for (heif_channel channel : {heif_channel_R,
+                               heif_channel_G,
+                               heif_channel_B}) {
+    outimg->add_plane(channel, width, height, target_state.bits_per_pixel, limits);
+
+    const Pixel* p_alpha;
+    size_t stride_alpha;
+    p_alpha = (const Pixel*)input->get_plane(heif_channel_Alpha, &stride_alpha);
+    int bpp_alpha = input->get_bits_per_pixel(heif_channel_Alpha);
+    Pixel alpha_max = (Pixel)((1 << bpp_alpha) - 1);
+
+    const Pixel* p_in;
+    size_t stride_in;
+    p_in = (const Pixel*)input->get_plane(channel, &stride_in);
+
+    Pixel* p_out;
+    size_t stride_out;
+    p_out = (Pixel*)outimg->get_plane(channel, &stride_out);
+
+    if (sizeof(Pixel) == 2) {
+      stride_alpha /= 2;
+      stride_in /= 2;
+      stride_out /= 2;
+    }
+
+    if (options_ext.alpha_composition_mode == heif_alpha_composition_mode_solid_color ||
+        (options_ext.alpha_composition_mode == heif_alpha_composition_mode_checkerboard && options_ext.checkerboard_square_size == 0)) {
+      uint16_t bkg16;
+
+      switch (channel) {
+        case heif_channel_R:
+          bkg16 = options_ext.background_red;
+          break;
+        case heif_channel_G:
+          bkg16 = options_ext.background_green;
+          break;
+        case heif_channel_B:
+          bkg16 = options_ext.background_blue;
+          break;
+        default:
+          assert(false);
+          bkg16 = 0;
+      }
+
+      Pixel bkg = static_cast<Pixel>(bkg16 >> (16 - input->get_bits_per_pixel(channel)));
+
+      for (uint32_t y = 0; y < height; y++)
+        for (uint32_t x = 0; x < width; x++) {
+          int a = p_alpha[y * stride_alpha + x];
+          p_out[y * stride_out + x] = static_cast<Pixel>((p_in[y * stride_in + x] * a + bkg * (alpha_max - a)) >> bpp_alpha);
+        }
+    }
+    else {
+      uint16_t bkg16_1, bkg16_2;
+
+      switch (channel) {
+        case heif_channel_R:
+          bkg16_1 = options_ext.background_red;
+          bkg16_2 = options_ext.secondary_background_red;
+          break;
+        case heif_channel_G:
+          bkg16_1 = options_ext.background_green;
+          bkg16_2 = options_ext.secondary_background_green;
+          break;
+        case heif_channel_B:
+          bkg16_1 = options_ext.background_blue;
+          bkg16_2 = options_ext.secondary_background_blue;
+          break;
+        default:
+          assert(false);
+          bkg16_1 = bkg16_2 = 0;
+      }
+
+      Pixel bkg1 = static_cast<Pixel>(bkg16_1 >> (16 - input->get_bits_per_pixel(channel)));
+      Pixel bkg2 = static_cast<Pixel>(bkg16_2 >> (16 - input->get_bits_per_pixel(channel)));
+
+      for (uint32_t y = 0; y < height; y++)
+        for (uint32_t x = 0; x < width; x++) {
+          uint8_t parity = (x / options_ext.checkerboard_square_size + y / options_ext.checkerboard_square_size) % 2;
+          Pixel bkg = parity ? bkg1 : bkg2;
+
+          int a = p_alpha[y * stride_alpha + x];
+          p_out[y * stride_out + x] = static_cast<Pixel>((p_in[y * stride_in + x] * a + bkg * (alpha_max - a)) >> bpp_alpha);
+        }
+    }
+
+  }
+
+  if (options_ext.alpha_composition_mode != heif_alpha_composition_mode_none) {
+    Result<std::shared_ptr<HeifPixelImage>> convOutput = ::convert_colorspace(outimg,
+                                                                              input_raw->get_colorspace(),
+                                                                              input_raw->get_chroma_format(),
+                                                                              nclx,
+                                                                              input_state.bits_per_pixel,
+                                                                              options, &options_ext_skip_alpha,
+                                                                              limits);
+    if (convOutput.error) {
+      return convOutput.error;
+    }
+    else {
+      return convOutput.value;
+    }
+  }
+  else {
+    return outimg;
+  }
+}
+
+template class Op_flatten_alpha_plane<uint8_t>;
+template class Op_flatten_alpha_plane<uint16_t>;
diff -pruN 1.19.8-1/libheif/color-conversion/alpha.h 1.20.1-1/libheif/color-conversion/alpha.h
--- 1.19.8-1/libheif/color-conversion/alpha.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/alpha.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,13 +32,35 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
+                     const heif_security_limits* limits) const override;
+};
+
+
+template<class Pixel>
+class Op_flatten_alpha_plane : public ColorConversionOperation
+{
+public:
+  std::vector<ColorStateWithCost>
+  state_after_conversion(const ColorState& input_state,
+                         const ColorState& target_state,
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
+
+  Result<std::shared_ptr<HeifPixelImage>>
+  convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
+                     const ColorState& input_state,
+                     const ColorState& target_state,
+                     const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/chroma_sampling.cc 1.20.1-1/libheif/color-conversion/chroma_sampling.cc
--- 1.19.8-1/libheif/color-conversion/chroma_sampling.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/chroma_sampling.cc	2025-07-02 13:05:31.000000000 +0000
@@ -26,7 +26,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_YCbCr444_to_YCbCr420_average<Pixel>::state_after_conversion(const ColorState& input_state,
                                                                const ColorState& target_state,
-                                                               const heif_color_conversion_options& options) const
+                                                               const heif_color_conversion_options& options,
+                                                               const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_YCbCr) {
     return {};
@@ -80,6 +81,7 @@ Op_YCbCr444_to_YCbCr420_average<Pixel>::
                                                            const ColorState& input_state,
                                                            const ColorState& target_state,
                                                            const heif_color_conversion_options& options,
+                                                           const heif_color_conversion_options_ext& options_ext,
                                                            const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
@@ -249,7 +251,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_YCbCr444_to_YCbCr422_average<Pixel>::state_after_conversion(const ColorState& input_state,
                                                                const ColorState& target_state,
-                                                               const heif_color_conversion_options& options) const
+                                                               const heif_color_conversion_options& options,
+                                                               const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_YCbCr) {
     return {};
@@ -303,6 +306,7 @@ Op_YCbCr444_to_YCbCr422_average<Pixel>::
                                                            const ColorState& input_state,
                                                            const ColorState& target_state,
                                                            const heif_color_conversion_options& options,
+                                                           const heif_color_conversion_options_ext& options_ext,
                                                            const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
@@ -450,7 +454,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_YCbCr420_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState& input_state,
                                                                 const ColorState& target_state,
-                                                                const heif_color_conversion_options& options) const
+                                                                const heif_color_conversion_options& options,
+                                                                const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_YCbCr) {
     return {};
@@ -500,6 +505,7 @@ Op_YCbCr420_bilinear_to_YCbCr444<Pixel>:
                                                             const ColorState& input_state,
                                                             const ColorState& target_state,
                                                             const heif_color_conversion_options& options,
+                                                            const heif_color_conversion_options_ext& options_ext,
                                                             const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
@@ -730,7 +736,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_YCbCr422_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState& input_state,
                                                                 const ColorState& target_state,
-                                                                const heif_color_conversion_options& options) const
+                                                                const heif_color_conversion_options& options,
+                                                                const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_YCbCr) {
     return {};
@@ -780,6 +787,7 @@ Op_YCbCr422_bilinear_to_YCbCr444<Pixel>:
                                                             const ColorState& input_state,
                                                             const ColorState& target_state,
                                                             const heif_color_conversion_options& options,
+                                                            const heif_color_conversion_options_ext& options_ext,
                                                             const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
diff -pruN 1.19.8-1/libheif/color-conversion/chroma_sampling.h 1.20.1-1/libheif/color-conversion/chroma_sampling.h
--- 1.19.8-1/libheif/color-conversion/chroma_sampling.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/chroma_sampling.h	2025-07-02 13:05:31.000000000 +0000
@@ -35,13 +35,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -53,13 +55,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -73,13 +77,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -90,13 +96,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/colorconversion.cc 1.20.1-1/libheif/color-conversion/colorconversion.cc
--- 1.19.8-1/libheif/color-conversion/colorconversion.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/colorconversion.cc	2025-07-02 13:05:31.000000000 +0000
@@ -238,6 +238,8 @@ void ColorConversionPipeline::init_ops()
   ops.emplace_back(std::make_shared<Op_RRGGBBxx_HDR_to_YCbCr420>());
   ops.emplace_back(std::make_shared<Op_RGB24_32_to_YCbCr444_GBR>());
   ops.emplace_back(std::make_shared<Op_drop_alpha_plane>());
+  ops.emplace_back(std::make_shared<Op_flatten_alpha_plane<uint8_t>>());
+  ops.emplace_back(std::make_shared<Op_flatten_alpha_plane<uint16_t>>());
   ops.emplace_back(std::make_shared<Op_to_hdr_planes>());
   ops.emplace_back(std::make_shared<Op_to_sdr_planes>());
   ops.emplace_back(std::make_shared<Op_YCbCr420_bilinear_to_YCbCr444<uint8_t>>());
@@ -260,11 +262,13 @@ void ColorConversionPipeline::release_op
 
 bool ColorConversionPipeline::construct_pipeline(const ColorState& input_state,
                                                  const ColorState& target_state,
-                                                 const heif_color_conversion_options& options)
+                                                 const heif_color_conversion_options& options,
+                                                 const heif_color_conversion_options_ext& options_ext)
 {
   m_conversion_steps.clear();
 
   m_options = options;
+  m_options_ext = options_ext;
 
   if (input_state == target_state) {
     return true;
@@ -360,7 +364,7 @@ bool ColorConversionPipeline::construct_
 
       auto out_states = op_ptr->state_after_conversion(processed_states.back().output_state,
                                                        target_state,
-                                                       options);
+                                                       options, options_ext);
       for (const auto& out_state : out_states) {
         int new_op_costs = out_state.speed_costs + processed_states.back().speed_costs;
 #if DEBUG_PIPELINE_CREATION
@@ -440,7 +444,7 @@ Result<std::shared_ptr<HeifPixelImage>>
     print_spec(std::cerr, in);
 #endif
 
-    auto outResult = step.operation->convert_colorspace(in, step.input_state, step.output_state, m_options, limits);
+    auto outResult = step.operation->convert_colorspace(in, step.input_state, step.output_state, m_options, m_options_ext, limits);
     if (outResult.error) {
       return outResult.error;
     }
@@ -471,6 +475,16 @@ Result<std::shared_ptr<HeifPixelImage>>
       out->set_pixel_ratio(h, v);
     }
 
+    if (in->has_gimi_sample_content_id()) {
+      out->set_gimi_sample_content_id(in->get_gimi_sample_content_id());
+    }
+
+    if (auto* tai = in->get_tai_timestamp()) {
+      out->set_tai_timestamp(tai);
+    }
+
+    out->set_sample_duration(in->get_sample_duration());
+
     const auto& warnings = in->get_warnings();
     for (const auto& warning : warnings) {
       out->add_warning(warning);
@@ -489,8 +503,15 @@ Result<std::shared_ptr<HeifPixelImage>>
                                                            const std::shared_ptr<const color_profile_nclx>& target_profile,
                                                            int output_bpp,
                                                            const heif_color_conversion_options& options,
+                                                           const heif_color_conversion_options_ext* options_ext_optional,
                                                            const heif_security_limits* limits)
 {
+  std::unique_ptr<heif_color_conversion_options_ext, void(*)(heif_color_conversion_options_ext*)>
+      options_ext(heif_color_conversion_options_ext_alloc(), heif_color_conversion_options_ext_free);
+
+  heif_color_conversion_options_ext_copy(options_ext.get(), options_ext_optional);
+
+
   // --- check that input image is valid
 
   uint32_t width = input->get_width();
@@ -560,7 +581,12 @@ Result<std::shared_ptr<HeifPixelImage>>
     output_state.has_alpha = is_interleaved_with_alpha(target_chroma);
   }
   else {
-    output_state.has_alpha = input_state.has_alpha;
+    if (options_ext->alpha_composition_mode != heif_alpha_composition_mode_none) {
+      output_state.has_alpha = false;
+    }
+    else {
+      output_state.has_alpha = input_state.has_alpha;
+    }
   }
 
   if (output_bpp) {
@@ -587,7 +613,7 @@ Result<std::shared_ptr<HeifPixelImage>>
   }
 
   ColorConversionPipeline pipeline;
-  bool success = pipeline.construct_pipeline(input_state, output_state, options);
+  bool success = pipeline.construct_pipeline(input_state, output_state, options, *options_ext);
   if (!success) {
     return Error{heif_error_Unsupported_feature,
                  heif_suberror_Unsupported_color_conversion};
@@ -608,11 +634,12 @@ Result<std::shared_ptr<const HeifPixelIm
                                                                  const std::shared_ptr<const color_profile_nclx>& target_profile,
                                                                  int output_bpp,
                                                                  const heif_color_conversion_options& options,
+                                                                 const heif_color_conversion_options_ext* options_ext,
                                                                  const heif_security_limits* limits)
 {
   std::shared_ptr<HeifPixelImage> non_const_input = std::const_pointer_cast<HeifPixelImage>(input);
 
-  auto result = convert_colorspace(non_const_input, colorspace, chroma, target_profile, output_bpp, options, limits);
+  auto result = convert_colorspace(non_const_input, colorspace, chroma, target_profile, output_bpp, options, options_ext, limits);
   if (result.error) {
     return result.error;
   }
diff -pruN 1.19.8-1/libheif/color-conversion/colorconversion.h 1.20.1-1/libheif/color-conversion/colorconversion.h
--- 1.19.8-1/libheif/color-conversion/colorconversion.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/colorconversion.h	2025-07-02 13:05:31.000000000 +0000
@@ -83,13 +83,15 @@ public:
   virtual std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const = 0;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const = 0;
 
   virtual Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const = 0;
 };
 
@@ -104,7 +106,8 @@ public:
 
   bool construct_pipeline(const ColorState& input_state,
                           const ColorState& target_state,
-                          const heif_color_conversion_options& options);
+                          const heif_color_conversion_options& options,
+                          const heif_color_conversion_options_ext& options_ext);
 
   Result<std::shared_ptr<HeifPixelImage>> convert_image(const std::shared_ptr<HeifPixelImage>& input,
                                                         const heif_security_limits* limits);
@@ -123,6 +126,7 @@ private:
   std::vector<ConversionStep> m_conversion_steps;
 
   heif_color_conversion_options m_options;
+  heif_color_conversion_options_ext m_options_ext;
 };
 
 
@@ -134,6 +138,7 @@ Result<std::shared_ptr<HeifPixelImage>>
                                                            const std::shared_ptr<const color_profile_nclx>& target_profile,
                                                            int output_bpp,
                                                            const heif_color_conversion_options& options,
+                                                           const heif_color_conversion_options_ext* options_ext,
                                                            const heif_security_limits* limits);
 
 Result<std::shared_ptr<const HeifPixelImage>> convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
@@ -142,6 +147,7 @@ Result<std::shared_ptr<const HeifPixelIm
                                                                  const std::shared_ptr<const color_profile_nclx>& target_profile,
                                                                  int output_bpp,
                                                                  const heif_color_conversion_options& options,
+                                                                 const heif_color_conversion_options_ext* options_ext,
                                                                  const heif_security_limits* limits);
 
 #endif
diff -pruN 1.19.8-1/libheif/color-conversion/hdr_sdr.cc 1.20.1-1/libheif/color-conversion/hdr_sdr.cc
--- 1.19.8-1/libheif/color-conversion/hdr_sdr.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/hdr_sdr.cc	2025-07-02 13:05:31.000000000 +0000
@@ -25,7 +25,8 @@
 std::vector<ColorStateWithCost>
 Op_to_hdr_planes::state_after_conversion(const ColorState& input_state,
                                          const ColorState& target_state,
-                                         const heif_color_conversion_options& options) const
+                                         const heif_color_conversion_options& options,
+                                         const heif_color_conversion_options_ext& options_ext) const
 {
   if ((input_state.chroma != heif_chroma_monochrome &&
        input_state.chroma != heif_chroma_420 &&
@@ -55,6 +56,7 @@ Op_to_hdr_planes::convert_colorspace(con
                                      const ColorState& input_state,
                                      const ColorState& target_state,
                                      const heif_color_conversion_options& options,
+                                     const heif_color_conversion_options_ext& options_ext,
                                      const heif_security_limits* limits) const
 {
   auto outimg = std::make_shared<HeifPixelImage>();
@@ -109,7 +111,8 @@ Op_to_hdr_planes::convert_colorspace(con
 std::vector<ColorStateWithCost>
 Op_to_sdr_planes::state_after_conversion(const ColorState& input_state,
                                          const ColorState& target_state,
-                                         const heif_color_conversion_options& options) const
+                                         const heif_color_conversion_options& options,
+                                         const heif_color_conversion_options_ext& options_ext) const
 {
   if ((input_state.chroma != heif_chroma_monochrome &&
        input_state.chroma != heif_chroma_420 &&
@@ -143,6 +146,7 @@ Op_to_sdr_planes::convert_colorspace(con
                                      const ColorState& input_state,
                                      const ColorState& target_state,
                                      const heif_color_conversion_options& options,
+                                     const heif_color_conversion_options_ext& options_ext,
                                      const heif_security_limits* limits) const
 {
 
diff -pruN 1.19.8-1/libheif/color-conversion/hdr_sdr.h 1.20.1-1/libheif/color-conversion/hdr_sdr.h
--- 1.19.8-1/libheif/color-conversion/hdr_sdr.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/hdr_sdr.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,13 +32,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -49,13 +51,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/monochrome.cc 1.20.1-1/libheif/color-conversion/monochrome.cc
--- 1.19.8-1/libheif/color-conversion/monochrome.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/monochrome.cc	2025-07-02 13:05:31.000000000 +0000
@@ -25,7 +25,8 @@
 std::vector<ColorStateWithCost>
 Op_mono_to_YCbCr420::state_after_conversion(const ColorState& input_state,
                                             const ColorState& target_state,
-                                            const heif_color_conversion_options& options) const
+                                            const heif_color_conversion_options& options,
+                                            const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_monochrome ||
       input_state.chroma != heif_chroma_monochrome) {
@@ -54,6 +55,7 @@ Op_mono_to_YCbCr420::convert_colorspace(
                                         const ColorState& input_state,
                                         const ColorState& target_state,
                                         const heif_color_conversion_options& options,
+                                        const heif_color_conversion_options_ext& options_ext,
                                         const heif_security_limits* limits) const
 {
   auto outimg = std::make_shared<HeifPixelImage>();
@@ -162,7 +164,8 @@ Op_mono_to_YCbCr420::convert_colorspace(
 std::vector<ColorStateWithCost>
 Op_mono_to_RGB24_32::state_after_conversion(const ColorState& input_state,
                                             const ColorState& target_state,
-                                            const heif_color_conversion_options& options) const
+                                            const heif_color_conversion_options& options,
+                                            const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -206,6 +209,7 @@ Op_mono_to_RGB24_32::convert_colorspace(
                                         const ColorState& input_state,
                                         const ColorState& target_state,
                                         const heif_color_conversion_options& options,
+                                        const heif_color_conversion_options_ext& options_ext,
                                         const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
diff -pruN 1.19.8-1/libheif/color-conversion/monochrome.h 1.20.1-1/libheif/color-conversion/monochrome.h
--- 1.19.8-1/libheif/color-conversion/monochrome.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/monochrome.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,13 +32,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -49,13 +51,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2rgb.cc 1.20.1-1/libheif/color-conversion/rgb2rgb.cc
--- 1.19.8-1/libheif/color-conversion/rgb2rgb.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2rgb.cc	2025-07-02 13:05:31.000000000 +0000
@@ -28,7 +28,8 @@
 std::vector<ColorStateWithCost>
 Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state,
                                            const ColorState& target_state,
-                                           const heif_color_conversion_options& options) const
+                                           const heif_color_conversion_options& options,
+                                           const heif_color_conversion_options_ext& options_ext) const
 {
   if (input_state.colorspace != heif_colorspace_RGB ||
       input_state.chroma != heif_chroma_444 ||
@@ -68,6 +69,7 @@ Op_RGB_to_RGB24_32::convert_colorspace(c
                                        const ColorState& input_state,
                                        const ColorState& target_state,
                                        const heif_color_conversion_options& options,
+                                       const heif_color_conversion_options_ext& options_ext,
                                        const heif_security_limits* limits) const
 {
   bool has_alpha = input->has_channel(heif_channel_Alpha);
@@ -147,7 +149,8 @@ Op_RGB_to_RGB24_32::convert_colorspace(c
 std::vector<ColorStateWithCost>
 Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
                                                   const ColorState& target_state,
-                                                  const heif_color_conversion_options& options) const
+                                                  const heif_color_conversion_options& options,
+                                                  const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -192,6 +195,7 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_color
                                               const ColorState& input_state,
                                               const ColorState& target_state,
                                               const heif_color_conversion_options& options,
+                                              const heif_color_conversion_options_ext& options_ext,
                                               const heif_security_limits* limits) const
 {
   if (input->get_bits_per_pixel(heif_channel_R) <= 8 ||
@@ -283,7 +287,8 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_color
 std::vector<ColorStateWithCost>
 Op_RGB_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
                                               const ColorState& target_state,
-                                              const heif_color_conversion_options& options) const
+                                              const heif_color_conversion_options& options,
+                                              const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -327,6 +332,7 @@ Op_RGB_to_RRGGBBaa_BE::convert_colorspac
                                           const ColorState& input_state,
                                           const ColorState& target_state,
                                           const heif_color_conversion_options& options,
+                                          const heif_color_conversion_options_ext& options_ext,
                                           const heif_security_limits* limits) const
 {
   if (input->get_bits_per_pixel(heif_channel_R) != 8 ||
@@ -412,7 +418,8 @@ Op_RGB_to_RRGGBBaa_BE::convert_colorspac
 std::vector<ColorStateWithCost>
 Op_RRGGBBaa_BE_to_RGB_HDR::state_after_conversion(const ColorState& input_state,
                                                   const ColorState& target_state,
-                                                  const heif_color_conversion_options& options) const
+                                                  const heif_color_conversion_options& options,
+                                                  const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -445,6 +452,7 @@ Op_RRGGBBaa_BE_to_RGB_HDR::convert_color
                                               const ColorState& input_state,
                                               const ColorState& target_state,
                                               const heif_color_conversion_options& options,
+                                              const heif_color_conversion_options_ext& options_ext,
                                               const heif_security_limits* limits) const
 {
   bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE ||
@@ -525,8 +533,9 @@ Op_RRGGBBaa_BE_to_RGB_HDR::convert_color
 
 std::vector<ColorStateWithCost>
 Op_RGB24_32_to_RGB::state_after_conversion(const ColorState& input_state,
-                                                  const ColorState& target_state,
-                                                  const heif_color_conversion_options& options) const
+                                           const ColorState& target_state,
+                                           const heif_color_conversion_options& options,
+                                           const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -559,6 +568,7 @@ Op_RGB24_32_to_RGB::convert_colorspace(c
                                        const ColorState& input_state,
                                        const ColorState& target_state,
                                        const heif_color_conversion_options& options,
+                                       const heif_color_conversion_options_ext& options_ext,
                                        const heif_security_limits* limits) const
 {
   bool has_alpha = input->get_chroma_format() == heif_chroma_interleaved_RGBA;
@@ -622,7 +632,8 @@ Op_RGB24_32_to_RGB::convert_colorspace(c
 std::vector<ColorStateWithCost>
 Op_RRGGBBaa_swap_endianness::state_after_conversion(const ColorState& input_state,
                                                     const ColorState& target_state,
-                                                    const heif_color_conversion_options& options) const
+                                                    const heif_color_conversion_options& options,
+                                                    const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -686,6 +697,7 @@ Op_RRGGBBaa_swap_endianness::convert_col
                                                 const ColorState& input_state,
                                                 const ColorState& target_state,
                                                 const heif_color_conversion_options& options,
+                                                const heif_color_conversion_options_ext& options_ext,
                                                 const heif_security_limits* limits) const
 {
   auto outimg = std::make_shared<HeifPixelImage>();
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2rgb.h 1.20.1-1/libheif/color-conversion/rgb2rgb.h
--- 1.19.8-1/libheif/color-conversion/rgb2rgb.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2rgb.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,13 +32,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -49,13 +51,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -66,13 +70,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -83,13 +89,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -100,13 +108,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -117,13 +127,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2yuv.cc 1.20.1-1/libheif/color-conversion/rgb2yuv.cc
--- 1.19.8-1/libheif/color-conversion/rgb2yuv.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2yuv.cc	2025-07-02 13:05:31.000000000 +0000
@@ -31,7 +31,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_RGB_to_YCbCr<Pixel>::state_after_conversion(const ColorState& input_state,
                                                const ColorState& target_state,
-                                               const heif_color_conversion_options& options) const
+                                               const heif_color_conversion_options& options,
+                                               const heif_color_conversion_options_ext& options_ext) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
 
@@ -95,6 +96,7 @@ Op_RGB_to_YCbCr<Pixel>::convert_colorspa
                                            const ColorState& input_state,
                                            const ColorState& target_state,
                                            const heif_color_conversion_options& options,
+                                           const heif_color_conversion_options_ext& options_ext,
                                            const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
@@ -288,7 +290,8 @@ template class Op_RGB_to_YCbCr<uint16_t>
 std::vector<ColorStateWithCost>
 Op_RRGGBBxx_HDR_to_YCbCr420::state_after_conversion(const ColorState& input_state,
                                                     const ColorState& target_state,
-                                                    const heif_color_conversion_options& options) const
+                                                    const heif_color_conversion_options& options,
+                                                    const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -344,6 +347,7 @@ Op_RRGGBBxx_HDR_to_YCbCr420::convert_col
                                                 const ColorState& input_state,
                                                 const ColorState& target_state,
                                                 const heif_color_conversion_options& options,
+                                                const heif_color_conversion_options_ext& options_ext,
                                                 const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
@@ -481,7 +485,8 @@ Op_RRGGBBxx_HDR_to_YCbCr420::convert_col
 std::vector<ColorStateWithCost>
 Op_RGB24_32_to_YCbCr::state_after_conversion(const ColorState& input_state,
                                              const ColorState& target_state,
-                                             const heif_color_conversion_options& options) const
+                                             const heif_color_conversion_options& options,
+                                             const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -551,6 +556,7 @@ Op_RGB24_32_to_YCbCr::convert_colorspace
                                          const ColorState& input_state,
                                          const ColorState& target_state,
                                          const heif_color_conversion_options& options,
+                                         const heif_color_conversion_options_ext& options_ext,
                                          const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
@@ -785,7 +791,8 @@ Op_RGB24_32_to_YCbCr::convert_colorspace
 std::vector<ColorStateWithCost>
 Op_RGB24_32_to_YCbCr444_GBR::state_after_conversion(const ColorState& input_state,
                                                     const ColorState& target_state,
-                                                    const heif_color_conversion_options& options) const
+                                                    const heif_color_conversion_options& options,
+                                                    const heif_color_conversion_options_ext& options_ext) const
 {
   // Note: no input alpha channel required. It will be filled up with 0xFF.
 
@@ -824,6 +831,7 @@ Op_RGB24_32_to_YCbCr444_GBR::convert_col
                                                 const ColorState& input_state,
                                                 const ColorState& target_state,
                                                 const heif_color_conversion_options& options,
+                                                const heif_color_conversion_options_ext& options_ext,
                                                 const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2yuv.h 1.20.1-1/libheif/color-conversion/rgb2yuv.h
--- 1.19.8-1/libheif/color-conversion/rgb2yuv.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2yuv.h	2025-07-02 13:05:31.000000000 +0000
@@ -33,13 +33,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -51,13 +53,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -68,13 +72,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -85,13 +91,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2yuv_sharp.cc 1.20.1-1/libheif/color-conversion/rgb2yuv_sharp.cc
--- 1.19.8-1/libheif/color-conversion/rgb2yuv_sharp.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2yuv_sharp.cc	2025-07-02 13:05:31.000000000 +0000
@@ -55,7 +55,8 @@ static uint16_t Shift(uint16_t v, int in
 std::vector<ColorStateWithCost>
 Op_Any_RGB_to_YCbCr_420_Sharp::state_after_conversion(
     const ColorState& input_state, const ColorState& target_state,
-    const heif_color_conversion_options& options) const
+    const heif_color_conversion_options& options,
+    const heif_color_conversion_options_ext& options_ext) const
 {
 #ifdef HAVE_LIBSHARPYUV
   // this Op only implements the sharp_yuv algorithm
@@ -127,6 +128,7 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_c
     const ColorState& input_state,
     const ColorState& target_state,
     const heif_color_conversion_options& options,
+    const heif_color_conversion_options_ext& options_ext,
     const heif_security_limits* limits) const
 {
 #ifdef HAVE_LIBSHARPYUV
diff -pruN 1.19.8-1/libheif/color-conversion/rgb2yuv_sharp.h 1.20.1-1/libheif/color-conversion/rgb2yuv_sharp.h
--- 1.19.8-1/libheif/color-conversion/rgb2yuv_sharp.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/rgb2yuv_sharp.h	2025-07-02 13:05:31.000000000 +0000
@@ -32,13 +32,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/color-conversion/yuv2rgb.cc 1.20.1-1/libheif/color-conversion/yuv2rgb.cc
--- 1.19.8-1/libheif/color-conversion/yuv2rgb.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/yuv2rgb.cc	2025-07-02 13:05:31.000000000 +0000
@@ -29,7 +29,8 @@ template<class Pixel>
 std::vector<ColorStateWithCost>
 Op_YCbCr_to_RGB<Pixel>::state_after_conversion(const ColorState& input_state,
                                                const ColorState& target_state,
-                                               const heif_color_conversion_options& options) const
+                                               const heif_color_conversion_options& options,
+                                               const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -87,6 +88,7 @@ Op_YCbCr_to_RGB<Pixel>::convert_colorspa
                                            const ColorState& input_state,
                                            const ColorState& target_state,
                                            const heif_color_conversion_options& options,
+                                           const heif_color_conversion_options_ext& options_ext,
                                            const heif_security_limits* limits) const
 {
   bool hdr = !std::is_same<Pixel, uint8_t>::value;
@@ -269,7 +271,8 @@ template class Op_YCbCr_to_RGB<uint16_t>
 std::vector<ColorStateWithCost>
 Op_YCbCr420_to_RGB24::state_after_conversion(const ColorState& input_state,
                                              const ColorState& target_state,
-                                             const heif_color_conversion_options& options) const
+                                             const heif_color_conversion_options& options,
+                                             const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -317,6 +320,7 @@ Op_YCbCr420_to_RGB24::convert_colorspace
                                          const ColorState& input_state,
                                          const ColorState& target_state,
                                          const heif_color_conversion_options& options,
+                                         const heif_color_conversion_options_ext& options_ext,
                                          const heif_security_limits* limits) const
 {
   if (input->get_bits_per_pixel(heif_channel_Y) != 8 ||
@@ -361,14 +365,33 @@ Op_YCbCr420_to_RGB24::convert_colorspace
 
   uint32_t x, y;
   for (y = 0; y < height; y++) {
+    // Row pointers for input and output
+    const uint8_t* y_row = &in_y[y * in_y_stride];
+    const uint8_t* cb_row = &in_cb[(y / 2) * in_cb_stride];
+    const uint8_t* cr_row = &in_cr[(y / 2) * in_cr_stride];
+    uint8_t* out_row = &out_p[y * out_p_stride];
+
+    int cb = 0;
+    int cr = 0;
+    int r_offset = 0;
+    int g_offset = 0;
+    int b_offset = 0;
+
     for (x = 0; x < width; x++) {
-      int yv = (in_y[y * in_y_stride + x]);
-      int cb = (in_cb[y / 2 * in_cb_stride + x / 2] - 128);
-      int cr = (in_cr[y / 2 * in_cr_stride + x / 2] - 128);
+      // Update color offsets every other pixel
+      if ((x & 1) == 0) {
+        cb = cb_row[x / 2] - 128;
+        cr = cr_row[x / 2] - 128;
+        r_offset = ((r_cr * cr + 128) >> 8);
+        g_offset = ((g_cb * cb + g_cr * cr + 128) >> 8);
+        b_offset = ((b_cb * cb + 128) >> 8);
+      }
 
-      out_p[y * out_p_stride + 3 * x + 0] = clip_int_u8(yv + ((r_cr * cr + 128) >> 8));
-      out_p[y * out_p_stride + 3 * x + 1] = clip_int_u8(yv + ((g_cb * cb + g_cr * cr + 128) >> 8));
-      out_p[y * out_p_stride + 3 * x + 2] = clip_int_u8(yv + ((b_cb * cb + 128) >> 8));
+      int yv = y_row[x];
+      uint8_t* rgb = &out_row[3 * x];
+      rgb[0] = clip_int_u8(yv + r_offset);
+      rgb[1] = clip_int_u8(yv + g_offset);
+      rgb[2] = clip_int_u8(yv + b_offset);
     }
   }
 
@@ -379,7 +402,8 @@ Op_YCbCr420_to_RGB24::convert_colorspace
 std::vector<ColorStateWithCost>
 Op_YCbCr420_to_RGB32::state_after_conversion(const ColorState& input_state,
                                              const ColorState& target_state,
-                                             const heif_color_conversion_options& options) const
+                                             const heif_color_conversion_options& options,
+                                             const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -428,6 +452,7 @@ Op_YCbCr420_to_RGB32::convert_colorspace
                                          const ColorState& input_state,
                                          const ColorState& target_state,
                                          const heif_color_conversion_options& options,
+                                         const heif_color_conversion_options_ext& options_ext,
                                          const heif_security_limits* limits) const
 {
   if (input->get_bits_per_pixel(heif_channel_Y) != 8 ||
@@ -509,7 +534,8 @@ Op_YCbCr420_to_RGB32::convert_colorspace
 std::vector<ColorStateWithCost>
 Op_YCbCr420_to_RRGGBBaa::state_after_conversion(const ColorState& input_state,
                                                 const ColorState& target_state,
-                                                const heif_color_conversion_options& options) const
+                                                const heif_color_conversion_options& options,
+                                                const heif_color_conversion_options_ext& options_ext) const
 {
   // this Op only implements the nearest-neighbor algorithm
 
@@ -563,6 +589,7 @@ Op_YCbCr420_to_RRGGBBaa::convert_colorsp
                                             const ColorState& input_state,
                                             const ColorState& target_state,
                                             const heif_color_conversion_options& options,
+                                            const heif_color_conversion_options_ext& options_ext,
                                             const heif_security_limits* limits) const
 {
   uint32_t width = input->get_width();
diff -pruN 1.19.8-1/libheif/color-conversion/yuv2rgb.h 1.20.1-1/libheif/color-conversion/yuv2rgb.h
--- 1.19.8-1/libheif/color-conversion/yuv2rgb.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/color-conversion/yuv2rgb.h	2025-07-02 13:05:31.000000000 +0000
@@ -34,13 +34,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -51,13 +53,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -68,13 +72,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
@@ -85,13 +91,15 @@ public:
   std::vector<ColorStateWithCost>
   state_after_conversion(const ColorState& input_state,
                          const ColorState& target_state,
-                         const heif_color_conversion_options& options) const override;
+                         const heif_color_conversion_options& options,
+                         const heif_color_conversion_options_ext& options_ext) const override;
 
   Result<std::shared_ptr<HeifPixelImage>>
   convert_colorspace(const std::shared_ptr<const HeifPixelImage>& input,
                      const ColorState& input_state,
                      const ColorState& target_state,
                      const heif_color_conversion_options& options,
+                     const heif_color_conversion_options_ext& options_ext,
                      const heif_security_limits* limits) const override;
 };
 
diff -pruN 1.19.8-1/libheif/common_utils.cc 1.20.1-1/libheif/common_utils.cc
--- 1.19.8-1/libheif/common_utils.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/common_utils.cc	2025-07-02 13:05:31.000000000 +0000
@@ -158,3 +158,29 @@ std::string fourcc_to_string(uint32_t co
 
   return str;
 }
+
+
+Result<std::string> vector_to_string(const std::vector<uint8_t>& vec)
+{
+  if (vec.empty()) {
+    return Error{heif_error_Invalid_input,
+                 heif_suberror_Unspecified,
+                 "Null length string"};
+  }
+
+  if (vec.back() != 0) {
+    return Error{heif_error_Invalid_input,
+                 heif_suberror_Unspecified,
+                 "utf8string not null-terminated"};
+  }
+
+  for (size_t i=0;i<vec.size()-1;i++) {
+    if (vec[i] == 0) {
+      return Error{heif_error_Invalid_input,
+                   heif_suberror_Unspecified,
+                   "utf8string with null character"};
+    }
+  }
+
+  return std::string(vec.begin(), vec.end()-1);
+}
diff -pruN 1.19.8-1/libheif/common_utils.h 1.20.1-1/libheif/common_utils.h
--- 1.19.8-1/libheif/common_utils.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/common_utils.h	2025-07-02 13:05:31.000000000 +0000
@@ -22,8 +22,11 @@
 #define LIBHEIF_COMMON_UTILS_H
 
 #include <cinttypes>
-#include "libheif/heif.h"
 #include <string>
+#include <vector>
+
+#include "libheif/heif.h"
+#include "error.h"
 
 #ifdef _MSC_VER
 #define MAYBE_UNUSED
@@ -98,4 +101,6 @@ inline uint8_t clip_f_u8(float fx)
   return static_cast<uint8_t>(x);
 }
 
+Result<std::string> vector_to_string(const std::vector<uint8_t>& vec);
+
 #endif //LIBHEIF_COMMON_UTILS_H
diff -pruN 1.19.8-1/libheif/context.cc 1.20.1-1/libheif/context.cc
--- 1.19.8-1/libheif/context.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/context.cc	2025-07-02 13:05:31.000000000 +0000
@@ -22,6 +22,7 @@
 #include "error.h"
 #include "libheif/heif.h"
 #include "region.h"
+#include "brands.h"
 #include <cstdint>
 #include <cassert>
 #include <cstring>
@@ -32,6 +33,10 @@
 #include <deque>
 #include "image-items/image_item.h"
 #include <codecs/hevc_boxes.h>
+#include "sequences/track.h"
+#include "sequences/track_visual.h"
+#include "sequences/track_metadata.h"
+#include "libheif/heif_sequences.h"
 
 #if ENABLE_PARALLEL_TILE_DECODING
 #include <future>
@@ -94,6 +99,7 @@ struct heif_error heif_encoder::alloc()
 
 
 HeifContext::HeifContext()
+    : m_memory_tracker(&m_limits)
 {
   const char* security_limits_variable = getenv("LIBHEIF_SECURITY_LIMITS");
 
@@ -121,7 +127,6 @@ HeifContext::~HeifContext()
 
 static void copy_security_limits(heif_security_limits* dst, const heif_security_limits* src)
 {
-  dst->version = 1;
   dst->max_image_size_pixels = src->max_image_size_pixels;
   dst->max_number_of_tiles = src->max_number_of_tiles;
   dst->max_bayer_pattern_pixels = src->max_bayer_pattern_pixels;
@@ -136,11 +141,22 @@ static void copy_security_limits(heif_se
   dst->max_size_entity_group = src->max_size_entity_group;
 
   dst->max_children_per_box = src->max_children_per_box;
+
+  if (src->version >= 2) {
+    dst->max_sample_description_box_entries = src->max_sample_description_box_entries;
+    dst->max_sample_group_description_box_entries = src->max_sample_group_description_box_entries;
+  }
 }
 
 
 void HeifContext::set_security_limits(const heif_security_limits* limits)
 {
+  // copy default limits
+  if (limits->version < global_security_limits.version) {
+    copy_security_limits(&m_limits, &global_security_limits);
+  }
+
+  // overwrite with input limits
   copy_security_limits(&m_limits, limits);
 }
 
@@ -230,13 +246,21 @@ std::shared_ptr<ImageItem> HeifContext::
 
 std::shared_ptr<ImageItem> HeifContext::get_primary_image(bool return_error_image)
 {
-  if (!return_error_image && m_primary_image->get_item_error())
+  if (m_primary_image == nullptr)
+    return nullptr;
+  else if (!return_error_image && m_primary_image->get_item_error())
     return nullptr;
   else
     return m_primary_image;
 }
 
 
+std::shared_ptr<const ImageItem> HeifContext::get_primary_image(bool return_error_image) const
+{
+  return const_cast<HeifContext*>(this)->get_primary_image(return_error_image);
+}
+
+
 bool HeifContext::is_image(heif_item_id ID) const
 {
   return m_all_images.find(ID) != m_all_images.end();
@@ -259,8 +283,48 @@ void HeifContext::add_region_referenced_
   m_heif_file->add_iref_reference(region_item_id, fourcc("mask"), {mask_item_id});
 }
 
+
+static uint64_t rescale(uint64_t duration, uint32_t old_base, uint32_t new_base)
+{
+  // prevent division by zero
+  // TODO: we might emit an error in this case
+  if (old_base == 0) {
+    return 0;
+  }
+
+  return duration * new_base / old_base;
+}
+
+
 void HeifContext::write(StreamWriter& writer)
 {
+  // --- finalize some parameters
+
+  uint64_t max_sequence_duration = 0;
+  if (auto mvhd = m_heif_file->get_mvhd_box()) {
+    for (const auto& track : m_tracks) {
+      track.second->finalize_track();
+
+      // rescale track duration to movie timescale units
+
+      uint64_t track_duration_in_media_units = track.second->get_duration_in_media_units();
+      uint32_t media_timescale = track.second->get_timescale();
+
+      uint32_t mvhd_timescale = m_heif_file->get_mvhd_box()->get_time_scale();
+      if (mvhd_timescale == 0) {
+        mvhd_timescale = track.second->get_timescale();
+        m_heif_file->get_mvhd_box()->set_time_scale(mvhd_timescale);
+      }
+
+      uint64_t movie_duration = rescale(track_duration_in_media_units, media_timescale, mvhd_timescale);
+      track.second->set_track_duration_in_movie_units(movie_duration);
+
+      max_sequence_duration = std::max(max_sequence_duration, movie_duration);
+    }
+
+    mvhd->set_duration(max_sequence_duration);
+  }
+
   // --- serialize regions
 
   for (auto& image : m_all_images) {
@@ -286,7 +350,37 @@ void HeifContext::write(StreamWriter& wr
 
   // --- sort item properties
 
-  m_heif_file->get_ipma_box()->sort_properties(m_heif_file->get_ipco_box());
+  if (auto ipma = m_heif_file->get_ipma_box()) {
+    ipma->sort_properties(m_heif_file->get_ipco_box());
+  }
+
+  // --- derive box versions
+
+  m_heif_file->derive_box_versions();
+
+  // --- determine brands
+
+  heif_brand2 main_brand;
+  std::vector<heif_brand2> compatible_brands;
+  compatible_brands = compute_compatible_brands(this, &main_brand);
+
+  // Note: major brand should be repeated in the compatible brands, according to this:
+  //   ISOBMFF (ISO/IEC 14496-12:2020) § K.4:
+  //   NOTE This document requires that the major brand be repeated in the compatible-brands,
+  //   but this requirement is relaxed in the 'profiles' parameter for compactness.
+  // See https://github.com/strukturag/libheif/issues/478
+
+  auto ftyp = m_heif_file->get_ftyp_box();
+
+  // set major brand if not set manually yet
+  if (ftyp->get_major_brand() == 0) {
+    ftyp->set_major_brand(main_brand);
+  }
+
+  ftyp->set_minor_version(0);
+  for (auto brand : compatible_brands) {
+    ftyp->add_compatible_brand(brand);
+  }
 
   // --- write to file
 
@@ -333,6 +427,26 @@ void HeifContext::remove_top_level_image
 
 Error HeifContext::interpret_heif_file()
 {
+  if (m_heif_file->has_images()) {
+    Error err = interpret_heif_file_images();
+    if (err) {
+      return err;
+    }
+  }
+
+  if (m_heif_file->has_sequences()) {
+    Error err = interpret_heif_file_sequences();
+    if (err) {
+      return err;
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+Error HeifContext::interpret_heif_file_images()
+{
   m_all_images.clear();
   m_top_level_images.clear();
   m_primary_image.reset();
@@ -1106,6 +1220,26 @@ Result<std::shared_ptr<HeifPixelImage>>
 
   // --- convert to output chroma format
 
+  auto img_result = convert_to_output_colorspace(img, out_colorspace, out_chroma, options);
+  if (img_result.error) {
+    return img_result.error;
+  }
+  else {
+    img = *img_result;
+  }
+
+  img->add_warnings(imgitem->get_decoding_warnings());
+
+  return img;
+}
+
+
+
+Result<std::shared_ptr<HeifPixelImage>> HeifContext::convert_to_output_colorspace(std::shared_ptr<HeifPixelImage> img,
+                                                                                  heif_colorspace out_colorspace,
+                                                                                  heif_chroma out_chroma,
+                                                                                  const struct heif_decoding_options& options) const
+{
   heif_colorspace target_colorspace = (out_colorspace == heif_colorspace_undefined ?
                                        img->get_colorspace() :
                                        out_colorspace);
@@ -1121,21 +1255,16 @@ Result<std::shared_ptr<HeifPixelImage>>
 
   if (different_chroma ||
       different_colorspace ||
-      converted_output_bpp) {
+      converted_output_bpp ||
+      (img->has_alpha() && options.color_conversion_options_ext && options.color_conversion_options_ext->alpha_composition_mode != heif_alpha_composition_mode_none)) {
 
-    auto img_result = convert_colorspace(img, target_colorspace, target_chroma, nullptr,
-                                         converted_output_bpp, options.color_conversion_options, get_security_limits());
-    if (img_result.error) {
-      return img_result.error;
-    }
-    else {
-      img = *img_result;
-    }
+    return convert_colorspace(img, target_colorspace, target_chroma, nullptr, converted_output_bpp,
+                                         options.color_conversion_options, options.color_conversion_options_ext,
+                                         get_security_limits());
+  }
+  else {
+    return img;
   }
-
-  img->add_warnings(imgitem->get_decoding_warnings());
-
-  return img;
 }
 
 
@@ -1204,19 +1333,27 @@ Result<std::shared_ptr<ImageItem>> HeifC
 
   heif_encoding_options options = in_options;
 
-  if (const auto* nclx = output_image_item->get_forced_output_nclx()) {
-    options.output_nclx_profile = const_cast<heif_color_profile_nclx*>(nclx);
-  }
+  std::shared_ptr<HeifPixelImage> colorConvertedImage;
 
-  Result<std::shared_ptr<HeifPixelImage>> srcImageResult = output_image_item->convert_colorspace_for_encoding(pixel_image,
-                                                                                                              encoder,
-                                                                                                              options);
-  if (srcImageResult.error) {
-    return srcImageResult.error;
-  }
+  if (output_image_item->get_encoder()) {
+    if (const auto* nclx = output_image_item->get_encoder()->get_forced_output_nclx()) {
+      options.output_nclx_profile = const_cast<heif_color_profile_nclx*>(nclx);
+    }
 
-  std::shared_ptr<HeifPixelImage> colorConvertedImage = srcImageResult.value;
+    Result<std::shared_ptr<HeifPixelImage>> srcImageResult;
+    srcImageResult = output_image_item->get_encoder()->convert_colorspace_for_encoding(pixel_image,
+                                                                                       encoder,
+                                                                                       options,
+                                                                                       get_security_limits());
+    if (srcImageResult.error) {
+      return srcImageResult.error;
+    }
 
+    colorConvertedImage = srcImageResult.value;
+  }
+  else {
+    colorConvertedImage = pixel_image;
+  }
 
   Error err = output_image_item->encode_to_item(this,
                                                 colorConvertedImage,
@@ -1271,8 +1408,8 @@ Result<std::shared_ptr<ImageItem>> HeifC
   }
   output_image_item->set_properties(properties);
 
-  m_heif_file->set_brand(encoder->plugin->compression_format,
-                         output_image_item->is_miaf_compatible());
+  //m_heif_file->set_brand(encoder->plugin->compression_format,
+  //                       output_image_item->is_miaf_compatible());
 
   return output_image_item;
 }
@@ -1467,7 +1604,14 @@ Error HeifContext::add_generic_metadata(
 
 heif_property_id HeifContext::add_property(heif_item_id targetItem, std::shared_ptr<Box> property, bool essential)
 {
-  heif_property_id id = m_heif_file->add_property(targetItem, property, essential);
+  heif_property_id id;
+
+  if (auto img = get_image(targetItem, false)) {
+    id = img->add_property(property, essential);
+  }
+  else {
+    id = m_heif_file->add_property(targetItem, property, essential);
+  }
 
   return id;
 }
@@ -1562,3 +1706,147 @@ Result<heif_item_id> HeifContext::add_py
 
   return {group_id};
 }
+
+
+Error HeifContext::interpret_heif_file_sequences()
+{
+  m_tracks.clear();
+
+
+  // --- reference all non-hidden images
+
+  auto moov = m_heif_file->get_moov_box();
+  assert(moov);
+
+  auto mvhd = moov->get_child_box<Box_mvhd>();
+  if (!mvhd) {
+    assert(false); // TODO
+  }
+
+  auto tracks = moov->get_child_boxes<Box_trak>();
+  for (const auto& track_box : tracks) {
+    auto track = Track::alloc_track(this, track_box);
+    m_tracks.insert({track->get_id(), track});
+
+    if (track->is_visual_track()) {
+      m_visual_track_id = track->get_id();
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+std::vector<uint32_t> HeifContext::get_track_IDs() const
+{
+  std::vector<uint32_t> ids;
+
+  for (const auto& track : m_tracks) {
+    ids.push_back(track.first);
+  }
+
+  return ids;
+}
+
+
+Result<std::shared_ptr<Track>> HeifContext::get_track(uint32_t track_id)
+{
+  assert(has_sequence());
+
+  if (track_id != 0) {
+    auto iter = m_tracks.find(track_id);
+    if (iter == m_tracks.end()) {
+      return Error{heif_error_Usage_error,
+                   heif_suberror_Unspecified,
+                   "Invalid track id"};
+    }
+
+    return iter->second;
+  }
+
+  if (m_visual_track_id != 0) {
+    return m_tracks[m_visual_track_id];
+  }
+
+  return m_tracks.begin()->second;
+}
+
+
+Result<std::shared_ptr<const Track>> HeifContext::get_track(uint32_t track_id) const
+{
+  auto result = const_cast<HeifContext*>(this)->get_track(track_id);
+  if (result.error) {
+    return result.error;
+  }
+  else {
+    Result<std::shared_ptr<const Track>> my_result;
+    my_result.value = result.value;
+    return my_result;
+  }
+}
+
+
+uint32_t HeifContext::get_sequence_timescale() const
+{
+  auto mvhd = m_heif_file->get_mvhd_box();
+  if (!mvhd) {
+    return 0;
+  }
+
+  return mvhd->get_time_scale();
+}
+
+
+void HeifContext::set_sequence_timescale(uint32_t timescale)
+{
+  get_heif_file()->init_for_sequence();
+
+  auto mvhd = m_heif_file->get_mvhd_box();
+
+  /* unnecessary, since mvhd duration is set during writing
+
+  uint32_t old_timescale = mvhd->get_time_scale();
+  if (old_timescale != 0) {
+    uint64_t scaled_duration = mvhd->get_duration() * timescale / old_timescale;
+    mvhd->set_duration(scaled_duration);
+  }
+  */
+
+  mvhd->set_time_scale(timescale);
+}
+
+
+uint64_t HeifContext::get_sequence_duration() const
+{
+  auto mvhd = m_heif_file->get_mvhd_box();
+  if (!mvhd) {
+    return 0;
+  }
+
+  return mvhd->get_duration();
+}
+
+
+Result<std::shared_ptr<Track_Visual>> HeifContext::add_visual_sequence_track(const TrackOptions* options,
+                                                                             uint32_t handler_type,
+                                                                             uint16_t width, uint16_t height)
+{
+  m_heif_file->init_for_sequence();
+
+  std::shared_ptr<Track_Visual> trak = std::make_shared<Track_Visual>(this, 0, width, height, options, handler_type);
+  m_tracks.insert({trak->get_id(), trak});
+
+  return trak;
+}
+
+
+Result<std::shared_ptr<class Track_Metadata>> HeifContext::add_uri_metadata_sequence_track(const TrackOptions* options,
+                                                                                           std::string uri)
+{
+  m_heif_file->init_for_sequence();
+
+  std::shared_ptr<Track_Metadata> trak = std::make_shared<Track_Metadata>(this, 0, uri, options);
+  m_tracks.insert({trak->get_id(), trak});
+
+  return trak;
+}
diff -pruN 1.19.8-1/libheif/context.h 1.20.1-1/libheif/context.h
--- 1.19.8-1/libheif/context.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/context.h	2025-07-02 13:05:31.000000000 +0000
@@ -47,6 +47,8 @@ class StreamWriter;
 
 class ImageItem;
 
+class Track;
+
 
 // This is a higher-level view than HeifFile.
 // Images are grouped logically into main images and their thumbnails.
@@ -94,6 +96,8 @@ public:
 
   std::shared_ptr<ImageItem> get_primary_image(bool return_error_image);
 
+  std::shared_ptr<const ImageItem> get_primary_image(bool return_error_image) const;
+
   bool is_image(heif_item_id ID) const;
 
   bool has_alpha(heif_item_id ID) const;
@@ -104,6 +108,11 @@ public:
                                                        const struct heif_decoding_options& options,
                                                        bool decode_only_tile, uint32_t tx, uint32_t ty) const;
 
+  Result<std::shared_ptr<HeifPixelImage>> convert_to_output_colorspace(std::shared_ptr<HeifPixelImage> img,
+                                                                       heif_colorspace out_colorspace,
+                                                                       heif_chroma out_chroma,
+                                                                       const struct heif_decoding_options& options) const;
+
   Error get_id_of_non_virtual_child_image(heif_item_id in, heif_item_id& out) const;
 
   std::string debug_dump_boxes() const;
@@ -168,6 +177,31 @@ public:
 
   void add_region_referenced_mask_ref(heif_item_id region_item_id, heif_item_id mask_item_id);
 
+
+  // === sequences ==
+
+  bool has_sequence() const { return !m_tracks.empty(); }
+
+  int get_number_of_tracks() const { return static_cast<int>(m_tracks.size()); }
+
+  std::vector<uint32_t> get_track_IDs() const;
+
+  // If 0 is passed as track_id, the main visual track is returned (we assume that there is only one visual track).
+  Result<std::shared_ptr<Track>> get_track(uint32_t track_id);
+
+  Result<std::shared_ptr<const Track>> get_track(uint32_t track_id) const;
+
+  uint32_t get_sequence_timescale() const;
+
+  uint64_t get_sequence_duration() const;
+
+  void set_sequence_timescale(uint32_t timescale);
+
+  Result<std::shared_ptr<class Track_Visual>> add_visual_sequence_track(const struct TrackOptions*, uint32_t handler_type,
+                                                                        uint16_t width, uint16_t height);
+
+  Result<std::shared_ptr<class Track_Metadata>> add_uri_metadata_sequence_track(const struct TrackOptions*, std::string uri);
+
 private:
   std::map<heif_item_id, std::shared_ptr<ImageItem>> m_all_images;
 
@@ -182,11 +216,21 @@ private:
   int m_max_decoding_threads = 4;
 
   heif_security_limits m_limits;
+  TotalMemoryTracker m_memory_tracker;
 
   std::vector<std::shared_ptr<RegionItem>> m_region_items;
 
+  // --- sequences
+
+  std::map<uint32_t, std::shared_ptr<Track>> m_tracks;
+  uint32_t m_visual_track_id = 0;
+
   Error interpret_heif_file();
 
+  Error interpret_heif_file_images();
+
+  Error interpret_heif_file_sequences();
+
   void remove_top_level_image(const std::shared_ptr<ImageItem>& image);
 };
 
diff -pruN 1.19.8-1/libheif/error.cc 1.20.1-1/libheif/error.cc
--- 1.19.8-1/libheif/error.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/error.cc	2025-07-02 13:05:31.000000000 +0000
@@ -21,6 +21,7 @@
 #include "error.h"
 
 #include <cassert>
+#include <cstring>
 
 // static
 const char Error::kSuccess[] = "Success";
@@ -47,6 +48,34 @@ Error::Error(heif_error_code c,
 }
 
 
+Error Error::from_heif_error(const heif_error& c_error)
+{
+  // unpack the concatenated error message and extract the last part only
+
+  const char* err_string = get_error_string(c_error.code);
+  const char* sub_err_string = get_error_string(c_error.subcode);
+
+  std::string msg = c_error.message;
+  if (msg.starts_with(err_string)) {
+    msg = msg.substr(strlen(err_string));
+
+    if (msg.starts_with(": ")) {
+      msg = msg.substr(2);
+    }
+
+    if (msg.starts_with(sub_err_string)) {
+      msg = msg.substr(strlen(sub_err_string));
+
+      if (msg.starts_with(": ")) {
+        msg = msg.substr(2);
+      }
+    }
+  }
+
+  return {c_error.code, c_error.subcode, msg};
+}
+
+
 const char* Error::get_error_string(heif_error_code err)
 {
   switch (err) {
@@ -76,6 +105,8 @@ const char* Error::get_error_string(heif
       return "Error while loading plugin";
     case heif_error_Canceled:
       return "Canceled by user";
+    case heif_error_End_of_sequence:
+      return "End of sequence";
   }
 
   assert(false);
@@ -178,6 +209,8 @@ const char* Error::get_error_string(heif
       return "Invalid JPEG 2000 codestream";
     case heif_suberror_Decompression_invalid_data:
       return "Invalid data in generic compression inflation";
+    case heif_suberror_No_moov_box:
+      return "No 'moov' box";
     case heif_suberror_No_icbr_box:
       return "No 'icbr' box";
     case heif_suberror_Invalid_mini_box:
diff -pruN 1.19.8-1/libheif/error.h 1.20.1-1/libheif/error.h
--- 1.19.8-1/libheif/error.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/error.h	2025-07-02 13:05:31.000000000 +0000
@@ -77,6 +77,8 @@ public:
         heif_suberror_code sc = heif_suberror_Unspecified,
         const std::string& msg = "");
 
+  static Error from_heif_error(const heif_error&);
+
   static const Error Ok;
 
   static const Error InternalError;
diff -pruN 1.19.8-1/libheif/file.cc 1.20.1-1/libheif/file.cc
--- 1.19.8-1/libheif/file.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/file.cc	2025-07-02 13:05:31.000000000 +0000
@@ -28,7 +28,7 @@
 #include "image-items/vvc.h"
 #include "codecs/avif_boxes.h"
 #include "codecs/hevc_boxes.h"
-#include "codecs/uncompressed/unc_boxes.h"
+#include "sequences/seq_boxes.h"
 
 #include <cstdint>
 #include <fstream>
@@ -39,6 +39,8 @@
 #include <cassert>
 #include <algorithm>
 
+#include "libheif/heif_cxx.h"
+
 #if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER)
 
 #ifndef NOMINMAX
@@ -147,6 +149,16 @@ void HeifFile::new_empty_file()
   m_top_level_boxes.clear();
 
   m_ftyp_box = std::make_shared<Box_ftyp>();
+  m_top_level_boxes.push_back(m_ftyp_box);
+}
+
+
+void HeifFile::init_for_image()
+{
+  if (m_meta_box) {
+    return;
+  }
+
   m_hdlr_box = std::make_shared<Box_hdlr>();
   m_meta_box = std::make_shared<Box_meta>();
   m_ipco_box = std::make_shared<Box_ipco>();
@@ -167,7 +179,6 @@ void HeifFile::new_empty_file()
 
   m_infe_boxes.clear();
 
-  m_top_level_boxes.push_back(m_ftyp_box);
   m_top_level_boxes.push_back(m_meta_box);
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
   // TODO: do not create 'mini' box as we cannot write them yet.
@@ -178,74 +189,35 @@ void HeifFile::new_empty_file()
 }
 
 
-void HeifFile::set_brand(heif_compression_format format, bool miaf_compatible)
+void HeifFile::init_for_sequence()
 {
-  // Note: major brand should be repeated in the compatible brands, according to this:
-  //   ISOBMFF (ISO/IEC 14496-12:2020) § K.4:
-  //   NOTE This document requires that the major brand be repeated in the compatible-brands,
-  //   but this requirement is relaxed in the 'profiles' parameter for compactness.
-  // See https://github.com/strukturag/libheif/issues/478
-
-  switch (format) {
-    case heif_compression_HEVC:
-      m_ftyp_box->set_major_brand(heif_brand2_heic);
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(heif_brand2_mif1);
-      m_ftyp_box->add_compatible_brand(heif_brand2_heic);
-      break;
-
-    case heif_compression_AV1:
-      m_ftyp_box->set_major_brand(heif_brand2_avif);
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(heif_brand2_avif);
-      m_ftyp_box->add_compatible_brand(heif_brand2_mif1);
-      break;
-
-    case heif_compression_VVC:
-      m_ftyp_box->set_major_brand(heif_brand2_vvic);
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(heif_brand2_mif1);
-      m_ftyp_box->add_compatible_brand(heif_brand2_vvic);
-      break;
+  if (m_moov_box) {
+    return;
+  }
 
-    case heif_compression_JPEG:
-      m_ftyp_box->set_major_brand(heif_brand2_jpeg);
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(heif_brand2_jpeg);
-      m_ftyp_box->add_compatible_brand(heif_brand2_mif1);
-      break;
+  m_moov_box = std::make_shared<Box_moov>();
+  m_top_level_boxes.push_back(m_moov_box);
 
-    case heif_compression_uncompressed:
-      // Not clear what the correct major brand should be
-      m_ftyp_box->set_major_brand(heif_brand2_mif2);
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(heif_brand2_mif1);
-      break;
+  m_mvhd_box = std::make_shared<Box_mvhd>();
+  m_moov_box->append_child_box(m_mvhd_box);
+}
 
-    case heif_compression_JPEG2000:
-    case heif_compression_HTJ2K:
-      m_ftyp_box->set_major_brand(fourcc("j2ki"));
-      m_ftyp_box->set_minor_version(0);
-      m_ftyp_box->add_compatible_brand(fourcc("mif1"));
-      m_ftyp_box->add_compatible_brand(fourcc("j2ki"));
-      break;
 
-    default:
-      break;
+size_t HeifFile::append_mdat_data(const std::vector<uint8_t>& data)
+{
+  if (!m_mdat_data) {
+    m_mdat_data = std::make_unique<MdatData_Memory>();
   }
 
-  if (miaf_compatible) {
-    m_ftyp_box->add_compatible_brand(heif_brand2_miaf);
-  }
+  return m_mdat_data->append_data(data);
+}
 
-#if 0
-  // Temporarily disabled, pending resolution of
-  // https://github.com/strukturag/libheif/issues/888
-  if (get_num_images() == 1) {
-    // This could be overly conservative, but is safe
-    m_ftyp_box->add_compatible_brand(heif_brand2_1pic);
+
+void HeifFile::derive_box_versions()
+{
+  for (auto& box : m_top_level_boxes) {
+    box->derive_box_version_recursive();
   }
-#endif
 }
 
 
@@ -258,11 +230,26 @@ void HeifFile::write(StreamWriter& write
       continue;
     }
 #endif
-    box->derive_box_version_recursive();
-    box->write(writer);
+    Error err = box->write(writer);
+    (void)err; // TODO: error ?
+  }
+
+  if (m_iloc_box) {
+    // TODO: rewrite to use MdatData class
+    Error err = m_iloc_box->write_mdat_after_iloc(writer);
+    (void)err; // TODO: error ?
   }
 
-  m_iloc_box->write_mdat_after_iloc(writer);
+  if (m_mdat_data) {
+    Result<size_t> mdatResult = write_mdat(writer);
+    if (mdatResult.error) {
+      return; // TODO: error ?
+    }
+
+    for (auto& box : m_top_level_boxes) {
+      box->patch_file_pointers_recursively(writer, mdatResult.value);
+    }
+  }
 }
 
 
@@ -343,6 +330,8 @@ Error HeifFile::parse_heif_file()
 
   m_top_level_boxes.push_back(m_ftyp_box);
 
+  bool is_brand_msf1 = m_ftyp_box->has_compatible_brand(heif_brand2_msf1);
+
   // --- check whether this is a HEIF file and its structural format
 
   if (!m_ftyp_box->has_compatible_brand(heif_brand2_heic) &&
@@ -353,14 +342,15 @@ Error HeifFile::parse_heif_file()
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
       !(m_ftyp_box->get_major_brand() == heif_brand2_mif3) &&
 #endif
-      !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg)) {
+      !m_ftyp_box->has_compatible_brand(heif_brand2_jpeg) &&
+      !is_brand_msf1) {
     std::stringstream sstr;
     sstr << "File does not include any supported brands.\n";
 
     return Error(heif_error_Unsupported_filetype,
                  heif_suberror_Unspecified,
                  sstr.str());
-  }
+      }
 
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
   m_mini_box = m_file_layout->get_mini_box();
@@ -376,17 +366,49 @@ Error HeifFile::parse_heif_file()
 #endif
 
   m_meta_box = m_file_layout->get_meta_box();
-  m_top_level_boxes.push_back(m_meta_box);
+  if (m_meta_box) {
+    m_top_level_boxes.push_back(m_meta_box);
+  }
+
   // TODO: we are missing 'mdat' top level boxes
 
+  m_moov_box = m_file_layout->get_moov_box();
+  if (m_moov_box) {
+    m_top_level_boxes.push_back(m_moov_box);
+  }
+
   // if we didn't find the mini box, meta is required
 
-  if (!m_meta_box) {
+  if (!m_meta_box && !is_brand_msf1) {
     return Error(heif_error_Invalid_input,
                  heif_suberror_No_meta_box);
   }
 
+  if (!m_moov_box && is_brand_msf1) {
+    return Error(heif_error_Invalid_input,
+                 heif_suberror_No_moov_box);
+  }
 
+  if (m_meta_box) {
+    auto err = parse_heif_images();
+    if (err) {
+      return err;
+    }
+  }
+
+  if (m_moov_box) {
+    auto err = parse_heif_sequences();
+    if (err) {
+      return err;
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+Error HeifFile::parse_heif_images()
+{
   m_hdlr_box = m_meta_box->get_child_box<Box_hdlr>();
   if (STRICT_PARSING && !m_hdlr_box) {
     return Error(heif_error_Invalid_input,
@@ -472,6 +494,19 @@ Error HeifFile::parse_heif_file()
 }
 
 
+Error HeifFile::parse_heif_sequences()
+{
+  m_mvhd_box = m_moov_box->get_child_box<Box_mvhd>();
+  if (!m_mvhd_box) {
+    return {heif_error_Invalid_input,
+            heif_suberror_Unspecified,
+            "No mvhd box in image sequence."};
+  }
+
+  return Error::Ok;
+}
+
+
 Error HeifFile::check_for_ref_cycle(heif_item_id ID,
                                     const std::shared_ptr<Box_iref>& iref_box) const
 {
@@ -503,7 +538,7 @@ Error HeifFile::check_for_ref_cycle_recu
 }
 
 
-bool HeifFile::image_exists(heif_item_id ID) const
+bool HeifFile::item_exists(heif_item_id ID) const
 {
   auto image_iter = m_infe_boxes.find(ID);
   return image_iter != m_infe_boxes.end();
@@ -573,7 +608,7 @@ Error HeifFile::get_uncompressed_item_da
   // std::lock_guard<std::mutex> guard(m_read_mutex);   // TODO: I think that this is not needed anymore because this function is not used for image data anymore.
 #endif
 
-  if (!image_exists(ID)) {
+  if (!item_exists(ID)) {
     return Error(heif_error_Usage_error,
                  heif_suberror_Nonexisting_item_referenced);
   }
@@ -658,6 +693,25 @@ Error HeifFile::get_uncompressed_item_da
 }
 
 
+Error HeifFile::append_data_from_file_range(std::vector<uint8_t>& out_data, uint64_t offset, uint32_t size) const
+{
+  bool success = m_input_stream->seek(offset);
+  if (!success) {
+    // TODO: error
+  }
+
+  auto old_size = out_data.size();
+  out_data.resize(old_size + size);
+
+  success = m_input_stream->read(out_data.data() + old_size, size);
+  if (!success) {
+    // TODO: error
+  }
+
+  return {};
+}
+
+
 Error HeifFile::append_data_from_iloc(heif_item_id ID, std::vector<uint8_t>& out_data, uint64_t offset, uint64_t size) const
 {
   const auto& items = m_iloc_box->get_items();
@@ -797,6 +851,8 @@ heif_item_id HeifFile::add_new_image(uin
 
 std::shared_ptr<Box_infe> HeifFile::add_new_infe_box(uint32_t item_type)
 {
+  init_for_image();
+
   heif_item_id id = get_unused_item_id();
 
   auto infe = std::make_shared<Box_infe>();
@@ -1185,3 +1241,29 @@ std::wstring HeifFile::convert_utf8_path
   return ret;
 }
 #endif
+
+
+Result<size_t> HeifFile::write_mdat(StreamWriter& writer)
+{
+  // --- write mdat box
+
+  size_t mdatSize = m_mdat_data->get_data_size();
+
+  if (mdatSize <= 0xFFFFFFFF - 8) {
+    writer.write32((uint32_t) (mdatSize + 8));
+    writer.write32(fourcc("mdat"));
+  }
+  else {
+    // box size > 4 GB
+
+    writer.write32(1);
+    writer.write32(fourcc("mdat"));
+    writer.write64(mdatSize+8+8);
+  }
+
+  size_t dataStartPos = writer.get_position();
+
+  m_mdat_data->write(writer);
+
+  return dataStartPos;
+}
diff -pruN 1.19.8-1/libheif/file.h 1.20.1-1/libheif/file.h
--- 1.19.8-1/libheif/file.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/file.h	2025-07-02 13:05:31.000000000 +0000
@@ -26,7 +26,7 @@
 #include "image-items/avif.h"
 #include "image-items/hevc.h"
 #include "image-items/vvc.h"
-#include "codecs/uncompressed/unc_boxes.h"
+//#include "codecs/uncompressed/unc_boxes.h"
 #include "file_layout.h"
 
 #include <map>
@@ -37,6 +37,7 @@
 #include <unordered_set>
 #include <limits>
 #include <utility>
+#include "mdat_data.h"
 
 #if ENABLE_PARALLEL_TILE_DECODING
 
@@ -49,6 +50,11 @@ class HeifPixelImage;
 
 class Box_j2kH;
 
+class Box_moov;
+
+class Box_mvhd;
+
+
 
 class HeifFile
 {
@@ -67,14 +73,24 @@ public:
 
   Error read_from_memory(const void* data, size_t size, bool copy);
 
+  bool has_images() const { return m_meta_box != nullptr; }
+
+  bool has_sequences() const { return m_moov_box != nullptr; }
+
   std::shared_ptr<StreamReader> get_reader() { return m_input_stream; }
 
   void new_empty_file();
 
-  void set_brand(heif_compression_format format, bool miaf_compatible);
+  void init_for_image();
+
+  void init_for_sequence();
 
   void set_hdlr_box(std::shared_ptr<Box_hdlr> box) { m_hdlr_box = std::move(box); }
 
+  size_t append_mdat_data(const std::vector<uint8_t>& data);
+
+  void derive_box_versions();
+
   void write(StreamWriter& writer);
 
   int get_num_images() const { return static_cast<int>(m_infe_boxes.size()); }
@@ -85,7 +101,7 @@ public:
 
   std::vector<heif_item_id> get_item_IDs() const;
 
-  bool image_exists(heif_item_id ID) const;
+  bool item_exists(heif_item_id ID) const;
 
   bool has_item_with_id(heif_item_id ID) const;
 
@@ -97,6 +113,8 @@ public:
 
   Error get_uncompressed_item_data(heif_item_id ID, std::vector<uint8_t>* data) const;
 
+  Error append_data_from_file_range(std::vector<uint8_t>& out_data, uint64_t offset, uint32_t size) const;
+
   Error append_data_from_iloc(heif_item_id ID, std::vector<uint8_t>& out_data, uint64_t offset, uint64_t size) const;
 
   Error append_data_from_iloc(heif_item_id ID, std::vector<uint8_t>& out_data) const {
@@ -133,6 +151,8 @@ public:
 
   std::shared_ptr<Box_grpl> get_grpl_box() const { return m_grpl_box; }
 
+  std::shared_ptr<Box_meta> get_meta_box() const { return m_meta_box; }
+
   std::shared_ptr<Box_EntityToGroup> get_entity_group(heif_entity_group_id id);
 
   Error get_properties(heif_item_id imageID,
@@ -214,6 +234,13 @@ public:
   static std::wstring convert_utf8_path_to_utf16(std::string pathutf8);
 #endif
 
+
+  // --- sequences
+
+  std::shared_ptr<Box_moov> get_moov_box() { return m_moov_box; }
+
+  std::shared_ptr<Box_mvhd> get_mvhd_box() { return m_mvhd_box; }
+
 private:
 #if ENABLE_PARALLEL_TILE_DECODING
   mutable std::mutex m_read_mutex;
@@ -245,10 +272,24 @@ private:
 
   std::map<heif_item_id, std::shared_ptr<Box_infe> > m_infe_boxes;
 
+  std::unique_ptr<MdatData> m_mdat_data;
+
+  // returns the position of the first data byte in the file
+  Result<size_t> write_mdat(StreamWriter& writer);
+
+  // --- sequences
+
+  std::shared_ptr<Box_moov> m_moov_box;
+  std::shared_ptr<Box_mvhd> m_mvhd_box;
+
   const heif_security_limits* m_limits = nullptr;
 
   Error parse_heif_file();
 
+  Error parse_heif_images();
+
+  Error parse_heif_sequences();
+
   Error check_for_ref_cycle(heif_item_id ID,
                             const std::shared_ptr<Box_iref>& iref_box) const;
 
diff -pruN 1.19.8-1/libheif/file_layout.cc 1.20.1-1/libheif/file_layout.cc
--- 1.19.8-1/libheif/file_layout.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/file_layout.cc	2025-07-02 13:05:31.000000000 +0000
@@ -19,6 +19,8 @@
  */
 
 #include "file_layout.h"
+#include "sequences/seq_boxes.h"
+#include <limits>
 
 
 FileLayout::FileLayout()
@@ -95,6 +97,10 @@ Error FileLayout::read(const std::shared
 
   uint64_t next_box_start = ftyp_size;
 
+  bool meta_found = false;
+  bool mini_found = false;
+  bool moov_found = false;
+
   for (;;) {
     // TODO: overflow
     uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE;
@@ -103,9 +109,14 @@ Error FileLayout::read(const std::shared
     }
 
     if (next_box_header_end > m_max_length) {
-      return {heif_error_Invalid_input,
-              heif_suberror_Unspecified,
-              "Insufficient input data"};
+      if (meta_found || mini_found || moov_found) {
+        return Error::Ok;
+      }
+      else {
+        return {heif_error_Invalid_input,
+                heif_suberror_Unspecified,
+                "Insufficient input data"};
+      }
     }
 
     BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length);
@@ -145,7 +156,7 @@ Error FileLayout::read(const std::shared
 
       m_boxes.push_back(meta_box);
       m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box);
-      break;
+      meta_found = true;
     }
 
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
@@ -181,18 +192,64 @@ Error FileLayout::read(const std::shared
       if (m_mini_box == nullptr) {
         std::cout << "error casting mini box" << std::endl;
       }
-      return Error::Ok;
+
+      mini_found = true;
     }
 #endif
 
-    if (box_header.get_box_size() == 0) {
+    if (box_header.get_short_type() == fourcc("moov")) {
+      const uint64_t moov_box_start = next_box_start;
+      if (box_header.get_box_size() == 0) {
+        // TODO: get file-size from stream and compute box size
+        return {heif_error_Invalid_input,
+                heif_suberror_No_moov_box,
+                "Cannot read moov box with unspecified size"};
+      }
+
+      // TODO: overflow
+      uint64_t end_of_moov_box = moov_box_start + box_header.get_box_size();
+      if (m_max_length < end_of_moov_box) {
+        m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box);
+      }
+
+      if (m_max_length < end_of_moov_box) {
+        return {heif_error_Invalid_input,
+                heif_suberror_No_moov_box,
+                "Cannot read full moov box"};
+      }
+
+      BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box);
+      std::shared_ptr<Box> moov_box;
+      err = Box::read(moov_box_range, &moov_box, limits);
+      if (err) {
+        return err;
+      }
+
+      m_boxes.push_back(moov_box);
+      m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box);
+
+      moov_found = true;
+    }
+
+    uint64_t boxSize = box_header.get_box_size();
+    if (boxSize == Box::size_until_end_of_file) {
+      if (meta_found || mini_found || moov_found) {
+        return Error::Ok;
+      }
+      else {
+        return {heif_error_Invalid_input,
+                heif_suberror_Unspecified,
+                "No meta box found"};
+      }
+    }
+
+    if (std::numeric_limits<uint64_t>::max() - boxSize < next_box_start) {
       return {heif_error_Invalid_input,
-              heif_suberror_No_meta_box,
-              "No meta box found"};
+              heif_suberror_Unspecified,
+              "Box size too large, integer overflow"};
     }
 
-    // TODO: overflow
-    next_box_start = next_box_start + box_header.get_box_size();
+    next_box_start = next_box_start + boxSize;
   }
 
   return Error::Ok;
diff -pruN 1.19.8-1/libheif/file_layout.h 1.20.1-1/libheif/file_layout.h
--- 1.19.8-1/libheif/file_layout.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/file_layout.h	2025-07-02 13:05:31.000000000 +0000
@@ -27,9 +27,12 @@
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
 #include "mini.h"
 #endif
+
 #include <memory>
 #include <vector>
 
+class Box_moov;
+
 
 class FileLayout
 {
@@ -62,6 +65,8 @@ public:
   std::shared_ptr<Box_mini> get_mini_box() { return m_mini_box; }
 #endif
 
+  std::shared_ptr<Box_moov> get_moov_box() { return m_moov_box; }
+
 private:
   WriteMode m_writeMode = WriteMode::Floating;
 
@@ -77,6 +82,7 @@ private:
 #if ENABLE_EXPERIMENTAL_MINI_FORMAT
   std::shared_ptr<Box_mini> m_mini_box;
 #endif
+  std::shared_ptr<Box_moov> m_moov_box;
 
 
   uint64_t m_max_length = 0; // Length seen so far. It can grow over time.
diff -pruN 1.19.8-1/libheif/image-items/avc.cc 1.20.1-1/libheif/image-items/avc.cc
--- 1.19.8-1/libheif/image-items/avc.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/avc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -30,7 +30,7 @@
 #include <utility>
 
 
-Result<ImageItem::CodedImageData> ImageItem_AVC::encode(const std::shared_ptr<HeifPixelImage>& image,
+Result<Encoder::CodedImageData> ImageItem_AVC::encode(const std::shared_ptr<HeifPixelImage>& image,
                                                         struct heif_encoder* encoder,
                                                         const struct heif_encoding_options& options,
                                                         enum heif_image_input_class input_class)
diff -pruN 1.19.8-1/libheif/image-items/avc.h 1.20.1-1/libheif/image-items/avc.h
--- 1.19.8-1/libheif/image-items/avc.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/avc.h	2025-07-02 13:05:31.000000000 +0000
@@ -41,20 +41,20 @@ public:
 
   const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
-
   heif_compression_format get_compression_format() const override { return heif_compression_AVC; }
 
   Error on_load_file() override;
 
+  heif_brand2 get_compatible_brand() const override { return heif_brand2_avci; }
+
 protected:
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
 public:
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override;
 
   std::shared_ptr<class Decoder_AVC> m_decoder;
 };
diff -pruN 1.19.8-1/libheif/image-items/avif.cc 1.20.1-1/libheif/image-items/avif.cc
--- 1.19.8-1/libheif/image-items/avif.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/avif.cc	2025-07-02 13:05:31.000000000 +0000
@@ -21,6 +21,7 @@
 #include "pixelimage.h"
 #include "avif.h"
 #include "codecs/avif_dec.h"
+#include "codecs/avif_enc.h"
 #include "codecs/avif_boxes.h"
 #include "bitstream.h"
 #include "common_utils.h"
@@ -35,6 +36,16 @@
 // https://aomediacodec.github.io/av1-spec/av1-spec.pdf
 
 
+ImageItem_AVIF::ImageItem_AVIF(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id)
+{
+  m_encoder = std::make_shared<Encoder_AVIF>();
+}
+
+ImageItem_AVIF::ImageItem_AVIF(HeifContext* ctx) : ImageItem(ctx)
+{
+  m_encoder = std::make_shared<Encoder_AVIF>();
+}
+
 
 Error ImageItem_AVIF::on_load_file()
 {
@@ -55,53 +66,6 @@ Error ImageItem_AVIF::on_load_file()
 }
 
 
-Result<ImageItem::CodedImageData> ImageItem_AVIF::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                         struct heif_encoder* encoder,
-                                                         const struct heif_encoding_options& options,
-                                                         enum heif_image_input_class input_class)
-{
-  CodedImageData codedImage;
-
-  Box_av1C::configuration config;
-
-  // Fill preliminary av1C in case we cannot parse the sequence_header() correctly in the code below.
-  // TODO: maybe we can remove this later.
-  fill_av1C_configuration(&config, image);
-
-  heif_image c_api_image;
-  c_api_image.image = image;
-
-  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
-  if (err.code) {
-    return Error(err.code,
-                 err.subcode,
-                 err.message);
-  }
-
-  for (;;) {
-    uint8_t* data;
-    int size;
-
-    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
-
-    bool found_config = fill_av1C_configuration_from_stream(&config, data, size);
-    (void) found_config;
-
-    if (data == nullptr) {
-      break;
-    }
-
-    codedImage.append(data, size);
-  }
-
-  auto av1C = std::make_shared<Box_av1C>();
-  av1C->set_configuration(config);
-  codedImage.properties.push_back(av1C);
-
-  return codedImage;
-}
-
-
 Result<std::vector<uint8_t>> ImageItem_AVIF::read_bitstream_configuration_data() const
 {
   return m_decoder->read_bitstream_configuration_data();
@@ -112,3 +76,9 @@ Result<std::shared_ptr<class Decoder>> I
 {
   return {m_decoder};
 }
+
+
+std::shared_ptr<class Encoder> ImageItem_AVIF::get_encoder() const
+{
+  return m_encoder;
+}
diff -pruN 1.19.8-1/libheif/image-items/avif.h 1.20.1-1/libheif/image-items/avif.h
--- 1.19.8-1/libheif/image-items/avif.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/avif.h	2025-07-02 13:05:31.000000000 +0000
@@ -36,33 +36,31 @@
 class ImageItem_AVIF : public ImageItem
 {
 public:
-  ImageItem_AVIF(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {}
+  ImageItem_AVIF(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_AVIF(HeifContext* ctx) : ImageItem(ctx) {}
+  ImageItem_AVIF(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("av01"); }
 
   const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
-
   heif_compression_format get_compression_format() const override { return heif_compression_AV1; }
 
-  Error on_load_file() override;
+  heif_brand2 get_compatible_brand() const override { return heif_brand2_avif; }
 
-public:
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  Error on_load_file() override;
 
 protected:
   Result<std::vector<uint8_t>> read_bitstream_configuration_data() const override;
 
   Result<std::shared_ptr<class Decoder>> get_decoder() const override;
 
+  std::shared_ptr<class Encoder> get_encoder() const override;
+
 private:
   std::shared_ptr<class Decoder_AVIF> m_decoder;
+
+  std::shared_ptr<class Encoder_AVIF> m_encoder;
 };
 
 #endif
diff -pruN 1.19.8-1/libheif/image-items/grid.cc 1.20.1-1/libheif/image-items/grid.cc
--- 1.19.8-1/libheif/image-items/grid.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/grid.cc	2025-07-02 13:05:31.000000000 +0000
@@ -143,20 +143,23 @@ std::string ImageGrid::dump() const
 }
 
 
-extern void set_default_encoding_options(heif_encoding_options& options);
-
-
 ImageItem_Grid::ImageItem_Grid(HeifContext* ctx)
     : ImageItem(ctx)
 {
-  set_default_encoding_options(m_encoding_options);
+  m_encoding_options = heif_encoding_options_alloc();
 }
 
 
 ImageItem_Grid::ImageItem_Grid(HeifContext* ctx, heif_item_id id)
     : ImageItem(ctx, id)
 {
-  set_default_encoding_options(m_encoding_options);
+  m_encoding_options = heif_encoding_options_alloc();
+}
+
+
+ImageItem_Grid::~ImageItem_Grid()
+{
+  heif_encoding_options_free(m_encoding_options);
 }
 
 
@@ -781,8 +784,19 @@ Result<std::shared_ptr<ImageItem_Grid>>
 
   // Set Brands
 
-  file->set_brand(encoder->plugin->compression_format,
-                  griditem->is_miaf_compatible());
+  //file->set_brand(encoder->plugin->compression_format,
+  //                griditem->is_miaf_compatible());
 
   return griditem;
 }
+
+heif_brand2 ImageItem_Grid::get_compatible_brand() const
+{
+  if (m_grid_tile_ids.empty()) { return 0; }
+
+  heif_item_id child_id = m_grid_tile_ids[0];
+  auto child = get_context()->get_image(child_id, false);
+  if (!child) { return 0; }
+
+  return child->get_compatible_brand();
+}
diff -pruN 1.19.8-1/libheif/image-items/grid.h 1.20.1-1/libheif/image-items/grid.h
--- 1.19.8-1/libheif/image-items/grid.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/grid.h	2025-07-02 13:05:31.000000000 +0000
@@ -80,6 +80,8 @@ public:
 
   ImageItem_Grid(HeifContext* ctx);
 
+  ~ImageItem_Grid() override;
+
   uint32_t get_infe_type() const override { return fourcc("grid"); }
 
   static Result<std::shared_ptr<ImageItem_Grid>> add_new_grid_item(HeifContext* ctx,
@@ -113,15 +115,16 @@ public:
   int get_chroma_bits_per_pixel() const override;
 
   void set_encoding_options(const heif_encoding_options* options) {
-    m_encoding_options = *options;
+    heif_encoding_options_copy(m_encoding_options, options);
   }
 
-  const heif_encoding_options* get_encoding_options() const { return &m_encoding_options; }
+  const heif_encoding_options* get_encoding_options() const { return m_encoding_options; }
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override {
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override
+  {
     return Error{heif_error_Unsupported_feature,
                  heif_suberror_Unspecified, "Cannot encode image to 'grid'"};
   }
@@ -129,6 +132,8 @@ public:
   Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const struct heif_decoding_options& options,
                                                                   bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
 
+  heif_brand2 get_compatible_brand() const override;
+
 protected:
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
@@ -152,7 +157,7 @@ private:
   ImageGrid m_grid_spec;
   std::vector<heif_item_id> m_grid_tile_ids;
 
-  heif_encoding_options m_encoding_options;
+  heif_encoding_options* m_encoding_options = nullptr;
 
   Error read_grid_spec();
 
diff -pruN 1.19.8-1/libheif/image-items/hevc.cc 1.20.1-1/libheif/image-items/hevc.cc
--- 1.19.8-1/libheif/image-items/hevc.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/hevc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -32,6 +32,21 @@
 #include <string>
 #include <utility>
 #include "api/libheif/api_structs.h"
+#include "codecs/hevc_enc.h"
+
+
+ImageItem_HEVC::ImageItem_HEVC(HeifContext* ctx, heif_item_id id)
+    : ImageItem(ctx, id)
+{
+  m_encoder = std::make_shared<Encoder_HEVC>();
+}
+
+
+ImageItem_HEVC::ImageItem_HEVC(HeifContext* ctx)
+    : ImageItem(ctx)
+{
+  m_encoder = std::make_shared<Encoder_HEVC>();
+}
 
 
 Error ImageItem_HEVC::on_load_file()
@@ -53,89 +68,31 @@ Error ImageItem_HEVC::on_load_file()
 }
 
 
-Result<ImageItem::CodedImageData> ImageItem_HEVC::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                         struct heif_encoder* encoder,
-                                                         const struct heif_encoding_options& options,
-                                                         enum heif_image_input_class input_class)
-{
-  CodedImageData codedImage;
-
-  auto hvcC = std::make_shared<Box_hvcC>();
-
-  heif_image c_api_image;
-  c_api_image.image = image;
-
-  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
-  if (err.code) {
-    return Error(err.code,
-                 err.subcode,
-                 err.message);
-  }
-
-  int encoded_width = 0;
-  int encoded_height = 0;
-
-  for (;;) {
-    uint8_t* data;
-    int size;
-
-    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
-
-    if (data == nullptr) {
-      break;
-    }
-
-
-    const uint8_t NAL_SPS = 33;
-
-    if ((data[0] >> 1) == NAL_SPS) {
-      Box_hvcC::configuration config;
-
-      parse_sps_for_hvcC_configuration(data, size, &config, &encoded_width, &encoded_height);
-
-      hvcC->set_configuration(config);
-
-      codedImage.encoded_image_width = encoded_width;
-      codedImage.encoded_image_height = encoded_height;
-    }
-
-    switch (data[0] >> 1) {
-      case 0x20:
-      case 0x21:
-      case 0x22:
-        hvcC->append_nal_data(data, size);
-        break;
+heif_brand2 ImageItem_HEVC::get_compatible_brand() const
+{
+  auto hvcC = get_property<Box_hvcC>();
 
-      default:
-        codedImage.append_with_4bytes_size(data, size);
-        // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size);
-    }
+  if (has_essential_property_other_than(std::set{fourcc("hvcC"),
+                                                 fourcc("irot"),
+                                                 fourcc("imir"),
+                                                 fourcc("clap")})) {
+    return 0;
   }
 
-  if (!encoded_width || !encoded_height) {
-    return Error(heif_error_Encoder_plugin_error,
-                 heif_suberror_Invalid_image_size);
+  const auto& config = hvcC->get_configuration();
+  if (config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_Main) ||
+      config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_MainStillPicture)) {
+    return heif_brand2_heic;
   }
 
-  codedImage.properties.push_back(hvcC);
-
-
-  // Make sure that the encoder plugin works correctly and the encoded image has the correct size.
-
-  if (encoder->plugin->plugin_api_version >= 3 &&
-      encoder->plugin->query_encoded_size != nullptr) {
-    uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height();
-
-    encoder->plugin->query_encoded_size(encoder->encoder,
-                                        image->get_width(), image->get_height(),
-                                        &check_encoded_width,
-                                        &check_encoded_height);
-
-    assert((int)check_encoded_width == encoded_width);
-    assert((int)check_encoded_height == encoded_height);
+  if (config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_Main10) ||
+      config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_RExt)) {
+    return heif_brand2_heix;
   }
 
-  return codedImage;
+  // TODO: what brand should we use for this case?
+
+  return heif_brand2_heix;
 }
 
 
@@ -151,6 +108,12 @@ Result<std::shared_ptr<class Decoder>> I
 }
 
 
+std::shared_ptr<class Encoder> ImageItem_HEVC::get_encoder() const
+{
+  return m_encoder;
+}
+
+
 void ImageItem_HEVC::set_preencoded_hevc_image(const std::vector<uint8_t>& data)
 {
   auto hvcC = std::make_shared<Box_hvcC>();
diff -pruN 1.19.8-1/libheif/image-items/hevc.h 1.20.1-1/libheif/image-items/hevc.h
--- 1.19.8-1/libheif/image-items/hevc.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/hevc.h	2025-07-02 13:05:31.000000000 +0000
@@ -34,25 +34,20 @@
 class ImageItem_HEVC : public ImageItem
 {
 public:
-  ImageItem_HEVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {}
+  ImageItem_HEVC(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_HEVC(HeifContext* ctx) : ImageItem(ctx) {}
+  ImageItem_HEVC(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("hvc1"); }
 
   // TODO: MIAF says that the *:hevc:* urn is deprecated and we should use "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
   const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:hevc:2015:auxid:1"; }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
-
   heif_compression_format get_compression_format() const override { return heif_compression_HEVC; }
 
   Error on_load_file() override;
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  heif_brand2 get_compatible_brand() const override;
 
   // currently not used
   void set_preencoded_hevc_image(const std::vector<uint8_t>& data);
@@ -62,8 +57,11 @@ protected:
 
   Result<std::shared_ptr<class Decoder>> get_decoder() const override;
 
+  std::shared_ptr<class Encoder> get_encoder() const override;
+
 private:
   std::shared_ptr<class Decoder_HEVC> m_decoder;
+  std::shared_ptr<class Encoder_HEVC> m_encoder;
 };
 
 #endif
diff -pruN 1.19.8-1/libheif/image-items/iden.cc 1.20.1-1/libheif/image-items/iden.cc
--- 1.19.8-1/libheif/image-items/iden.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/iden.cc	2025-07-02 13:05:31.000000000 +0000
@@ -131,3 +131,9 @@ int ImageItem_iden::get_chroma_bits_per_
 
   return image->get_chroma_bits_per_pixel();
 }
+
+heif_brand2 ImageItem_iden::get_compatible_brand() const
+{
+  assert(false);
+  return 0; // TODO (we never write 'iden' images)
+}
diff -pruN 1.19.8-1/libheif/image-items/iden.h 1.20.1-1/libheif/image-items/iden.h
--- 1.19.8-1/libheif/image-items/iden.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/iden.h	2025-07-02 13:05:31.000000000 +0000
@@ -49,10 +49,10 @@ public:
 
   int get_chroma_bits_per_pixel() const override;
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override
   {
     return Error{heif_error_Unsupported_feature,
                  heif_suberror_Unspecified, "Cannot encode image to 'iden'"};
@@ -61,6 +61,8 @@ public:
   Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const struct heif_decoding_options& options,
                                                                   bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
 
+  heif_brand2 get_compatible_brand() const override;
+
 private:
 };
 
diff -pruN 1.19.8-1/libheif/image-items/image_item.cc 1.20.1-1/libheif/image-items/image_item.cc
--- 1.19.8-1/libheif/image-items/image_item.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/image_item.cc	2025-07-02 13:05:31.000000000 +0000
@@ -229,19 +229,19 @@ std::shared_ptr<ImageItem> ImageItem::al
 }
 
 
-Result<ImageItem::CodedImageData> ImageItem::encode_to_bitstream_and_boxes(const std::shared_ptr<HeifPixelImage>& image,
+Result<Encoder::CodedImageData> ImageItem::encode_to_bitstream_and_boxes(const std::shared_ptr<HeifPixelImage>& image,
                                                                            struct heif_encoder* encoder,
                                                                            const struct heif_encoding_options& options,
                                                                            enum heif_image_input_class input_class)
 {
   // === generate compressed image bitstream
 
-  Result<ImageItem::CodedImageData> encodeResult = encode(image, encoder, options, input_class);
+  Result<Encoder::CodedImageData> encodeResult = encode(image, encoder, options, input_class);
   if (encodeResult.error) {
     return encodeResult;
   }
 
-  CodedImageData& codedImage = encodeResult.value;
+  Encoder::CodedImageData& codedImage = encodeResult.value;
 
   // === generate properties
 
@@ -364,6 +364,16 @@ Result<ImageItem::CodedImageData> ImageI
     codedImage.properties.push_back(mdcv);
   }
 
+
+  // --- write TAI property
+
+  if (auto* tai = image->get_tai_timestamp()) {
+    auto itai = std::make_shared<Box_itai>();
+    itai->set_from_tai_timestamp_packet(tai);
+
+    codedImage.properties.push_back(itai);
+  }
+
   return encodeResult;
 }
 
@@ -382,12 +392,12 @@ Error ImageItem::encode_to_item(HeifCont
 
   // compress image and assign data to item
 
-  Result<CodedImageData> codingResult = encode_to_bitstream_and_boxes(image, encoder, options, input_class);
+  Result<Encoder::CodedImageData> codingResult = encode_to_bitstream_and_boxes(image, encoder, options, input_class);
   if (codingResult.error) {
     return codingResult.error;
   }
 
-  CodedImageData& codedImage = codingResult.value;
+  Encoder::CodedImageData& codedImage = codingResult.value;
 
   auto infe_box = ctx->get_heif_file()->add_new_infe_box(get_infe_type());
   heif_item_id image_id = infe_box->get_item_ID();
@@ -530,119 +540,13 @@ int ImageItem::get_chroma_bits_per_pixel
 }
 
 
-static std::shared_ptr<color_profile_nclx> compute_target_nclx_profile(const std::shared_ptr<HeifPixelImage>& image, const heif_color_profile_nclx* output_nclx_profile)
+Result<Encoder::CodedImageData> ImageItem::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                  struct heif_encoder* h_encoder,
+                                                  const struct heif_encoding_options& options,
+                                                  enum heif_image_input_class input_class)
 {
-  auto target_nclx_profile = std::make_shared<color_profile_nclx>();
-
-  // If there is an output NCLX specified, use that.
-  if (output_nclx_profile) {
-    target_nclx_profile->set_from_heif_color_profile_nclx(output_nclx_profile);
-  }
-    // Otherwise, if there is an input NCLX, keep that.
-  else if (auto input_nclx = image->get_color_profile_nclx()) {
-    *target_nclx_profile = *input_nclx;
-  }
-    // Otherwise, just use the defaults (set below)
-  else {
-    target_nclx_profile->set_undefined();
-  }
-
-  target_nclx_profile->replace_undefined_values_with_sRGB_defaults();
-
-  return target_nclx_profile;
-}
-
-
-static bool nclx_profile_matches_spec(heif_colorspace colorspace,
-                                      std::shared_ptr<const color_profile_nclx> image_nclx,
-                                      const struct heif_color_profile_nclx* spec_nclx)
-{
-  if (colorspace != heif_colorspace_YCbCr) {
-    return true;
-  }
-
-  // No target specification -> always matches
-  if (!spec_nclx) {
-    return true;
-  }
-
-  if (!image_nclx) {
-    // if no input nclx is specified, compare against default one
-    image_nclx = std::make_shared<color_profile_nclx>();
-  }
-
-  if (image_nclx->get_full_range_flag() != (spec_nclx->full_range_flag == 0 ? false : true)) {
-    return false;
-  }
-
-  if (image_nclx->get_matrix_coefficients() != spec_nclx->matrix_coefficients) {
-    return false;
-  }
-
-  // TODO: are the colour primaries relevant for matrix-coefficients != 12,13 ?
-  //       If not, we should skip this test for anything else than matrix-coefficients != 12,13.
-  if (image_nclx->get_colour_primaries() != spec_nclx->color_primaries) {
-    return false;
-  }
-
-  return true;
-}
-
-
-Result<std::shared_ptr<HeifPixelImage>> ImageItem::convert_colorspace_for_encoding(const std::shared_ptr<HeifPixelImage>& image,
-                                                                                   struct heif_encoder* encoder,
-                                                                                   const struct heif_encoding_options& options)
-{
-  const heif_color_profile_nclx* output_nclx_profile;
-
-  if (const auto* nclx = get_forced_output_nclx()) {
-    output_nclx_profile = nclx;
-  }
-  else {
-    output_nclx_profile = options.output_nclx_profile;
-  }
-
-
-  heif_colorspace colorspace = image->get_colorspace();
-  heif_chroma chroma = image->get_chroma_format();
-
-  if (encoder->plugin->plugin_api_version >= 2) {
-    encoder->plugin->query_input_colorspace2(encoder->encoder, &colorspace, &chroma);
-  }
-  else {
-    encoder->plugin->query_input_colorspace(&colorspace, &chroma);
-  }
-
-
-  // If output format forces an NCLX, use that. Otherwise use user selected NCLX.
-
-  std::shared_ptr<color_profile_nclx> target_nclx_profile = compute_target_nclx_profile(image, output_nclx_profile);
-
-  // --- convert colorspace
-
-  std::shared_ptr<HeifPixelImage> output_image;
-
-  if (colorspace == image->get_colorspace() &&
-      chroma == image->get_chroma_format() &&
-      nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), output_nclx_profile)) {
-    return image;
-  }
-
-
-  // @TODO: use color profile when converting
-  int output_bpp = 0; // same as input
-
-  //auto target_nclx = std::make_shared<color_profile_nclx>();
-  //target_nclx->set_from_heif_color_profile_nclx(target_heif_nclx);
-
-  auto output_image_result = convert_colorspace(image, colorspace, chroma, target_nclx_profile,
-                                                output_bpp, options.color_conversion_options,
-                                                get_context()->get_security_limits());
-  if (output_image_result.error) {
-    return output_image_result.error;
-  }
-
-  return *output_image_result;
+  auto encoder = get_encoder();
+  return encoder->encode(image, h_encoder, options, input_class);
 }
 
 
@@ -650,7 +554,7 @@ void ImageItem::add_color_profile(const
                                   const struct heif_encoding_options& options,
                                   enum heif_image_input_class input_class,
                                   const heif_color_profile_nclx* target_heif_nclx,
-                                  ImageItem::CodedImageData& inout_codedImage)
+                                  Encoder::CodedImageData& inout_codedImage)
 {
   if (input_class == heif_image_input_class_normal || input_class == heif_image_input_class_thumbnail) {
     auto icc_profile = image->get_color_profile_icc();
@@ -688,27 +592,6 @@ void ImageItem::add_color_profile(const
 }
 
 
-void ImageItem::CodedImageData::append(const uint8_t* data, size_t size)
-{
-  bitstream.insert(bitstream.end(), data, data + size);
-}
-
-
-void ImageItem::CodedImageData::append_with_4bytes_size(const uint8_t* data, size_t size)
-{
-  assert(size <= 0xFFFFFFFF);
-
-  uint8_t size_field[4];
-  size_field[0] = (uint8_t) ((size >> 24) & 0xFF);
-  size_field[1] = (uint8_t) ((size >> 16) & 0xFF);
-  size_field[2] = (uint8_t) ((size >> 8) & 0xFF);
-  size_field[3] = (uint8_t) ((size >> 0) & 0xFF);
-
-  bitstream.insert(bitstream.end(), size_field, size_field + 4);
-  bitstream.insert(bitstream.end(), data, data + size);
-}
-
-
 Error ImageItem::transform_requested_tile_position_to_original_tile_position(uint32_t& tile_x, uint32_t& tile_y) const
 {
   Result<std::vector<std::shared_ptr<Box>>> propertiesResult = get_properties();
@@ -884,6 +767,10 @@ Result<std::shared_ptr<HeifPixelImage>>
 
   std::shared_ptr<ImageItem> alpha_image = get_alpha_channel();
   if (alpha_image) {
+    if (alpha_image->get_item_error()) {
+      return alpha_image->get_item_error();
+    }
+
     auto alphaDecodingResult = alpha_image->decode_image(options, decode_tile_only, tile_x0, tile_y0);
     if (alphaDecodingResult.error) {
       return alphaDecodingResult.error;
@@ -974,6 +861,13 @@ Result<std::shared_ptr<HeifPixelImage>>
     if (pasp) {
       img->set_pixel_ratio(pasp->hSpacing, pasp->vSpacing);
     }
+
+    // TAI
+
+    auto itai = get_property<Box_itai>();
+    if (itai) {
+      img->set_tai_timestamp(itai->get_tai_timestamp_packet());
+    }
   }
 
   return img;
@@ -994,32 +888,6 @@ Result<std::vector<uint8_t>> ImageItem::
 }
 #endif
 
-Result<std::vector<uint8_t>> ImageItem::get_compressed_image_data() const
-{
-  // TODO: Remove this later when decoding is done through Decoder.
-
-  // --- get the compressed image data
-
-  // data from configuration blocks
-
-  Result<std::vector<uint8_t>> confData = read_bitstream_configuration_data();
-  if (confData.error) {
-    return confData.error;
-  }
-
-  std::vector<uint8_t> data = confData.value;
-
-  // image data, usually from 'mdat'
-
-  Error error = m_heif_context->get_heif_file()->append_data_from_iloc(m_id, data);
-  if (error) {
-    return error;
-  }
-
-  return data;
-}
-
-
 Result<std::shared_ptr<HeifPixelImage>> ImageItem::decode_compressed_image(const struct heif_decoding_options& options,
                                                                            bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
 {
@@ -1035,7 +903,8 @@ Result<std::shared_ptr<HeifPixelImage>>
 
   decoder->set_data_extent(std::move(extent));
 
-  return decoder->decode_single_frame_from_compressed_data(options);
+  return decoder->decode_single_frame_from_compressed_data(options,
+                                                           get_context()->get_security_limits());
 }
 
 
@@ -1080,6 +949,24 @@ Result<std::vector<std::shared_ptr<Box>>
 }
 
 
+bool ImageItem::has_essential_property_other_than(const std::set<uint32_t>& props) const
+{
+  Result<std::vector<std::shared_ptr<Box>>> propertiesResult = get_properties();
+  if (propertiesResult.error) {
+    return false;
+  }
+
+  for (const auto& property : *propertiesResult) {
+    if (property->is_essential() &&
+        props.find(property->get_short_type()) == props.end()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+
 Error ImageItem::process_image_transformations_on_tiling(heif_image_tiling& tiling) const
 {
   Result<std::vector<std::shared_ptr<Box>>> propertiesResult = get_properties();
diff -pruN 1.19.8-1/libheif/image-items/image_item.h 1.20.1-1/libheif/image-items/image_item.h
--- 1.19.8-1/libheif/image-items/image_item.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/image_item.h	2025-07-02 13:05:31.000000000 +0000
@@ -28,7 +28,10 @@
 #include <vector>
 #include <memory>
 #include <utility>
+#include <set>
 #include "api/libheif/heif_plugin.h"
+#include "codecs/encoder.h"
+
 
 class HeifContext;
 
@@ -49,9 +52,9 @@ public:
 class ImageItem : public ErrorBuffer
 {
 public:
-  ImageItem(HeifContext* file);
+  ImageItem(HeifContext* ctx);
 
-  ImageItem(HeifContext* file, heif_item_id id);
+  ImageItem(HeifContext* ctx, heif_item_id id);
 
   virtual ~ImageItem() = default;
 
@@ -63,10 +66,6 @@ public:
 
   static uint32_t compression_format_to_fourcc_infe_type(heif_compression_format);
 
-  Result<std::shared_ptr<HeifPixelImage>> convert_colorspace_for_encoding(const std::shared_ptr<HeifPixelImage>& image,
-                                                                          struct heif_encoder* encoder,
-                                                                          const struct heif_encoding_options& options);
-
   virtual uint32_t get_infe_type() const { return 0; }
 
   virtual const char* get_auxC_alpha_channel_type() const { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; }
@@ -75,9 +74,6 @@ public:
 
   virtual Error get_item_error() const { return Error::Ok; }
 
-  // If the output format requires a specific nclx (like JPEG), return this. Otherwise, return NULL.
-  virtual const heif_color_profile_nclx* get_forced_output_nclx() const { return nullptr; }
-
   virtual heif_compression_format get_compression_format() const { return heif_compression_undefined; }
 
   virtual Result<std::vector<uint8_t>> read_bitstream_configuration_data() const { return std::vector<uint8_t>{}; }
@@ -277,11 +273,13 @@ public:
 
   // --- miaf
 
-  // TODO: we should have a function that challs all MIAF constraints and sets the compatibility flag.
+  // TODO: we should have a function that checks all MIAF constraints and sets the compatibility flag.
   void mark_not_miaf_compatible() { m_miaf_compatible = false; }
 
   bool is_miaf_compatible() const { return m_miaf_compatible; }
 
+  // return 0 if we don't know the brand
+  virtual heif_brand2 get_compatible_brand() const { return 0; }
 
   // === decoding ===
 
@@ -295,30 +293,16 @@ public:
   virtual Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const struct heif_decoding_options& options,
                                                                           bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const;
 
-  virtual Result<std::vector<uint8_t>> get_compressed_image_data() const;
-
   Result<std::vector<std::shared_ptr<Box>>> get_properties() const;
 
-  // === encoding ===
-
-  struct CodedImageData
-  {
-    std::vector<std::shared_ptr<Box>> properties;
-    std::vector<uint8_t> bitstream;
-
-    // If 0, the encoded size is equal to the input size.
-    uint32_t encoded_image_width = 0;
-    uint32_t encoded_image_height = 0;
-
-    void append(const uint8_t* data, size_t size);
+  bool has_essential_property_other_than(const std::set<uint32_t>&) const;
 
-    void append_with_4bytes_size(const uint8_t* data, size_t size);
-  };
+  // === encoding ===
 
-  Result<CodedImageData> encode_to_bitstream_and_boxes(const std::shared_ptr<HeifPixelImage>& image,
-                                                       struct heif_encoder* encoder,
-                                                       const struct heif_encoding_options& options,
-                                                       enum heif_image_input_class input_class);
+  Result<Encoder::CodedImageData> encode_to_bitstream_and_boxes(const std::shared_ptr<HeifPixelImage>& image,
+                                                                struct heif_encoder* encoder,
+                                                                const struct heif_encoding_options& options,
+                                                                enum heif_image_input_class input_class);
 
   Error encode_to_item(HeifContext* ctx,
                        const std::shared_ptr<HeifPixelImage>& image,
@@ -391,6 +375,8 @@ public:
     };
   }
 
+  virtual std::shared_ptr<class Encoder> get_encoder() const { return nullptr; }
+
 private:
   HeifContext* m_heif_context;
   std::vector<std::shared_ptr<Box>> m_properties;
@@ -437,10 +423,10 @@ private:
 protected:
   // Result<std::vector<uint8_t>> read_bitstream_configuration_data_override(heif_item_id itemId, heif_compression_format format) const;
 
-  virtual Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                        struct heif_encoder* encoder,
-                                        const struct heif_encoding_options& options,
-                                        enum heif_image_input_class input_class) { return {}; }
+  virtual Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                 struct heif_encoder* encoder,
+                                                 const struct heif_encoding_options& options,
+                                                 enum heif_image_input_class input_class);
 
   // --- encoding utility functions
 
@@ -448,7 +434,7 @@ protected:
                                 const struct heif_encoding_options& options,
                                 enum heif_image_input_class input_class,
                                 const heif_color_profile_nclx* target_heif_nclx,
-                                ImageItem::CodedImageData& inout_codedImage);
+                                Encoder::CodedImageData& inout_codedImage);
 };
 
 
diff -pruN 1.19.8-1/libheif/image-items/jpeg.cc 1.20.1-1/libheif/image-items/jpeg.cc
--- 1.19.8-1/libheif/image-items/jpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/jpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -20,6 +20,7 @@
 
 #include "jpeg.h"
 #include "codecs/jpeg_dec.h"
+#include "codecs/jpeg_enc.h"
 #include "codecs/jpeg_boxes.h"
 #include "security_limits.h"
 #include "pixelimage.h"
@@ -28,87 +29,16 @@
 #include <utility>
 
 
-static uint8_t JPEG_SOS = 0xDA;
-
-
-const heif_color_profile_nclx* ImageItem_JPEG::get_forced_output_nclx() const
+ImageItem_JPEG::ImageItem_JPEG(HeifContext* ctx, heif_item_id id)
+    : ImageItem(ctx, id)
 {
-  // JPEG always uses CCIR-601
-
-  static heif_color_profile_nclx target_heif_nclx;
-  target_heif_nclx.version = 1;
-  target_heif_nclx.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
-  target_heif_nclx.color_primaries = heif_color_primaries_ITU_R_BT_601_6;
-  target_heif_nclx.transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_601_6;
-  target_heif_nclx.full_range_flag = true;
-
-  return &target_heif_nclx;
+  m_encoder = std::make_shared<Encoder_JPEG>();
 }
 
-
-Result<ImageItem::CodedImageData> ImageItem_JPEG::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                         struct heif_encoder* encoder,
-                                                         const struct heif_encoding_options& options,
-                                                         enum heif_image_input_class input_class)
+ImageItem_JPEG::ImageItem_JPEG(HeifContext* ctx)
+    : ImageItem(ctx)
 {
-  CodedImageData codedImage;
-
-
-  heif_image c_api_image;
-  c_api_image.image = image;
-
-  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
-  if (err.code) {
-    return Error(err.code,
-                 err.subcode,
-                 err.message);
-  }
-
-  std::vector<uint8_t> vec;
-
-  for (;;) {
-    uint8_t* data;
-    int size;
-
-    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
-
-    if (data == nullptr) {
-      break;
-    }
-
-    size_t oldsize = vec.size();
-    vec.resize(oldsize + size);
-    memcpy(vec.data() + oldsize, data, size);
-  }
-
-#if 0
-  // Optional: split the JPEG data into a jpgC box and the actual image data.
-  // Currently disabled because not supported yet in other decoders.
-  if (false) {
-    size_t pos = find_jpeg_marker_start(vec, JPEG_SOS);
-    if (pos > 0) {
-      std::vector<uint8_t> jpgC_data(vec.begin(), vec.begin() + pos);
-      auto jpgC = std::make_shared<Box_jpgC>();
-      jpgC->set_data(jpgC_data);
-
-      auto ipma_box = m_heif_file->get_ipma_box();
-      int index = m_heif_file->get_ipco_box()->find_or_append_child_box(jpgC);
-      ipma_box->add_property_for_item_ID(image_id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
-
-      std::vector<uint8_t> image_data(vec.begin() + pos, vec.end());
-      vec = std::mo ve(image_data);
-    }
-  }
-#endif
-  (void) JPEG_SOS;
-
-  codedImage.bitstream = std::move(vec);
-
-#if 0
-  // TODO: extract 'jpgC' header data
-#endif
-
-  return {codedImage};
+  m_encoder = std::make_shared<Encoder_JPEG>();
 }
 
 
@@ -123,6 +53,13 @@ Result<std::shared_ptr<Decoder>> ImageIt
   return {m_decoder};
 }
 
+
+std::shared_ptr<Encoder> ImageItem_JPEG::get_encoder() const
+{
+  return m_encoder;
+}
+
+
 Error ImageItem_JPEG::on_load_file()
 {
   // Note: jpgC box is optional. NULL is a valid value.
@@ -137,3 +74,8 @@ Error ImageItem_JPEG::on_load_file()
 
   return Error::Ok;
 }
+
+heif_brand2 ImageItem_JPEG::get_compatible_brand() const
+{
+  return heif_brand2_jpeg;
+}
diff -pruN 1.19.8-1/libheif/image-items/jpeg.h 1.20.1-1/libheif/image-items/jpeg.h
--- 1.19.8-1/libheif/image-items/jpeg.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/jpeg.h	2025-07-02 13:05:31.000000000 +0000
@@ -31,33 +31,29 @@
 class ImageItem_JPEG : public ImageItem
 {
 public:
-  ImageItem_JPEG(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) { }
+  ImageItem_JPEG(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_JPEG(HeifContext* ctx) : ImageItem(ctx) { }
+  ImageItem_JPEG(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("jpeg"); }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override;
-
   heif_compression_format get_compression_format() const override { return heif_compression_JPEG; }
 
 
   Error on_load_file() override;
 
-public:
-
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                        struct heif_encoder* encoder,
-                                        const struct heif_encoding_options& options,
-                                        enum heif_image_input_class input_class) override;
+  heif_brand2 get_compatible_brand() const override;
 
 protected:
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
+  std::shared_ptr<Encoder> get_encoder() const override;
+
   Result<std::vector<uint8_t>> read_bitstream_configuration_data() const override;
 
 private:
   std::shared_ptr<class Decoder_JPEG> m_decoder;
+  std::shared_ptr<class Encoder_JPEG> m_encoder;
 };
 
 #endif // LIBHEIF_JPEG_H
diff -pruN 1.19.8-1/libheif/image-items/jpeg2000.cc 1.20.1-1/libheif/image-items/jpeg2000.cc
--- 1.19.8-1/libheif/image-items/jpeg2000.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/jpeg2000.cc	2025-07-02 13:05:31.000000000 +0000
@@ -21,51 +21,23 @@
 #include "jpeg2000.h"
 #include "libheif/api_structs.h"
 #include "codecs/jpeg2000_dec.h"
+#include "codecs/jpeg2000_enc.h"
 #include "codecs/jpeg2000_boxes.h"
 #include <cstdint>
-#include <iostream>
-#include <cstdio>
 #include <utility>
 
 
-
-Result<ImageItem::CodedImageData> ImageItem_JPEG2000::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                             struct heif_encoder* encoder,
-                                                             const struct heif_encoding_options& options,
-                                                             enum heif_image_input_class input_class)
+ImageItem_JPEG2000::ImageItem_JPEG2000(HeifContext* ctx, heif_item_id id)
+    : ImageItem(ctx, id)
 {
-  CodedImageData codedImageData;
-
-  heif_image c_api_image;
-  c_api_image.image = image;
-
-  encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
-
-  // get compressed data
-  for (;;) {
-    uint8_t* data;
-    int size;
-
-    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr);
-
-    if (data == nullptr) {
-      break;
-    }
-
-    codedImageData.append(data, size);
-  }
-
-  // add 'j2kH' property
-  auto j2kH = std::make_shared<Box_j2kH>();
-
-  // add 'cdef' to 'j2kH'
-  auto cdef = std::make_shared<Box_cdef>();
-  cdef->set_channels(image->get_colorspace());
-  j2kH->append_child_box(cdef);
+  m_encoder = std::make_shared<Encoder_JPEG2000>();
+}
 
-  codedImageData.properties.push_back(j2kH);
 
-  return codedImageData;
+ImageItem_JPEG2000::ImageItem_JPEG2000(HeifContext* ctx)
+    : ImageItem(ctx)
+{
+  m_encoder = std::make_shared<Encoder_JPEG2000>();
 }
 
 
@@ -88,11 +60,19 @@ Result<std::vector<uint8_t>> ImageItem_J
   return std::vector<uint8_t>{};
 }
 
+
 Result<std::shared_ptr<Decoder>> ImageItem_JPEG2000::get_decoder() const
 {
   return {m_decoder};
 }
 
+
+std::shared_ptr<class Encoder> ImageItem_JPEG2000::get_encoder() const
+{
+  return m_encoder;
+}
+
+
 Error ImageItem_JPEG2000::on_load_file()
 {
   auto j2kH = get_property<Box_j2kH>();
@@ -111,3 +91,8 @@ Error ImageItem_JPEG2000::on_load_file()
 
   return Error::Ok;
 }
+
+heif_brand2 ImageItem_JPEG2000::get_compatible_brand() const
+{
+  return heif_brand2_j2ki;
+}
diff -pruN 1.19.8-1/libheif/image-items/jpeg2000.h 1.20.1-1/libheif/image-items/jpeg2000.h
--- 1.19.8-1/libheif/image-items/jpeg2000.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/jpeg2000.h	2025-07-02 13:05:31.000000000 +0000
@@ -33,29 +33,29 @@
 class ImageItem_JPEG2000 : public ImageItem
 {
 public:
-  ImageItem_JPEG2000(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {}
+  ImageItem_JPEG2000(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_JPEG2000(HeifContext* ctx) : ImageItem(ctx) {}
+  ImageItem_JPEG2000(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("j2k1"); }
 
   heif_compression_format get_compression_format() const override { return heif_compression_JPEG2000; }
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  heif_brand2 get_compatible_brand() const override;
 
 protected:
   Result<std::vector<uint8_t>> read_bitstream_configuration_data() const override;
 
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
+  std::shared_ptr<Encoder> get_encoder() const override;
+
 public:
   Error on_load_file() override;
 
 private:
   std::shared_ptr<class Decoder_JPEG2000> m_decoder;
+  std::shared_ptr<class Encoder_JPEG2000> m_encoder;
 };
 
 #endif // LIBHEIF_JPEG2000_H
diff -pruN 1.19.8-1/libheif/image-items/mask_image.cc 1.20.1-1/libheif/image-items/mask_image.cc
--- 1.19.8-1/libheif/image-items/mask_image.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/mask_image.cc	2025-07-02 13:05:31.000000000 +0000
@@ -154,12 +154,12 @@ Result<std::shared_ptr<HeifPixelImage>>
 }
 
 
-Result<ImageItem::CodedImageData> ImageItem_mask::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                         struct heif_encoder* encoder,
-                                                         const struct heif_encoding_options& options,
-                                                         enum heif_image_input_class input_class)
+Result<Encoder::CodedImageData> ImageItem_mask::encode(const std::shared_ptr<HeifPixelImage>& image,
+                                                       struct heif_encoder* encoder,
+                                                       const struct heif_encoding_options& options,
+                                                       enum heif_image_input_class input_class)
 {
-  CodedImageData codedImageData;
+  Encoder::CodedImageData codedImageData;
 
   if (image->get_colorspace() != heif_colorspace_monochrome)
   {
diff -pruN 1.19.8-1/libheif/image-items/mask_image.h 1.20.1-1/libheif/image-items/mask_image.h
--- 1.19.8-1/libheif/image-items/mask_image.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/mask_image.h	2025-07-02 13:05:31.000000000 +0000
@@ -89,8 +89,6 @@ public:
 
   uint32_t get_infe_type() const override { return fourcc("mski"); }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
-
   heif_compression_format get_compression_format() const override { return heif_compression_mask; }
 
   bool is_ispe_essential() const override { return true; }
@@ -102,10 +100,10 @@ public:
   Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const struct heif_decoding_options& options,
                                                                   bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override;
 };
 
 #endif //LIBHEIF_MASK_IMAGE_H
diff -pruN 1.19.8-1/libheif/image-items/overlay.cc 1.20.1-1/libheif/image-items/overlay.cc
--- 1.19.8-1/libheif/image-items/overlay.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/overlay.cc	2025-07-02 13:05:31.000000000 +0000
@@ -38,7 +38,7 @@ void writevec(uint8_t* data, size_t& idx
 
 static int32_t readvec_signed(const std::vector<uint8_t>& data, int& ptr, int len)
 {
-  const uint32_t high_bit = 0x80 << ((len - 1) * 8);
+  const uint32_t high_bit = UINT32_C(0x80) << ((len - 1) * 8);
 
   uint32_t val = 0;
   while (len--) {
@@ -345,7 +345,7 @@ Result<std::shared_ptr<HeifPixelImage>>
 
     if (overlay_img->get_colorspace() != heif_colorspace_RGB ||
         overlay_img->get_chroma_format() != heif_chroma_444) {
-      auto overlay_img_result = convert_colorspace(overlay_img, heif_colorspace_RGB, heif_chroma_444, nullptr, 0, options.color_conversion_options,
+      auto overlay_img_result = convert_colorspace(overlay_img, heif_colorspace_RGB, heif_chroma_444, nullptr, 0, options.color_conversion_options, options.color_conversion_options_ext,
                                                    get_context()->get_security_limits());
       if (overlay_img_result.error) {
         return overlay_img_result.error;
@@ -457,3 +457,14 @@ Result<std::shared_ptr<ImageItem_Overlay
 
   return iovl_image;
 }
+
+heif_brand2 ImageItem_Overlay::get_compatible_brand() const
+{
+  if (m_overlay_image_ids.empty()) { return 0; }
+
+  heif_item_id child_id = m_overlay_image_ids[0];
+  auto child = get_context()->get_image(child_id, false);
+  if (!child) { return 0; }
+
+  return child->get_compatible_brand();
+}
diff -pruN 1.19.8-1/libheif/image-items/overlay.h 1.20.1-1/libheif/image-items/overlay.h
--- 1.19.8-1/libheif/image-items/overlay.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/overlay.h	2025-07-02 13:05:31.000000000 +0000
@@ -105,10 +105,12 @@ public:
 
   Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const override;
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override
+  heif_brand2 get_compatible_brand() const override;
+
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override
   {
     return Error{heif_error_Unsupported_feature,
                  heif_suberror_Unspecified, "Cannot encode image to 'iovl'"};
diff -pruN 1.19.8-1/libheif/image-items/tiled.cc 1.20.1-1/libheif/image-items/tiled.cc
--- 1.19.8-1/libheif/image-items/tiled.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/tiled.cc	2025-07-02 13:05:31.000000000 +0000
@@ -667,7 +667,8 @@ Error ImageItem_Tiled::add_image_tile(ui
 
   heif_encoding_options* options = heif_encoding_options_alloc(); // TODO: should this be taken from heif_context_add_tiled_image() ?
 
-  Result<std::shared_ptr<HeifPixelImage>> colorConversionResult = item->convert_colorspace_for_encoding(image, encoder, *options);
+  Result<std::shared_ptr<HeifPixelImage>> colorConversionResult;
+  colorConversionResult = item->get_encoder()->convert_colorspace_for_encoding(image, encoder, *options, get_context()->get_security_limits());
   if (colorConversionResult.error) {
     heif_encoding_options_free(options);
     return colorConversionResult.error;
@@ -675,7 +676,7 @@ Error ImageItem_Tiled::add_image_tile(ui
 
   std::shared_ptr<HeifPixelImage> colorConvertedImage = colorConversionResult.value;
 
-  Result<ImageItem::CodedImageData> encodeResult = item->encode_to_bitstream_and_boxes(colorConvertedImage, encoder, *options, heif_image_input_class_normal); // TODO (other than JPEG)
+  Result<Encoder::CodedImageData> encodeResult = item->encode_to_bitstream_and_boxes(colorConvertedImage, encoder, *options, heif_image_input_class_normal); // TODO (other than JPEG)
   heif_encoding_options_free(options);
 
   if (encodeResult.error) {
@@ -735,8 +736,8 @@ Error ImageItem_Tiled::add_image_tile(ui
     }
   }
 
-  get_file()->set_brand(encoder->plugin->compression_format,
-                        true); // TODO: out_grid_image->is_miaf_compatible());
+  //get_file()->set_brand(encoder->plugin->compression_format,
+  //                      true); // TODO: out_grid_image->is_miaf_compatible());
 
   return Error::Ok;
 }
@@ -830,7 +831,8 @@ ImageItem_Tiled::decode_grid_tile(const
 
   m_tile_decoder->set_data_extent(std::move(*extentResult));
 
-  return m_tile_decoder->decode_single_frame_from_compressed_data(options);
+  return m_tile_decoder->decode_single_frame_from_compressed_data(options,
+                                                                  get_context()->get_security_limits());
 }
 
 
@@ -909,4 +911,18 @@ int ImageItem_Tiled::get_chroma_bits_per
   m_tile_decoder->set_data_extent(std::move(any_tile_extent));
 
   return m_tile_decoder->get_chroma_bits_per_pixel();
-}
\ No newline at end of file
+}
+
+heif_brand2 ImageItem_Tiled::get_compatible_brand() const
+{
+  return 0;
+
+  // TODO: it is not clear to me what brand to use here.
+
+  /*
+  switch (m_tild_header.get_parameters().compression_format_fourcc) {
+    case heif_compression_HEVC:
+      return heif_brand2_heic;
+  }
+   */
+}
diff -pruN 1.19.8-1/libheif/image-items/tiled.h 1.20.1-1/libheif/image-items/tiled.h
--- 1.19.8-1/libheif/image-items/tiled.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/tiled.h	2025-07-02 13:05:31.000000000 +0000
@@ -171,10 +171,11 @@ public:
 
   int get_chroma_bits_per_pixel() const override;
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override {
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override
+  {
     return Error{heif_error_Unsupported_feature,
                  heif_suberror_Unspecified, "Cannot encode image to 'tild'"};
   }
@@ -182,6 +183,8 @@ public:
   Result<std::shared_ptr<HeifPixelImage>> decode_compressed_image(const struct heif_decoding_options& options,
                                                                   bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override;
 
+  heif_brand2 get_compatible_brand() const override;
+
   // --- tild
 
   void set_tild_header(const TiledHeader& header) { m_tild_header = header; }
diff -pruN 1.19.8-1/libheif/image-items/unc_image.cc 1.20.1-1/libheif/image-items/unc_image.cc
--- 1.19.8-1/libheif/image-items/unc_image.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/unc_image.cc	2025-07-02 13:05:31.000000000 +0000
@@ -35,6 +35,7 @@
 #include "codecs/uncompressed/unc_boxes.h"
 #include "unc_image.h"
 #include "codecs/uncompressed/unc_dec.h"
+#include "codecs/uncompressed/unc_enc.h"
 #include "codecs/uncompressed/unc_codec.h"
 #include "image_item.h"
 
@@ -61,6 +62,19 @@ static void maybe_make_minimised_uncC(st
 }
 
 
+ImageItem_uncompressed::ImageItem_uncompressed(HeifContext* ctx, heif_item_id id)
+    : ImageItem(ctx, id)
+{
+  m_encoder = std::make_shared<Encoder_uncompressed>();
+}
+
+ImageItem_uncompressed::ImageItem_uncompressed(HeifContext* ctx)
+    : ImageItem(ctx)
+{
+  m_encoder = std::make_shared<Encoder_uncompressed>();
+}
+
+
 Result<std::shared_ptr<HeifPixelImage>> ImageItem_uncompressed::decode_compressed_image(const struct heif_decoding_options& options,
                                                                                 bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const
 {
@@ -257,28 +271,38 @@ Result<std::vector<uint8_t>> encode_imag
 }
 
 
-Result<ImageItem::CodedImageData> ImageItem_uncompressed::encode(const std::shared_ptr<HeifPixelImage>& src_image,
+Result<Encoder::CodedImageData> ImageItem_uncompressed::encode(const std::shared_ptr<HeifPixelImage>& src_image,
                                                                  struct heif_encoder* encoder,
                                                                  const struct heif_encoding_options& options,
                                                                  enum heif_image_input_class input_class)
 {
-  heif_unci_image_parameters parameters{};
-  parameters.image_width = src_image->get_width();
-  parameters.image_height = src_image->get_height();
-  parameters.tile_width = parameters.image_width;
-  parameters.tile_height = parameters.image_height;
+  return encode_static(src_image, options);
+}
+
+
+Result<Encoder::CodedImageData> ImageItem_uncompressed::encode_static(const std::shared_ptr<HeifPixelImage>& src_image,
+                                                               const struct heif_encoding_options& options)
+{
+  auto parameters = std::unique_ptr<heif_unci_image_parameters,
+                                    void (*)(heif_unci_image_parameters*)>(heif_unci_image_parameters_alloc(),
+                                                                           heif_unci_image_parameters_release);
+
+  parameters->image_width = src_image->get_width();
+  parameters->image_height = src_image->get_height();
+  parameters->tile_width = parameters->image_width;
+  parameters->tile_height = parameters->image_height;
 
 
   // --- generate configuration property boxes
 
-  Result<unciHeaders> genHeadersResult = generate_headers(src_image, &parameters, &options);
+  Result<unciHeaders> genHeadersResult = generate_headers(src_image, parameters.get(), &options);
   if (genHeadersResult.error) {
     return genHeadersResult.error;
   }
 
   const unciHeaders& headers = *genHeadersResult;
 
-  CodedImageData codedImageData;
+  Encoder::CodedImageData codedImageData;
   if (headers.uncC) {
     codedImageData.properties.push_back(headers.uncC);
   }
@@ -396,7 +420,7 @@ Result<std::shared_ptr<ImageItem_uncompr
   }
 
   // Set Brands
-  ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible());
+  //ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible());
 
   return {unci_image};
 }
@@ -510,10 +534,16 @@ Result<std::shared_ptr<Decoder>> ImageIt
   return {m_decoder};
 }
 
+std::shared_ptr<Encoder> ImageItem_uncompressed::get_encoder() const
+{
+  return m_encoder;
+}
+
 Error ImageItem_uncompressed::on_load_file()
 {
   std::shared_ptr<Box_cmpd> cmpd = get_property<Box_cmpd>();
   std::shared_ptr<Box_uncC> uncC = get_property<Box_uncC>();
+  std::shared_ptr<Box_ispe> ispe = get_property<Box_ispe>();
 
   if (!uncC) {
     return Error{heif_error_Invalid_input,
@@ -521,7 +551,7 @@ Error ImageItem_uncompressed::on_load_fi
                  "No 'uncC' box found."};
   }
 
-  m_decoder = std::make_shared<Decoder_uncompressed>(uncC, cmpd);
+  m_decoder = std::make_shared<Decoder_uncompressed>(uncC, cmpd, ispe);
 
   DataExtent extent;
   extent.set_from_image_item(get_context()->get_heif_file(), get_id());
@@ -536,3 +566,8 @@ bool ImageItem_uncompressed::has_coded_a
 {
   return m_decoder->has_alpha_component();
 }
+
+heif_brand2 ImageItem_uncompressed::get_compatible_brand() const
+{
+  return 0; // TODO: not clear to me what to use
+}
diff -pruN 1.19.8-1/libheif/image-items/unc_image.h 1.20.1-1/libheif/image-items/unc_image.h
--- 1.19.8-1/libheif/image-items/unc_image.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/unc_image.h	2025-07-02 13:05:31.000000000 +0000
@@ -25,6 +25,7 @@
 #include "pixelimage.h"
 #include "file.h"
 #include "context.h"
+#include "libheif/heif_uncompressed.h"
 
 #include <cstdint>
 #include <string>
@@ -37,9 +38,9 @@ class HeifContext;
 class ImageItem_uncompressed : public ImageItem
 {
 public:
-  ImageItem_uncompressed(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {}
+  ImageItem_uncompressed(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_uncompressed(HeifContext* ctx) : ImageItem(ctx) {}
+  ImageItem_uncompressed(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("unci"); }
 
@@ -50,7 +51,7 @@ public:
 
   bool has_coded_alpha_channel() const override;
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
+  // const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
 
   bool is_ispe_essential() const override { return true; }
 
@@ -65,14 +66,19 @@ public:
 
   Error on_load_file() override;
 
+  heif_brand2 get_compatible_brand() const override;
+
 public:
 
   // --- encoding
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
+  Result<Encoder::CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
+                                         struct heif_encoder* encoder,
+                                         const struct heif_encoding_options& options,
+                                         enum heif_image_input_class input_class) override;
+
+  static Result<Encoder::CodedImageData> encode_static(const std::shared_ptr<HeifPixelImage>& image,
+                                                       const struct heif_encoding_options& options);
 
   static Result<std::shared_ptr<ImageItem_uncompressed>> add_unci_item(HeifContext* ctx,
                                                                 const heif_unci_image_parameters* parameters,
@@ -84,8 +90,12 @@ public:
 protected:
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
+  std::shared_ptr<Encoder> get_encoder() const override;
+
 private:
   std::shared_ptr<class Decoder_uncompressed> m_decoder;
+  std::shared_ptr<class Encoder_uncompressed> m_encoder;
+
   /*
   Result<ImageItem::CodedImageData> generate_headers(const std::shared_ptr<const HeifPixelImage>& src_image,
                                                      const heif_unci_image_parameters* parameters,
diff -pruN 1.19.8-1/libheif/image-items/vvc.cc 1.20.1-1/libheif/image-items/vvc.cc
--- 1.19.8-1/libheif/image-items/vvc.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/vvc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -20,6 +20,7 @@
 
 #include "vvc.h"
 #include "codecs/vvc_dec.h"
+#include "codecs/vvc_enc.h"
 #include "codecs/vvc_boxes.h"
 #include <cstring>
 #include <string>
@@ -28,69 +29,14 @@
 #include <utility>
 
 
-Result<ImageItem::CodedImageData> ImageItem_VVC::encode(const std::shared_ptr<HeifPixelImage>& image,
-                                                        struct heif_encoder* encoder,
-                                                        const struct heif_encoding_options& options,
-                                                        enum heif_image_input_class input_class)
+ImageItem_VVC::ImageItem_VVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id)
 {
-  CodedImageData codedImage;
-
-  auto vvcC = std::make_shared<Box_vvcC>();
-  codedImage.properties.push_back(vvcC);
-
-
-  heif_image c_api_image;
-  c_api_image.image = image;
-
-  struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class);
-  if (err.code) {
-    return Error(err.code,
-                 err.subcode,
-                 err.message);
-  }
-
-  int encoded_width = 0;
-  int encoded_height = 0;
-
-  for (;;) {
-    uint8_t* data;
-    int size;
-
-    encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, NULL);
-
-    if (data == NULL) {
-      break;
-    }
-
-
-    const uint8_t NAL_SPS = 15;
-
-    uint8_t nal_type = 0;
-    if (size>=2) {
-      nal_type = (data[1] >> 3) & 0x1F;
-    }
-
-    if (nal_type == NAL_SPS) {
-      Box_vvcC::configuration config;
-
-      parse_sps_for_vvcC_configuration(data, size, &config, &encoded_width, &encoded_height);
-
-      vvcC->set_configuration(config);
-    }
-
-    switch (nal_type) {
-      case 14: // VPS
-      case 15: // SPS
-      case 16: // PPS
-        vvcC->append_nal_data(data, size);
-        break;
-
-      default:
-        codedImage.append_with_4bytes_size(data, size);
-    }
-  }
+  m_encoder = std::make_shared<Encoder_VVC>();
+}
 
-  return codedImage;
+ImageItem_VVC::ImageItem_VVC(HeifContext* ctx) : ImageItem(ctx)
+{
+  m_encoder = std::make_shared<Encoder_VVC>();
 }
 
 
@@ -122,6 +68,13 @@ Result<std::shared_ptr<Decoder>> ImageIt
   return {m_decoder};
 }
 
+
+std::shared_ptr<class Encoder> ImageItem_VVC::get_encoder() const
+{
+  return m_encoder;
+}
+
+
 Error ImageItem_VVC::on_load_file()
 {
   auto vvcC_box = get_property<Box_vvcC>();
@@ -139,3 +92,8 @@ Error ImageItem_VVC::on_load_file()
 
   return Error::Ok;
 }
+
+heif_brand2 ImageItem_VVC::get_compatible_brand() const
+{
+  return heif_brand2_vvic;
+}
diff -pruN 1.19.8-1/libheif/image-items/vvc.h 1.20.1-1/libheif/image-items/vvc.h
--- 1.19.8-1/libheif/image-items/vvc.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/image-items/vvc.h	2025-07-02 13:05:31.000000000 +0000
@@ -31,25 +31,22 @@
 class ImageItem_VVC : public ImageItem
 {
 public:
-  ImageItem_VVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {}
+  ImageItem_VVC(HeifContext* ctx, heif_item_id id);
 
-  ImageItem_VVC(HeifContext* ctx) : ImageItem(ctx) {}
+  ImageItem_VVC(HeifContext* ctx);
 
   uint32_t get_infe_type() const override { return fourcc("vvc1"); }
 
   const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; }
 
-  const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; }
-
   heif_compression_format get_compression_format() const override { return heif_compression_VVC; }
 
-  Result<CodedImageData> encode(const std::shared_ptr<HeifPixelImage>& image,
-                                struct heif_encoder* encoder,
-                                const struct heif_encoding_options& options,
-                                enum heif_image_input_class input_class) override;
-
   Error on_load_file() override;
 
+  std::shared_ptr<class Encoder> get_encoder() const override;
+
+  heif_brand2 get_compatible_brand() const override;
+
 protected:
   Result<std::shared_ptr<Decoder>> get_decoder() const override;
 
@@ -57,6 +54,7 @@ protected:
 
 private:
   std::shared_ptr<class Decoder_VVC> m_decoder;
+  std::shared_ptr<class Encoder_VVC> m_encoder;
 };
 
 #endif // LIBHEIF_VVC_H
diff -pruN 1.19.8-1/libheif/mdat_data.h 1.20.1-1/libheif/mdat_data.h
--- 1.19.8-1/libheif/mdat_data.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/mdat_data.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,65 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_MDAT_DATA_H
+#define LIBHEIF_MDAT_DATA_H
+
+#include <cstdint>
+#include <vector>
+#include <cassert>
+#include <algorithm>
+
+
+class MdatData
+{
+public:
+  virtual ~MdatData() = default;
+
+  // returns the start position of the appended data
+  virtual size_t append_data(const std::vector<uint8_t>& data) = 0;
+
+  virtual size_t get_data_size() const = 0;
+
+  virtual Error write(StreamWriter&) = 0;
+};
+
+
+class MdatData_Memory : public MdatData
+{
+public:
+  size_t append_data(const std::vector<uint8_t>& data) override {
+    size_t startPos = m_data.size();
+    m_data.insert(m_data.end(), data.begin(), data.end());
+    return startPos;
+  }
+
+  size_t get_data_size() const override { return m_data.size(); }
+
+  Error write(StreamWriter& writer) override
+  {
+    writer.write(m_data);
+    return Error::Ok;
+  }
+
+private:
+  std::vector<uint8_t> m_data;
+};
+
+#endif //LIBHEIF_MDAT_DATA_H
diff -pruN 1.19.8-1/libheif/pixelimage.cc 1.20.1-1/libheif/pixelimage.cc
--- 1.19.8-1/libheif/pixelimage.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/pixelimage.cc	2025-07-02 13:05:31.000000000 +0000
@@ -95,6 +95,8 @@ HeifPixelImage::~HeifPixelImage()
   for (auto& iter : m_planes) {
     delete[] iter.second.allocated_mem;
   }
+
+  heif_tai_timestamp_packet_release(m_tai_timestamp);
 }
 
 
@@ -207,7 +209,7 @@ Error HeifPixelImage::add_plane(heif_cha
     bit_depth = 8;
   }
 
-  if (auto err = plane.alloc(width, height, heif_channel_datatype_unsigned_integer, bit_depth, num_interleaved_pixels, limits)) {
+  if (auto err = plane.alloc(width, height, heif_channel_datatype_unsigned_integer, bit_depth, num_interleaved_pixels, limits, m_memory_handle)) {
     return err;
   }
   else {
@@ -221,7 +223,7 @@ Error HeifPixelImage::add_channel(heif_c
                                   const heif_security_limits* limits)
 {
   ImagePlane plane;
-  if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits)) {
+  if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
     return err;
   }
   else {
@@ -233,11 +235,18 @@ Error HeifPixelImage::add_channel(heif_c
 
 Error HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
                                         int num_interleaved_components,
-                                        const heif_security_limits* limits)
+                                        const heif_security_limits* limits,
+                                        MemoryHandle& memory_handle)
 {
   assert(bit_depth >= 1);
   assert(bit_depth <= 128);
 
+  if (width == 0 || height == 0) {
+    return {heif_error_Usage_error,
+            heif_suberror_Unspecified,
+            "Invalid image size"};
+  }
+
   // use 16 byte alignment (enough for 128 bit data-types). Every row is an integer number of data-elements.
   uint16_t alignment = 16; // must be power of two
 
@@ -263,42 +272,49 @@ Error HeifPixelImage::ImagePlane::alloc(
   assert(alignment>=1);
 
   if (limits &&
-      limits->max_memory_block_size &&
-      (limits->max_memory_block_size < alignment - 1U ||
-       (limits->max_memory_block_size - (alignment - 1U)) / stride < m_mem_height)) {
+      limits->max_image_size_pixels &&
+      limits->max_image_size_pixels / height < width) {
+
     std::stringstream sstr;
-    sstr << "Allocating " << static_cast<size_t>(m_mem_height) * stride + alignment - 1 << " bytes exceeds the security limit of "
-         << limits->max_memory_block_size << " bytes";
+    sstr << "Allocating an image of size " << width << "x" << height << " exceeds the security limit of "
+         << limits->max_image_size_pixels << " pixels";
 
     return {heif_error_Memory_allocation_error,
             heif_suberror_Security_limit_exceeded,
             sstr.str()};
   }
 
-  try {
-    allocated_mem = new uint8_t[static_cast<size_t>(m_mem_height) * stride + alignment - 1];
-    uint8_t* mem_8 = allocated_mem;
-
-    // shift beginning of image data to aligned memory position
+  allocation_size = static_cast<size_t>(m_mem_height) * stride + alignment - 1;
 
-    auto mem_start_addr = (uint64_t) mem_8;
-    auto mem_start_offset = (mem_start_addr & (alignment - 1U));
-    if (mem_start_offset != 0) {
-      mem_8 += alignment - mem_start_offset;
-    }
+  if (auto err = memory_handle.alloc(allocation_size, limits, "image data")) {
+    return err;
+  }
 
-    mem = mem_8;
+    // --- allocate memory
 
-    return Error::Ok;
-  }
-  catch (const std::bad_alloc& excpt) {
+  allocated_mem = new (std::nothrow) uint8_t[allocation_size];
+  if (allocated_mem == nullptr) {
     std::stringstream sstr;
-    sstr << "Allocating " << static_cast<size_t>(m_mem_height) * stride + alignment - 1 << " bytes failed";
+    sstr << "Allocating " << allocation_size << " bytes failed";
 
     return {heif_error_Memory_allocation_error,
             heif_suberror_Unspecified,
             sstr.str()};
   }
+
+  uint8_t* mem_8 = allocated_mem;
+
+  // shift beginning of image data to aligned memory position
+
+  auto mem_start_addr = (uint64_t) mem_8;
+  auto mem_start_offset = (mem_start_addr & (alignment - 1U));
+  if (mem_start_offset != 0) {
+    mem_8 += alignment - mem_start_offset;
+  }
+
+  mem = mem_8;
+
+  return Error::Ok;
 }
 
 
@@ -323,7 +339,7 @@ Error HeifPixelImage::extend_padding_to_
       ImagePlane newPlane;
       if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth,
                                     num_interleaved_pixels_per_plane(m_chroma),
-                                    limits))
+                                    limits, m_memory_handle))
       {
         return err;
       }
@@ -399,7 +415,7 @@ Error HeifPixelImage::extend_to_size_wit
         plane->m_mem_height < subsampled_height) {
 
       ImagePlane newPlane;
-      if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma), limits)) {
+      if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma), limits, m_memory_handle)) {
         return err;
       }
 
@@ -696,9 +712,7 @@ void HeifPixelImage::fill_plane(heif_cha
   else {
     uint16_t* dst;
     size_t dst_stride = 0;
-    dst = (uint16_t*) get_plane(dst_channel, &dst_stride);
-
-    dst_stride /= 2;
+    dst = get_channel<uint16_t>(dst_channel, &dst_stride);
 
     for (uint32_t y = 0; y < height; y++) {
       for (uint32_t x = 0; x < width * num_interleaved; x++) {
@@ -717,8 +731,14 @@ void HeifPixelImage::transfer_plane_from
 
   ImagePlane plane = source->m_planes[src_channel];
   source->m_planes.erase(src_channel);
+  source->m_memory_handle.free(plane.allocation_size);
 
   m_planes.insert(std::make_pair(dst_channel, plane));
+
+  // Note: we assume that image planes are never transferred between heif_contexts
+  m_memory_handle.alloc(plane.allocation_size,
+                        source->m_memory_handle.get_security_limits(),
+                        "transferred image data");
 }
 
 
@@ -826,7 +846,7 @@ Result<std::shared_ptr<HeifPixelImage>>
     heif_color_conversion_options options{};
     heif_color_conversion_options_set_defaults(&options);
 
-    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits);
+    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
     if (converted_image_result.error) {
       return converted_image_result.error;
     }
@@ -982,7 +1002,7 @@ Result<std::shared_ptr<HeifPixelImage>>
     heif_color_conversion_options options{};
     heif_color_conversion_options_set_defaults(&options);
 
-    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits);
+    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
     if (converted_image_result.error) {
       return converted_image_result.error;
     }
@@ -1062,7 +1082,7 @@ Result<std::shared_ptr<HeifPixelImage>>
     heif_color_conversion_options options{};
     heif_color_conversion_options_set_defaults(&options);
 
-    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits);
+    auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, nullptr, limits);
     if (converted_image_result.error) {
       return converted_image_result.error;
     }
@@ -1082,10 +1102,10 @@ Result<std::shared_ptr<HeifPixelImage>>
     heif_channel channel = plane_pair.first;
     const ImagePlane& plane = plane_pair.second;
 
-    uint32_t plane_left = get_subsampled_size_h(left, channel, m_chroma, scaling_mode::is_divisible);
-    uint32_t plane_right = get_subsampled_size_h(right, channel, m_chroma, scaling_mode::round_up); // keep more chroma
-    uint32_t plane_top = get_subsampled_size_v(top, channel, m_chroma, scaling_mode::is_divisible); // is always divisible
-    uint32_t plane_bottom = get_subsampled_size_v(bottom, channel, m_chroma, scaling_mode::round_up); // keep more chroma
+    uint32_t plane_left = get_subsampled_size_h(left, channel, m_chroma, scaling_mode::is_divisible); // is always divisible
+    uint32_t plane_right = get_subsampled_size_h(right, channel, m_chroma, scaling_mode::round_down); // this keeps enough chroma since 'right' is a coordinate and not the width
+    uint32_t plane_top = get_subsampled_size_v(top, channel, m_chroma, scaling_mode::is_divisible);
+    uint32_t plane_bottom = get_subsampled_size_v(bottom, channel, m_chroma, scaling_mode::round_down);
 
     auto err = out_img->add_channel(channel,
                                     plane_right - plane_left + 1,
@@ -1250,11 +1270,8 @@ Error HeifPixelImage::overlay(std::share
     uint32_t out_w = get_width(channel);
     uint32_t out_h = get_height(channel);
 
-    // top-left points where to start copying in source and destination
-    uint32_t in_x0;
-    uint32_t in_y0;
-    uint32_t out_x0;
-    uint32_t out_y0;
+
+    // --- check whether overlay image overlaps with current image
 
     if (dx > 0 && static_cast<uint32_t>(dx) >= out_w) {
       // the overlay image is completely outside the right border -> skip overlaying
@@ -1265,37 +1282,54 @@ Error HeifPixelImage::overlay(std::share
       return Error::Ok;
     }
 
-    if (dx < 0) {
-      // overlay image started partially outside of left border
-
-      in_x0 = negate_negative_int32(dx);
-      out_x0 = 0;
-      in_w = in_w - in_x0; // in_x0 < in_w because in_w > -dx = in_x0
+    if (dy > 0 && static_cast<uint32_t>(dy) >= out_h) {
+      // the overlay image is completely outside the bottom border -> skip overlaying
+      return Error::Ok;
     }
-    else {
-      in_x0 = 0;
-      out_x0 = static_cast<uint32_t>(dx);
+    else if (dy < 0 && in_h <= negate_negative_int32(dy)) {
+      // the overlay image is completely outside the top border -> skip overlaying
+      return Error::Ok;
     }
 
-    // we know that dx >= 0 && dx < out_w
 
-    if (static_cast<uint32_t>(dx) > UINT32_MAX - in_w ||
-        dx + in_w > out_w) {
+    // --- compute overlapping area
+
+    // top-left points where to start copying in source and destination
+    uint32_t in_x0;
+    uint32_t in_y0;
+    uint32_t out_x0;
+    uint32_t out_y0;
+
+    // right border
+    if (dx + static_cast<int64_t>(in_w) > out_w) {
       // overlay image extends partially outside of right border
+      // Notes:
+      // - (out_w-dx) cannot underflow because dx<out_w is ensured above
+      // - (out_w-dx) cannot overflow (for dx<0) because, as just checked, out_w-dx < in_w
+      //              and in_w fits into uint32_t
+      in_w = static_cast<uint32_t>(static_cast<int64_t>(out_w) - dx);
+    }
 
-      in_w = out_w - static_cast<uint32_t>(dx); // we know that dx < out_w from first condition
+    // bottom border
+    if (dy + static_cast<int64_t>(in_h) > out_h) {
+      // overlay image extends partially outside of bottom border
+      in_h = static_cast<uint32_t>(static_cast<int64_t>(out_h) - dy);
     }
 
+    // left border
+    if (dx < 0) {
+      // overlay image starts partially outside of left border
 
-    if (dy > 0 && static_cast<uint32_t>(dy) >= out_h) {
-      // the overlay image is completely outside the bottom border -> skip overlaying
-      return Error::Ok;
+      in_x0 = negate_negative_int32(dx);
+      out_x0 = 0;
+      in_w = in_w - in_x0; // in_x0 < in_w because in_w > -dx = in_x0
     }
-    else if (dy < 0 && in_h <= negate_negative_int32(dy)) {
-      // the overlay image is completely outside the top border -> skip overlaying
-      return Error::Ok;
+    else {
+      in_x0 = 0;
+      out_x0 = static_cast<uint32_t>(dx);
     }
 
+    // top border
     if (dy < 0) {
       // overlay image started partially outside of top border
 
@@ -1308,15 +1342,7 @@ Error HeifPixelImage::overlay(std::share
       out_y0 = static_cast<uint32_t>(dy);
     }
 
-    // we know that dy >= 0 && dy < out_h
-
-    if (static_cast<uint32_t>(dy) > UINT32_MAX - in_h ||
-        dy + in_h > out_h) {
-      // overlay image extends partially outside of bottom border
-
-      in_h = out_h - static_cast<uint32_t>(dy); // we know that dy < out_h from first condition
-    }
-
+    // --- computer overlay in overlapping area
 
     for (uint32_t y = in_y0; y < in_h; y++) {
       if (!has_alpha) {
@@ -1415,49 +1441,111 @@ Error HeifPixelImage::scale_nearest_neig
 
   // --- scale all channels
 
-  for (const auto& plane_pair : m_planes) {
-    heif_channel channel = plane_pair.first;
-    const ImagePlane& plane = plane_pair.second;
+  int nInterleaved = num_interleaved_pixels_per_plane(m_chroma);
+  if (nInterleaved > 1) {
+    auto plane_iter = m_planes.find(heif_channel_interleaved);
+    assert(plane_iter != m_planes.end()); // the plane must exist since we have an interleaved chroma format
+    const ImagePlane& plane = plane_iter->second;
 
-    const int bpp = get_storage_bits_per_pixel(channel) / 8;
+    uint32_t out_w = out_img->get_width(heif_channel_interleaved);
+    uint32_t out_h = out_img->get_height(heif_channel_interleaved);
 
-    if (!out_img->has_channel(channel)) {
-      return {heif_error_Invalid_input, heif_suberror_Unspecified, "scaling input has extra color plane"};
-    }
+    if (plane.m_bit_depth <= 8) {
+      // SDR interleaved
 
+      size_t in_stride = plane.stride;
+      const auto* in_data = static_cast<const uint8_t*>(plane.mem);
 
-    if (plane.m_bit_depth != 8) {
-      return {heif_error_Unsupported_feature,
-              heif_suberror_Unspecified,
-              "Can currently only crop images with 8 bits per pixel"};
-    }
+      size_t out_stride = 0;
+      auto* out_data = out_img->get_plane(heif_channel_interleaved, &out_stride);
 
-    uint32_t out_w = out_img->get_width(channel);
-    uint32_t out_h = out_img->get_height(channel);
+      for (uint32_t y = 0; y < out_h; y++) {
+        uint32_t iy = y * m_height / height;
 
-    size_t in_stride = plane.stride;
-    const auto* in_data = static_cast<const uint8_t*>(plane.mem);
+        for (uint32_t x = 0; x < out_w; x++) {
+          uint32_t ix = x * m_width / width;
 
-    size_t out_stride = 0;
-    auto* out_data = static_cast<uint8_t*>(out_img->get_plane(channel, &out_stride));
+          for (int c = 0; c < nInterleaved; c++) {
+            out_data[y * out_stride + x * nInterleaved + c] = in_data[iy * in_stride + ix * nInterleaved + c];
+          }
+        }
+      }
+    }
+    else {
+      // HDR interleaved
+      // TODO: untested
+
+      size_t in_stride = plane.stride;
+      const uint16_t* in_data = static_cast<const uint16_t*>(plane.mem);
 
+      size_t out_stride = 0;
+      uint16_t* out_data = out_img->get_channel<uint16_t>(heif_channel_interleaved, &out_stride);
 
-    for (uint32_t y = 0; y < out_h; y++) {
-      uint32_t iy = y * m_height / height;
+      in_stride /= 2;
+
+      for (uint32_t y = 0; y < out_h; y++) {
+        uint32_t iy = y * m_height / height;
 
-      if (bpp == 1) {
         for (uint32_t x = 0; x < out_w; x++) {
           uint32_t ix = x * m_width / width;
 
-          out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
+          for (int c = 0; c < nInterleaved; c++) {
+            out_data[y * out_stride + x * nInterleaved + c] = in_data[iy * in_stride + ix * nInterleaved + c];
+          }
+        }
+      }
+    }
+  }
+  else {
+    for (const auto& plane_pair : m_planes) {
+      heif_channel channel = plane_pair.first;
+      const ImagePlane& plane = plane_pair.second;
+
+      if (!out_img->has_channel(channel)) {
+        return {heif_error_Invalid_input, heif_suberror_Unspecified, "scaling input has extra color plane"};
+      }
+
+
+      uint32_t out_w = out_img->get_width(channel);
+      uint32_t out_h = out_img->get_height(channel);
+
+      if (plane.m_bit_depth <= 8) {
+        // SDR planar
+
+        size_t in_stride = plane.stride;
+        const auto* in_data = static_cast<const uint8_t*>(plane.mem);
+
+        size_t out_stride = 0;
+        auto* out_data = out_img->get_plane(channel, &out_stride);
+
+        for (uint32_t y = 0; y < out_h; y++) {
+          uint32_t iy = y * m_height / height;
+
+          for (uint32_t x = 0; x < out_w; x++) {
+            uint32_t ix = x * m_width / width;
+
+            out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
+          }
         }
       }
       else {
-        for (uint32_t x = 0; x < out_w; x++) {
-          uint32_t ix = x * m_width / width;
+        // HDR planar
+
+        size_t in_stride = plane.stride;
+        const uint16_t* in_data = static_cast<const uint16_t*>(plane.mem);
+
+        size_t out_stride = 0;
+        uint16_t* out_data = out_img->get_channel<uint16_t>(channel, &out_stride);
+
+        in_stride /= 2;
+
+        for (uint32_t y = 0; y < out_h; y++) {
+          uint32_t iy = y * m_height / height;
+
+          for (uint32_t x = 0; x < out_w; x++) {
+            uint32_t ix = x * m_width / width;
 
-          for (int b = 0; b < bpp; b++) {
-            out_data[y * out_stride + bpp * x + b] = in_data[iy * in_stride + bpp * ix + b];
+            out_data[y * out_stride + x] = in_data[iy * in_stride + ix];
           }
         }
       }
diff -pruN 1.19.8-1/libheif/pixelimage.h 1.20.1-1/libheif/pixelimage.h
--- 1.19.8-1/libheif/pixelimage.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/pixelimage.h	2025-07-02 13:05:31.000000000 +0000
@@ -26,6 +26,7 @@
 #include "error.h"
 #include "nclx.h"
 #include <libheif/heif_experimental.h>
+#include "security_limits.h"
 
 #include <vector>
 #include <memory>
@@ -33,7 +34,7 @@
 #include <set>
 #include <utility>
 #include <cassert>
-
+#include <string>
 
 heif_chroma chroma_from_subsampling(int h, int v);
 
@@ -238,17 +239,42 @@ public:
 
   // --- mdcv
 
-  bool has_mdcv() const { return m_mdcv_set; }
+  bool has_mdcv() const { return m_mdcv.has_value(); }
 
-  heif_mastering_display_colour_volume get_mdcv() const { return m_mdcv; }
+  heif_mastering_display_colour_volume get_mdcv() const { return *m_mdcv; }
 
   void set_mdcv(const heif_mastering_display_colour_volume& mdcv)
   {
     m_mdcv = mdcv;
-    m_mdcv_set = true;
   }
 
-  void unset_mdcv() { m_mdcv_set = false; }
+  void unset_mdcv() { m_mdcv.reset(); }
+
+  Error set_tai_timestamp(const heif_tai_timestamp_packet* tai) {
+    delete m_tai_timestamp;
+
+    m_tai_timestamp = heif_tai_timestamp_packet_alloc();
+    heif_tai_timestamp_packet_copy(m_tai_timestamp, tai);
+    return Error::Ok;
+  }
+
+  const heif_tai_timestamp_packet* get_tai_timestamp() const {
+    return m_tai_timestamp;
+  }
+
+
+  void set_gimi_sample_content_id(std::string id) { m_gimi_sample_content_id = id; }
+
+  bool has_gimi_sample_content_id() const { return m_gimi_sample_content_id.has_value(); }
+
+  std::string get_gimi_sample_content_id() const { assert(has_gimi_sample_content_id()); return *m_gimi_sample_content_id; }
+
+
+  // --- sequences
+
+  void set_sample_duration(uint32_t d) { m_sample_duration = d; }
+
+  uint32_t get_sample_duration() const { return m_sample_duration; }
 
   // --- warnings
 
@@ -262,8 +288,10 @@ private:
   struct ImagePlane
   {
     // limits=nullptr disables the limits
-    Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, int num_interleaved_components,
-                const heif_security_limits* limits);
+    Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
+                int num_interleaved_components,
+                const heif_security_limits* limits,
+                MemoryHandle& memory_handle);
 
     heif_channel_datatype m_datatype = heif_channel_datatype_unsigned_integer;
     uint8_t m_bit_depth = 0;
@@ -279,6 +307,7 @@ private:
 
     void* mem = nullptr; // aligned memory start
     uint8_t* allocated_mem = nullptr; // unaligned memory we allocated
+    size_t   allocation_size = 0;
     uint32_t stride = 0; // bytes per line
 
     int get_bytes_per_pixel() const;
@@ -300,12 +329,18 @@ private:
   std::shared_ptr<const color_profile_raw> m_color_profile_icc;
 
   std::map<heif_channel, ImagePlane> m_planes;
+  MemoryHandle m_memory_handle;
 
   uint32_t m_PixelAspectRatio_h = 1;
   uint32_t m_PixelAspectRatio_v = 1;
   heif_content_light_level m_clli{};
-  heif_mastering_display_colour_volume m_mdcv{};
-  bool m_mdcv_set = false; // replace with std::optional<> when we are on C*+17
+  std::optional<heif_mastering_display_colour_volume> m_mdcv;
+
+  uint32_t m_sample_duration = 0; // duration of a sequence frame
+
+  heif_tai_timestamp_packet* m_tai_timestamp = nullptr;
+
+  std::optional<std::string> m_gimi_sample_content_id;
 
   std::vector<Error> m_warnings;
 };
diff -pruN 1.19.8-1/libheif/plugins/CMakeLists.txt 1.20.1-1/libheif/plugins/CMakeLists.txt
--- 1.19.8-1/libheif/plugins/CMakeLists.txt	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/CMakeLists.txt	2025-07-02 13:05:31.000000000 +0000
@@ -54,7 +54,7 @@ set(LIBDE265_extra_plugin_sources ../err
 plugin_compilation(libde265 LIBDE265 LIBDE265_FOUND LIBDE265 LIBDE265)
 
 set(DAV1D_sources decoder_dav1d.cc decoder_dav1d.h)
-set(DAV1D_extra_plugin_sources ../common_utils.cc ../common_utils.h)
+set(DAV1D_extra_plugin_sources ../error.cc ../common_utils.cc ../common_utils.h)
 plugin_compilation(dav1d DAV1D DAV1D_FOUND DAV1D DAV1D)
 
 set(AOM_DECODER_sources decoder_aom.cc decoder_aom.h)
diff -pruN 1.19.8-1/libheif/plugins/decoder_aom.cc 1.20.1-1/libheif/plugins/decoder_aom.cc
--- 1.19.8-1/libheif/plugins/decoder_aom.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_aom.cc	2025-07-02 13:05:31.000000000 +0000
@@ -24,6 +24,7 @@
 #include <memory>
 #include <cstring>
 #include <cassert>
+#include <string>
 
 #include <aom/aom_decoder.h>
 #include <aom/aomdx.h>
@@ -37,6 +38,7 @@ struct aom_decoder
   aom_codec_iface_t* iface;
 
   bool strict_decoding = false;
+  std::string error_message;
 };
 
 static const char kSuccess[] = "Success";
@@ -152,7 +154,8 @@ struct heif_error aom_push_data(void* de
 }
 
 
-struct heif_error aom_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error aom_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                        const heif_security_limits* limits)
 {
   struct aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
 
@@ -253,14 +256,18 @@ struct heif_error aom_decode_image(void*
       w = (w + 1) / 2;
     }
 
-    err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp);
+    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
     if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
       heif_image_release(heif_img);
       return err;
     }
 
-    int dst_stride;
-    uint8_t* dst_mem = heif_image_get_plane(heif_img, channel2plane[c], &dst_stride);
+    size_t dst_stride;
+    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
 
     int bytes_per_pixel = (bpp + 7) / 8;
 
@@ -273,10 +280,15 @@ struct heif_error aom_decode_image(void*
   return err;
 }
 
+struct heif_error aom_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return aom_decode_next_image(decoder_raw, out_img, limits);
+}
 
 static const struct heif_decoder_plugin decoder_aom
     {
-        3,
+        4,
         aom_plugin_name,
         aom_init_plugin,
         aom_deinit_plugin,
@@ -286,7 +298,8 @@ static const struct heif_decoder_plugin
         aom_push_data,
         aom_decode_image,
         aom_set_strict_decoding,
-        "aom"
+        "aom",
+        aom_decode_next_image
     };
 
 
diff -pruN 1.19.8-1/libheif/plugins/decoder_dav1d.cc 1.20.1-1/libheif/plugins/decoder_dav1d.cc
--- 1.19.8-1/libheif/plugins/decoder_dav1d.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_dav1d.cc	2025-07-02 13:05:31.000000000 +0000
@@ -29,6 +29,7 @@
 #include <cstdio>
 #include <limits>
 #include <utility>
+#include <string>
 
 #include <dav1d/version.h>
 #include <dav1d/dav1d.h>
@@ -39,6 +40,7 @@ struct dav1d_decoder
   Dav1dContext* context;
   Dav1dData data;
   bool strict_decoding = false;
+  std::string error_message;
 };
 
 static const char kEmptyString[] = "";
@@ -161,7 +163,8 @@ struct heif_error dav1d_push_data(void*
 }
 
 
-struct heif_error dav1d_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error dav1d_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                          const heif_security_limits* limits)
 {
   auto* decoder = (struct dav1d_decoder*) decoder_raw;
 
@@ -273,14 +276,18 @@ struct heif_error dav1d_decode_image(voi
     get_subsampled_size(frame.p.w, frame.p.h,
                         channel2plane[c], chroma, &w, &h);
 
-    err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp);
+    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
     if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
       heif_image_release(heif_img);
       return err;
     }
 
-    int dst_stride;
-    uint8_t* dst_mem = heif_image_get_plane(heif_img, channel2plane[c], &dst_stride);
+    size_t dst_stride;
+    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
 
     int bytes_per_pixel = (bpp + 7) / 8;
 
@@ -299,9 +306,16 @@ struct heif_error dav1d_decode_image(voi
 }
 
 
+struct heif_error dav1d_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return dav1d_decode_next_image(decoder_raw, out_img, limits);
+}
+
+
 static const struct heif_decoder_plugin decoder_dav1d
     {
-        3,
+        4,
         dav1d_plugin_name,
         dav1d_init_plugin,
         dav1d_deinit_plugin,
@@ -311,7 +325,8 @@ static const struct heif_decoder_plugin
         dav1d_push_data,
         dav1d_decode_image,
         dav1d_set_strict_decoding,
-        "dav1d"
+        "dav1d",
+        dav1d_decode_next_image
     };
 
 
diff -pruN 1.19.8-1/libheif/plugins/decoder_ffmpeg.cc 1.20.1-1/libheif/plugins/decoder_ffmpeg.cc
--- 1.19.8-1/libheif/plugins/decoder_ffmpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_ffmpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -22,6 +22,7 @@
 #include "libheif/heif_plugin.h"
 #include "decoder_ffmpeg.h"
 #include "nalu_utils.h"
+#include <string>
 
 #if defined(HAVE_CONFIG_H)
 #include "config.h"
@@ -31,7 +32,7 @@
 #include <memory>
 #include <utility>
 
-extern "C" 
+extern "C"
 {
     #include <libavcodec/avcodec.h>
 }
@@ -41,6 +42,7 @@ struct ffmpeg_decoder
 {
     NalMap nalMap;
     bool strict_decoding = false;
+    std::string error_message;
 };
 
 static const int FFMPEG_DECODER_PLUGIN_PRIORITY = 90;
@@ -118,25 +120,37 @@ static struct heif_error ffmpeg_v1_push_
 
 
 static heif_chroma ffmpeg_get_chroma_format(enum AVPixelFormat pix_fmt) {
-    if (pix_fmt == AV_PIX_FMT_GRAY8)
-    {
-        return heif_chroma_monochrome;
-    }
-    else if ((pix_fmt == AV_PIX_FMT_YUV420P) || (pix_fmt == AV_PIX_FMT_YUVJ420P) ||
-        (pix_fmt == AV_PIX_FMT_YUV420P10LE))
-    {
-        return heif_chroma_420;
-    }
-    else if (pix_fmt == AV_PIX_FMT_YUV422P)
-    {
-        return heif_chroma_422;
-    }
-    else if (pix_fmt == AV_PIX_FMT_YUV444P)
-    {
-        return heif_chroma_444;
-    }
-    // Unsupported pix_fmt
-    return heif_chroma_undefined;
+  switch (pix_fmt) {
+    case AV_PIX_FMT_GRAY8:
+    case AV_PIX_FMT_GRAY10LE:
+      return heif_chroma_monochrome;
+
+    case AV_PIX_FMT_YUV420P:
+    case AV_PIX_FMT_YUVJ420P:
+    case AV_PIX_FMT_YUV420P10LE:
+    case AV_PIX_FMT_YUV420P12LE:
+    case AV_PIX_FMT_YUV420P14LE:
+    case AV_PIX_FMT_YUV420P16LE:
+      return heif_chroma_420;
+
+    case AV_PIX_FMT_YUV422P:
+    case AV_PIX_FMT_YUV422P10LE:
+    case AV_PIX_FMT_YUV422P12LE:
+    case AV_PIX_FMT_YUV422P14LE:
+    case AV_PIX_FMT_YUV422P16LE:
+      return heif_chroma_422;
+
+    case AV_PIX_FMT_YUV444P:
+    case AV_PIX_FMT_YUV444P10LE:
+    case AV_PIX_FMT_YUV444P12LE:
+    case AV_PIX_FMT_YUV444P14LE:
+    case AV_PIX_FMT_YUV444P16LE:
+      return heif_chroma_444;
+
+    default:
+      // Unsupported pix_fmt
+      return heif_chroma_undefined;
+  }
 }
 
 static int ffmpeg_get_chroma_width(const AVFrame* frame, heif_channel channel, heif_chroma chroma)
@@ -171,7 +185,42 @@ static int ffmpeg_get_chroma_height(cons
     }
 }
 
-static struct heif_error hevc_decode(AVCodecContext* hevc_dec_ctx, AVFrame* hevc_frame, AVPacket* hevc_pkt, struct heif_image** image)
+static int get_ffmpeg_format_bpp(enum AVPixelFormat pix_fmt)
+{
+  switch (pix_fmt) {
+    case AV_PIX_FMT_GRAY8:
+    case AV_PIX_FMT_YUV420P:
+    case AV_PIX_FMT_YUVJ420P:
+    case AV_PIX_FMT_YUV422P:
+    case AV_PIX_FMT_YUV444P:
+      return 8;
+    case AV_PIX_FMT_GRAY10LE:
+    case AV_PIX_FMT_YUV420P10LE:
+    case AV_PIX_FMT_YUV422P10LE:
+    case AV_PIX_FMT_YUV444P10LE:
+      return 10;
+    case AV_PIX_FMT_GRAY12LE:
+    case AV_PIX_FMT_YUV420P12LE:
+    case AV_PIX_FMT_YUV422P12LE:
+    case AV_PIX_FMT_YUV444P12LE:
+      return 12;
+    case AV_PIX_FMT_GRAY14LE:
+    case AV_PIX_FMT_YUV420P14LE:
+    case AV_PIX_FMT_YUV422P14LE:
+    case AV_PIX_FMT_YUV444P14LE:
+      return 14;
+    case AV_PIX_FMT_GRAY16LE:
+    case AV_PIX_FMT_YUV420P16LE:
+    case AV_PIX_FMT_YUV422P16LE:
+    case AV_PIX_FMT_YUV444P16LE:
+      return 16;
+    default:
+      return 0;
+  }
+}
+
+static struct heif_error hevc_decode(ffmpeg_decoder* decoder, AVCodecContext* hevc_dec_ctx, AVFrame* hevc_frame, AVPacket* hevc_pkt, struct heif_image** image,
+                                     const heif_security_limits* limits)
 {
     int ret;
 
@@ -218,7 +267,15 @@ static struct heif_error hevc_decode(AVC
 
         for (int channel = 0; channel < nPlanes; channel++) {
 
-            int bpp = (hevc_dec_ctx->pix_fmt == AV_PIX_FMT_YUV420P10LE) ? 10 : 8;
+            int bpp = get_ffmpeg_format_bpp(hevc_dec_ctx->pix_fmt);
+            if (bpp == 0) {
+              heif_image_release(*image);
+              err = { heif_error_Decoder_plugin_error,
+                      heif_suberror_Unsupported_color_conversion,
+                      "Pixel format not implemented" };
+              return err;
+            }
+
             int stride = hevc_frame->linesize[channel];
             const uint8_t* data = hevc_frame->data[channel];
 
@@ -232,14 +289,18 @@ static struct heif_error hevc_decode(AVC
                 return err;
             }
 
-            err = heif_image_add_plane(*image, channel2plane[channel], w, h, bpp);
+            err = heif_image_add_plane_safe(*image, channel2plane[channel], w, h, bpp, limits);
             if (err.code) {
+              // copy error message to decoder object because heif_image will be released
+              decoder->error_message = err.message;
+              err.message = decoder->error_message.c_str();
+
                 heif_image_release(*image);
                 return err;
             }
 
-            int dst_stride;
-            uint8_t* dst_mem = heif_image_get_plane(*image, channel2plane[channel], &dst_stride);
+            size_t dst_stride;
+            uint8_t* dst_mem = heif_image_get_plane2(*image, channel2plane[channel], &dst_stride);
 
             int bytes_per_pixel = (bpp + 7) / 8;
 
@@ -259,8 +320,9 @@ static struct heif_error hevc_decode(AVC
     }
 }
 
-static struct heif_error ffmpeg_v1_decode_image(void* decoder_raw,
-                                                  struct heif_image** out_img)
+static struct heif_error ffmpeg_v1_decode_next_image(void* decoder_raw,
+                                                     struct heif_image** out_img,
+                                                     const heif_security_limits* limits)
 {
   struct ffmpeg_decoder* decoder = (struct ffmpeg_decoder*) decoder_raw;
 
@@ -411,7 +473,7 @@ static struct heif_error ffmpeg_v1_decod
   while (parse_hevc_data_size > 0) {
       hevc_parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
       ret = av_parser_parse2(hevc_parser, hevc_codecContext, &hevc_pkt->data, &hevc_pkt->size, parse_hevc_data, parse_hevc_data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
- 
+
       if (ret < 0) {
 	err = { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "av_parser_parse2 returned error" };
 	goto errexit;
@@ -421,7 +483,7 @@ static struct heif_error ffmpeg_v1_decod
 
       if (hevc_pkt->size)
       {
-	err = hevc_decode(hevc_codecContext, hevc_frame, hevc_pkt, out_img);
+	err = hevc_decode(decoder, hevc_codecContext, hevc_frame, hevc_pkt, out_img, limits);
 	if (err.code != heif_error_Ok)
 	  goto errexit;
       }
@@ -462,9 +524,17 @@ errexit:
   return err;
 }
 
+static struct heif_error ffmpeg_v1_decode_image(void* decoder_raw,
+                                                struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return ffmpeg_v1_decode_next_image(decoder_raw, out_img, limits);
+}
+
+
 static const struct heif_decoder_plugin decoder_ffmpeg
     {
-        3,
+        4,
         ffmpeg_plugin_name,
         ffmpeg_init_plugin,
         ffmpeg_deinit_plugin,
@@ -474,7 +544,8 @@ static const struct heif_decoder_plugin
         ffmpeg_v1_push_data,
         ffmpeg_v1_decode_image,
         ffmpeg_set_strict_decoding,
-        "ffmpeg"
+        "ffmpeg",
+        ffmpeg_v1_decode_next_image
     };
 
 const struct heif_decoder_plugin* get_decoder_plugin_ffmpeg()
diff -pruN 1.19.8-1/libheif/plugins/decoder_jpeg.cc 1.20.1-1/libheif/plugins/decoder_jpeg.cc
--- 1.19.8-1/libheif/plugins/decoder_jpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_jpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -27,6 +27,7 @@
 #include <csetjmp>
 #include <vector>
 #include <cstdio>
+#include <string>
 
 extern "C" {
 #include <jpeglib.h>
@@ -36,6 +37,7 @@ extern "C" {
 struct jpeg_decoder
 {
   std::vector<uint8_t> data;
+  std::string error_message;
 };
 
 static const char kSuccess[] = "Success";
@@ -145,7 +147,8 @@ void on_jpeg_error(j_common_ptr cinfo)
 }
 
 
-struct heif_error jpeg_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error jpeg_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                         const heif_security_limits* limits)
 {
   struct jpeg_decoder* decoder = (struct jpeg_decoder*) decoder_raw;
 
@@ -222,10 +225,18 @@ struct heif_error jpeg_decode_image(void
       return err;
     }
 
-    heif_image_add_plane(heif_img, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8);
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8, limits);
+    if (err.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
 
-    int y_stride;
-    uint8_t* py = heif_image_get_plane(heif_img, heif_channel_Y, &y_stride);
+      heif_image_release(heif_img);
+      return err;
+    }
+
+    size_t y_stride;
+    uint8_t* py = heif_image_get_plane2(heif_img, heif_channel_Y, &y_stride);
 
 
     // read the image
@@ -260,16 +271,37 @@ struct heif_error jpeg_decode_image(void
       return err;
     }
 
-    heif_image_add_plane(heif_img, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8);
-    heif_image_add_plane(heif_img, heif_channel_Cb, (cinfo.output_width + 1) / 2, (cinfo.output_height + 1) / 2, 8);
-    heif_image_add_plane(heif_img, heif_channel_Cr, (cinfo.output_width + 1) / 2, (cinfo.output_height + 1) / 2, 8);
-
-    int y_stride;
-    int cb_stride;
-    int cr_stride;
-    uint8_t* py = heif_image_get_plane(heif_img, heif_channel_Y, &y_stride);
-    uint8_t* pcb = heif_image_get_plane(heif_img, heif_channel_Cb, &cb_stride);
-    uint8_t* pcr = heif_image_get_plane(heif_img, heif_channel_Cr, &cr_stride);
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Y, cinfo.output_width, cinfo.output_height, 8, limits);
+    if (err.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      return err;
+    }
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Cb, (cinfo.output_width + 1) / 2, (cinfo.output_height + 1) / 2, 8, limits);
+    if (err.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      return err;
+    }
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Cr, (cinfo.output_width + 1) / 2, (cinfo.output_height + 1) / 2, 8, limits);
+    if (err.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      return err;
+    }
+
+    size_t y_stride;
+    size_t cb_stride;
+    size_t cr_stride;
+    uint8_t* py = heif_image_get_plane2(heif_img, heif_channel_Y, &y_stride);
+    uint8_t* pcb = heif_image_get_plane2(heif_img, heif_channel_Cb, &cb_stride);
+    uint8_t* pcr = heif_image_get_plane2(heif_img, heif_channel_Cr, &cr_stride);
 
     // read the image
 
@@ -328,10 +360,16 @@ struct heif_error jpeg_decode_image(void
   return heif_error_ok;
 }
 
+struct heif_error jpeg_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return jpeg_decode_next_image(decoder_raw, out_img, limits);
+}
+
 
 static const struct heif_decoder_plugin decoder_jpeg
     {
-        3,
+        4,
         jpeg_plugin_name,
         jpeg_init_plugin,
         jpeg_deinit_plugin,
@@ -341,7 +379,8 @@ static const struct heif_decoder_plugin
         jpeg_push_data,
         jpeg_decode_image,
         jpeg_set_strict_decoding,
-        "jpeg"
+        "jpeg",
+        jpeg_decode_next_image
     };
 
 
diff -pruN 1.19.8-1/libheif/plugins/decoder_libde265.cc 1.20.1-1/libheif/plugins/decoder_libde265.cc
--- 1.19.8-1/libheif/plugins/decoder_libde265.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_libde265.cc	2025-07-02 13:05:31.000000000 +0000
@@ -20,12 +20,11 @@
 
 #include "libheif/heif.h"
 #include "libheif/heif_plugin.h"
-//#include "libheif/heif_colorconversion.h"
-//#include "libheif/heif_api_structs.h"
 #include "decoder_libde265.h"
-#include <assert.h>
+#include <cassert>
 #include <memory>
 #include <cstring>
+#include <string>
 
 #include <libde265/de265.h>
 
@@ -35,6 +34,7 @@ struct libde265_decoder
 {
   de265_decoder_context* ctx;
   bool strict_decoding = false;
+  std::string error_message;
 };
 
 static const char kEmptyString[] = "";
@@ -87,7 +87,8 @@ static int libde265_does_support_format(
 
 static struct heif_error convert_libde265_image_to_heif_image(struct libde265_decoder* decoder,
                                                               const struct de265_image* de265img,
-                                                              struct heif_image** image)
+                                                              struct heif_image** image,
+                                                              const heif_security_limits* limits)
 {
   bool is_mono = (de265_get_chroma_format(de265img) == de265_chroma_mono);
 
@@ -136,14 +137,18 @@ static struct heif_error convert_libde26
       return err;
     }
 
-    err = heif_image_add_plane(*image, channel2plane[c], w,h, bpp);
+    err = heif_image_add_plane_safe(*image, channel2plane[c], w,h, bpp, limits);
     if (err.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
       heif_image_release(*image);
       return err;
     }
 
-    int dst_stride;
-    uint8_t* dst_mem = heif_image_get_plane(*image, channel2plane[c], &dst_stride);
+    size_t dst_stride;
+    uint8_t* dst_mem = heif_image_get_plane2(*image, channel2plane[c], &dst_stride);
 
     int bytes_per_pixel = (bpp + 7) / 8;
 
@@ -237,8 +242,9 @@ static struct heif_error libde265_v2_pus
 }
 
 
-static struct heif_error libde265_v2_decode_image(void* decoder_raw,
-                                                  struct heif_image** out_img)
+static struct heif_error libde265_v2_decode_next_image(void* decoder_raw,
+                                                       struct heif_image** out_img,
+                                                       const heif_security_limits* limits)
 {
   struct libde265_decoder* decoder = (struct libde265_decoder*)decoder_raw;
 
@@ -253,7 +259,7 @@ static struct heif_error libde265_v2_dec
     const de265_image* img = de265_get_next_picture(decoder->ctx);
     if (img) {
       struct heif_error err = convert_libde265_image_to_heif_image(decoder, img,
-                                                                   out_img);
+                                                                   out_img, limits);
       de265_release_picture(img);
 
       return err;
@@ -264,6 +270,12 @@ static struct heif_error libde265_v2_dec
   return err;
 }
 
+static struct heif_error libde265_v2_decode_image(void* decoder_raw,
+                                                  struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return libde265_v2_decode_next_image(decoder_raw, out_img, limits);
+}
 #else
 
 static struct heif_error libde265_v1_push_data(void* decoder_raw, const void* data, size_t size)
@@ -303,8 +315,9 @@ static struct heif_error libde265_v1_pus
 }
 
 
-static struct heif_error libde265_v1_decode_image(void* decoder_raw,
-                                                  struct heif_image** out_img)
+static struct heif_error libde265_v1_decode_next_image(void* decoder_raw,
+                                                       struct heif_image** out_img,
+                                                       const heif_security_limits* limits)
 {
   struct libde265_decoder* decoder = (struct libde265_decoder*) decoder_raw;
   struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
@@ -331,7 +344,7 @@ static struct heif_error libde265_v1_dec
       if (*out_img) {
         heif_image_release(*out_img);
       }
-      err = convert_libde265_image_to_heif_image(decoder, image, out_img);
+      err = convert_libde265_image_to_heif_image(decoder, image, out_img, limits);
       if (err.code != heif_error_Ok) {
         return err;
       }
@@ -369,6 +382,13 @@ static struct heif_error libde265_v1_dec
 }
 
 
+static struct heif_error libde265_v1_decode_image(void* decoder_raw,
+                                                  struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return libde265_v1_decode_next_image(decoder_raw, out_img, limits);
+}
+
 #endif
 
 
@@ -384,14 +404,17 @@ static const struct heif_decoder_plugin
   libde265_new_decoder,
   libde265_free_decoder,
   libde265_v2_push_data,
-  libde265_v2_decode_image
+  libde265_v2_decode_image,
+  libde265_set_strict_decoding,
+  "libde265",
+  libde265_v2_decode_next_image
 };
 
 #else
 
 static const struct heif_decoder_plugin decoder_libde265
     {
-        3,
+        4,
         libde265_plugin_name,
         libde265_init_plugin,
         libde265_deinit_plugin,
@@ -401,7 +424,8 @@ static const struct heif_decoder_plugin
         libde265_v1_push_data,
         libde265_v1_decode_image,
         libde265_set_strict_decoding,
-        "libde265"
+        "libde265",
+        libde265_v1_decode_next_image
     };
 
 #endif
diff -pruN 1.19.8-1/libheif/plugins/decoder_openh264.cc 1.20.1-1/libheif/plugins/decoder_openh264.cc
--- 1.19.8-1/libheif/plugins/decoder_openh264.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_openh264.cc	2025-07-02 13:05:31.000000000 +0000
@@ -28,11 +28,13 @@
 #include <cstdio>
 
 #include <wels/codec_api.h>
+#include <string>
 
 
 struct openh264_decoder
 {
   std::vector<uint8_t> data;
+  std::string error_message;
 };
 
 static const char kSuccess[] = "Success";
@@ -118,7 +120,8 @@ struct heif_error openh264_push_data(voi
 }
 
 
-struct heif_error openh264_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error openh264_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                             const heif_security_limits* limits)
 {
   auto* decoder = (struct openh264_decoder*) decoder_raw;
 
@@ -263,16 +266,42 @@ struct heif_error openh264_decode_image(
 
     *out_img = heif_img;
 
-    heif_image_add_plane(heif_img, heif_channel_Y, width, height, 8);
-    heif_image_add_plane(heif_img, heif_channel_Cb, cwidth, cheight, 8);
-    heif_image_add_plane(heif_img, heif_channel_Cr, cwidth, cheight, 8);
-
-    int y_stride;
-    int cb_stride;
-    int cr_stride;
-    uint8_t* py = heif_image_get_plane(heif_img, heif_channel_Y, &y_stride);
-    uint8_t* pcb = heif_image_get_plane(heif_img, heif_channel_Cb, &cb_stride);
-    uint8_t* pcr = heif_image_get_plane(heif_img, heif_channel_Cr, &cr_stride);
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Y, width, height, 8, limits);
+    if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      heif_image_release(heif_img);
+      return err;
+    }
+
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Cb, cwidth, cheight, 8, limits);
+    if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      heif_image_release(heif_img);
+      return err;
+    }
+
+    err = heif_image_add_plane_safe(heif_img, heif_channel_Cr, cwidth, cheight, 8, limits);
+    if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
+      heif_image_release(heif_img);
+      return err;
+    }
+
+    size_t y_stride;
+    size_t cb_stride;
+    size_t cr_stride;
+    uint8_t* py = heif_image_get_plane2(heif_img, heif_channel_Y, &y_stride);
+    uint8_t* pcb = heif_image_get_plane2(heif_img, heif_channel_Cb, &cb_stride);
+    uint8_t* pcr = heif_image_get_plane2(heif_img, heif_channel_Cr, &cr_stride);
 
     int ystride = sDstBufInfo.UsrData.sSystemBuffer.iStride[0];
     int cstride = sDstBufInfo.UsrData.sSystemBuffer.iStride[1];
@@ -301,9 +330,15 @@ struct heif_error openh264_decode_image(
   return heif_error_ok;
 }
 
+struct heif_error openh264_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return openh264_decode_next_image(decoder_raw, out_img, limits);
+}
+
 
 static const struct heif_decoder_plugin decoder_openh264{
-        3,
+        4,
         openh264_plugin_name,
         openh264_init_plugin,
         openh264_deinit_plugin,
@@ -313,7 +348,8 @@ static const struct heif_decoder_plugin
         openh264_push_data,
         openh264_decode_image,
         openh264_set_strict_decoding,
-        "openh264"
+        "openh264",
+        openh264_decode_next_image
 };
 
 
diff -pruN 1.19.8-1/libheif/plugins/decoder_openjpeg.cc 1.20.1-1/libheif/plugins/decoder_openjpeg.cc
--- 1.19.8-1/libheif/plugins/decoder_openjpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_openjpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -28,6 +28,7 @@
 #include <vector>
 #include <cassert>
 #include <memory>
+#include <string>
 
 static const int OPENJPEG_PLUGIN_PRIORITY = 100;
 static const int OPENJPEG_PLUGIN_PRIORITY_HTJ2K = 90;
@@ -36,6 +37,7 @@ struct openjpeg_decoder
 {
   std::vector<uint8_t> encoded_data;
   size_t read_position = 0;
+  std::string error_message;
 };
 
 
@@ -253,7 +255,8 @@ opj_stream_t* opj_stream_create_default_
 //**************************************************************************
 
 
-struct heif_error openjpeg_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error openjpeg_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                             const heif_security_limits* limits)
 {
   auto* decoder = (struct openjpeg_decoder*) decoder_raw;
 
@@ -367,10 +370,19 @@ struct heif_error openjpeg_decode_image(
     int cwidth = opj_comp.w;
     int cheight = opj_comp.h;
 
-    error = heif_image_add_plane(*out_img, channels[c], cwidth, cheight, bit_depth);
+    error = heif_image_add_plane_safe(*out_img, channels[c], cwidth, cheight, bit_depth, limits);
+    if (error.code) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = error.message;
+      error.message = decoder->error_message.c_str();
+
+      heif_image_release(*out_img);
+      *out_img = nullptr;
+      return error;
+    }
 
-    int stride = -1;
-    uint8_t* p = heif_image_get_plane(*out_img, channels[c], &stride);
+    size_t stride = 0;
+    uint8_t* p = heif_image_get_plane2(*out_img, channels[c], &stride);
 
 
     // TODO: a SIMD implementation to convert int32 to uint8 would speed this up
@@ -396,9 +408,15 @@ struct heif_error openjpeg_decode_image(
   return heif_error_ok;
 }
 
+struct heif_error openjpeg_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return openjpeg_decode_next_image(decoder_raw, out_img, limits);
+}
+
 
 static const struct heif_decoder_plugin decoder_openjpeg{
-    3,
+    4,
     openjpeg_plugin_name,
     openjpeg_init_plugin,
     openjpeg_deinit_plugin,
@@ -408,7 +426,8 @@ static const struct heif_decoder_plugin
     openjpeg_push_data,
     openjpeg_decode_image,
     openjpeg_set_strict_decoding,
-    "openjpeg"
+    "openjpeg",
+    openjpeg_decode_next_image
 };
 
 const struct heif_decoder_plugin* get_decoder_plugin_openjpeg()
diff -pruN 1.19.8-1/libheif/plugins/decoder_vvdec.cc 1.20.1-1/libheif/plugins/decoder_vvdec.cc
--- 1.19.8-1/libheif/plugins/decoder_vvdec.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/decoder_vvdec.cc	2025-07-02 13:05:31.000000000 +0000
@@ -25,6 +25,7 @@
 #include <cassert>
 #include <vector>
 #include <algorithm>
+#include <string>
 
 #include <vvdec/vvdec.h>
 
@@ -42,6 +43,7 @@ struct vvdec_decoder
   bool strict_decoding = false;
 
   std::vector<std::vector<uint8_t>> nalus;
+  std::string error_message;
 };
 
 static const char kSuccess[] = "Success";
@@ -173,7 +175,8 @@ struct heif_error vvdec_push_data(void*
 }
 
 
-struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_img)
+struct heif_error vvdec_decode_next_image(void* decoder_raw, struct heif_image** out_img,
+                                          const heif_security_limits* limits)
 {
   auto* decoder = (struct vvdec_decoder*) decoder_raw;
 
@@ -296,14 +299,18 @@ struct heif_error vvdec_decode_image(voi
     int w = (int)plane.width;
     int h = (int)plane.height;
 
-    err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp);
+    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
     if (err.code != heif_error_Ok) {
+      // copy error message to decoder object because heif_image will be released
+      decoder->error_message = err.message;
+      err.message = decoder->error_message.c_str();
+
       heif_image_release(heif_img);
       return err;
     }
 
-    int dst_stride;
-    uint8_t* dst_mem = heif_image_get_plane(heif_img, channel2plane[c], &dst_stride);
+    size_t dst_stride;
+    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
 
     int bytes_per_pixel = (bpp + 7) / 8;
 
@@ -325,10 +332,16 @@ struct heif_error vvdec_decode_image(voi
   return err;
 }
 
+struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_img)
+{
+  auto* limits = heif_get_global_security_limits();
+  return vvdec_decode_next_image(decoder_raw, out_img, limits);
+}
+
 
 static const struct heif_decoder_plugin decoder_vvdec
     {
-        3,
+        4,
         vvdec_plugin_name,
         vvdec_init_plugin,
         vvdec_deinit_plugin,
@@ -338,7 +351,8 @@ static const struct heif_decoder_plugin
         vvdec_push_data,
         vvdec_decode_image,
         vvdec_set_strict_decoding,
-        "vvdec"
+        "vvdec",
+        vvdec_decode_next_image
     };
 
 
diff -pruN 1.19.8-1/libheif/plugins/encoder_aom.cc 1.20.1-1/libheif/plugins/encoder_aom.cc
--- 1.19.8-1/libheif/plugins/encoder_aom.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_aom.cc	2025-07-02 13:05:31.000000000 +0000
@@ -897,8 +897,8 @@ struct heif_error aom_encode_image(void*
     const int h = aom_img_plane_height(img, plane);
     */
 
-    int in_stride = 0;
-    const uint8_t* in_p = heif_image_get_plane_readonly(image, (heif_channel) plane, &in_stride);
+    size_t in_stride = 0;
+    const uint8_t* in_p = heif_image_get_plane_readonly2(image, (heif_channel) plane, &in_stride);
 
     int w = source_width;
     int h = source_height;
diff -pruN 1.19.8-1/libheif/plugins/encoder_jpeg.cc 1.20.1-1/libheif/plugins/encoder_jpeg.cc
--- 1.19.8-1/libheif/plugins/encoder_jpeg.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_jpeg.cc	2025-07-02 13:05:31.000000000 +0000
@@ -381,15 +381,15 @@ struct heif_error jpeg_encode_image(void
 
 
 
-  int stride_y;
-  const uint8_t* row_y = heif_image_get_plane_readonly(image, heif_channel_Y,
-                                                       &stride_y);
-  int stride_u;
-  const uint8_t* row_u = heif_image_get_plane_readonly(image, heif_channel_Cb,
-                                                       &stride_u);
-  int stride_v;
-  const uint8_t* row_v = heif_image_get_plane_readonly(image, heif_channel_Cr,
-                                                       &stride_v);
+  size_t stride_y;
+  const uint8_t* row_y = heif_image_get_plane_readonly2(image, heif_channel_Y,
+                                                        &stride_y);
+  size_t stride_u;
+  const uint8_t* row_u = heif_image_get_plane_readonly2(image, heif_channel_Cb,
+                                                        &stride_u);
+  size_t stride_v;
+  const uint8_t* row_v = heif_image_get_plane_readonly2(image, heif_channel_Cr,
+                                                        &stride_v);
 
   JSAMPARRAY buffer = cinfo.mem->alloc_sarray(
       reinterpret_cast<j_common_ptr>(&cinfo), JPOOL_IMAGE,
diff -pruN 1.19.8-1/libheif/plugins/encoder_kvazaar.cc 1.20.1-1/libheif/plugins/encoder_kvazaar.cc
--- 1.19.8-1/libheif/plugins/encoder_kvazaar.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_kvazaar.cc	2025-07-02 13:05:31.000000000 +0000
@@ -348,7 +348,7 @@ static void append_chunk_data(kvz_data_c
 }
 
 
-static void copy_plane(kvz_pixel* out_p, uint32_t out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h, int padded_width, int padded_height,
+static void copy_plane(kvz_pixel* out_p, size_t out_stride, const uint8_t* in_p, size_t in_stride, int w, int h, int padded_width, int padded_height,
                        int bit_depth)
 {
   int bpp = (bit_depth > 8) ? 2 : 1;
@@ -601,23 +601,23 @@ static struct heif_error kvazaar_encode_
   }
 
   if (isGreyscale) {
-    int stride;
-    const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    size_t stride;
+    const uint8_t* data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
 
     copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height, bit_depth);
   }
   else {
-    int stride;
+    size_t stride;
     const uint8_t* data;
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
     copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height, bit_depth);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride);
     copy_plane(pic->u, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift, bit_depth_chroma);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cr, &stride);
     copy_plane(pic->v, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift, bit_depth_chroma);
   }
diff -pruN 1.19.8-1/libheif/plugins/encoder_openjph.cc 1.20.1-1/libheif/plugins/encoder_openjph.cc
--- 1.19.8-1/libheif/plugins/encoder_openjph.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_openjph.cc	2025-07-02 13:05:31.000000000 +0000
@@ -777,6 +777,10 @@ struct heif_error ojph_encode_image(void
                       "OpenJPH encoder plugin received image with invalid colorspace."};
   }
 
+  // reset output position to start
+  encoder->outfile.seek(0, ojph::outfile_base::seek::OJPH_SEEK_SET);
+  encoder->data_read = false;
+
   std::vector<heif_channel> sourceChannels = build_SIZ(encoder, image);
   build_COD(encoder);
 #if OPENJPH_MAJOR_VERSION > 1 || OPENJPH_MINOR_VERSION > 10
@@ -793,8 +797,8 @@ struct heif_error ojph_encode_image(void
   ojph::line_buf* cur_line = encoder->codestream.exchange(NULL, next_comp);
 
   for (const auto& sourceChannel : sourceChannels) {
-    int stride;
-    const uint8_t *data = heif_image_get_plane_readonly(image, sourceChannel, &stride);
+    size_t stride;
+    const uint8_t *data = heif_image_get_plane_readonly2(image, sourceChannel, &stride);
     uint32_t component_height = heif_image_get_height(image, sourceChannel);
     for (uint32_t y = 0; y < component_height; y++) {
       const uint8_t *sourceLine = data + y * stride;
diff -pruN 1.19.8-1/libheif/plugins/encoder_rav1e.cc 1.20.1-1/libheif/plugins/encoder_rav1e.cc
--- 1.19.8-1/libheif/plugins/encoder_rav1e.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_rav1e.cc	2025-07-02 13:05:31.000000000 +0000
@@ -608,12 +608,12 @@ struct heif_error rav1e_encode_image(voi
   // if (input_class == heif_image_input_class_alpha) {
   //} else
   {
-    int strideY;
-    const uint8_t* Y = heif_image_get_plane_readonly(image, heif_channel_Y, &strideY);
-    int strideCb;
-    const uint8_t* Cb = heif_image_get_plane_readonly(image, heif_channel_Cb, &strideCb);
-    int strideCr;
-    const uint8_t* Cr = heif_image_get_plane_readonly(image, heif_channel_Cr, &strideCr);
+    size_t strideY;
+    const uint8_t* Y = heif_image_get_plane_readonly2(image, heif_channel_Y, &strideY);
+    size_t strideCb;
+    const uint8_t* Cb = heif_image_get_plane_readonly2(image, heif_channel_Cb, &strideCb);
+    size_t strideCr;
+    const uint8_t* Cr = heif_image_get_plane_readonly2(image, heif_channel_Cr, &strideCr);
 
 
     uint32_t height = heif_image_get_height(image, heif_channel_Y);
diff -pruN 1.19.8-1/libheif/plugins/encoder_svt.cc 1.20.1-1/libheif/plugins/encoder_svt.cc
--- 1.19.8-1/libheif/plugins/encoder_svt.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_svt.cc	2025-07-02 13:05:31.000000000 +0000
@@ -26,6 +26,7 @@
 #include <cassert>
 #include <algorithm>
 #include <memory>
+#include <limits>
 
 #include "svt-av1/EbSvtAv1.h"
 #include "svt-av1/EbSvtAv1Enc.h"
@@ -841,10 +842,21 @@ struct heif_error svt_encode_image(void*
   int bytesPerPixel = bitdepth_y > 8 ? 2 : 1;
   std::vector<uint8_t> dummy_color_plane;
   if (input_class == heif_image_input_class_alpha) {
-    int stride;
-    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
-    input_picture_buffer->y_stride = stride / bytesPerPixel;
-    input_buffer.n_filled_len = stride * encoded_height;
+    size_t stride64;
+    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Y, &stride64);
+
+    uint32_t stride32;
+    if (stride64 > std::numeric_limits<uint32_t>::max()) {
+      return {heif_error_Encoder_plugin_error,
+              heif_suberror_Unspecified,
+              "Image too wide for encoder"};
+    }
+    else {
+      stride32 = static_cast<uint32_t>(stride64);
+    }
+
+    input_picture_buffer->y_stride = stride32 / bytesPerPixel;
+    input_buffer.n_filled_len = stride32 * encoded_height;
 
     uint32_t uvWidth = get_subsampled_size_h(encoded_width, heif_channel_Cb, heif_chroma_420, scaling_mode::round_up);
     uint32_t uvHeight = get_subsampled_size_v(encoded_height, heif_channel_Cb, heif_chroma_420, scaling_mode::round_up);
@@ -874,19 +886,32 @@ struct heif_error svt_encode_image(void*
     input_picture_buffer->cr = dummy_color_plane.data();
   }
   else {
-    int stride;
-    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
-    input_picture_buffer->y_stride = stride / bytesPerPixel;
-    input_buffer.n_filled_len = stride * encoded_height;
+    size_t stride64;
+    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Y, &stride64);
+
+    uint32_t stride32;
+    if (stride64 > std::numeric_limits<uint32_t>::max()) {
+      return {heif_error_Encoder_plugin_error,
+              heif_suberror_Unspecified,
+              "Image too wide for encoder"};
+    }
+    else {
+      stride32 = static_cast<uint32_t>(stride64);
+    }
+
+    input_picture_buffer->y_stride = stride32 / bytesPerPixel;
+    input_buffer.n_filled_len = stride32 * encoded_height;
 
     uint32_t uvHeight = (encoded_height + yShift) >> yShift;
-    input_picture_buffer->cb = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
-    input_buffer.n_filled_len += stride * uvHeight;
-    input_picture_buffer->cb_stride = stride / bytesPerPixel;
-
-    input_picture_buffer->cr = (uint8_t*) heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
-    input_buffer.n_filled_len += stride * uvHeight;
-    input_picture_buffer->cr_stride = stride / bytesPerPixel;
+    input_picture_buffer->cb = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride64);
+    stride32 = static_cast<uint32_t>(stride64); // chroma stride should always be smaller than luma stride
+    input_buffer.n_filled_len += stride32 * uvHeight;
+    input_picture_buffer->cb_stride = stride32 / bytesPerPixel;
+
+    input_picture_buffer->cr = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Cr, &stride64);
+    stride32 = static_cast<uint32_t>(stride64); // chroma stride should always be smaller than luma stride
+    input_buffer.n_filled_len += stride32 * uvHeight;
+    input_picture_buffer->cr_stride = stride32 / bytesPerPixel;
   }
 
   input_buffer.flags = 0;
diff -pruN 1.19.8-1/libheif/plugins/encoder_uvg266.cc 1.20.1-1/libheif/plugins/encoder_uvg266.cc
--- 1.19.8-1/libheif/plugins/encoder_uvg266.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_uvg266.cc	2025-07-02 13:05:31.000000000 +0000
@@ -355,7 +355,7 @@ static void append_chunk_data(uvg_data_c
 }
 
 
-static void copy_plane(uvg_pixel* out_p, uint32_t out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h, int padded_width, int padded_height)
+static void copy_plane(uvg_pixel* out_p, size_t out_stride, const uint8_t* in_p, size_t in_stride, int w, int h, int padded_width, int padded_height)
 {
   for (int y = 0; y < padded_height; y++) {
     int sy = std::min(y, h - 1); // source y
@@ -573,23 +573,23 @@ static struct heif_error uvg266_encode_i
   }
 
   if (isGreyscale) {
-    int stride;
-    const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    size_t stride;
+    const uint8_t* data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
 
     copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height);
   }
   else {
-    int stride;
+    size_t stride;
     const uint8_t* data;
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
     copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride);
     copy_plane(pic->u, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cr, &stride);
     copy_plane(pic->v, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
   }
diff -pruN 1.19.8-1/libheif/plugins/encoder_vvenc.cc 1.20.1-1/libheif/plugins/encoder_vvenc.cc
--- 1.19.8-1/libheif/plugins/encoder_vvenc.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/plugins/encoder_vvenc.cc	2025-07-02 13:05:31.000000000 +0000
@@ -339,7 +339,7 @@ static void append_chunk_data(struct enc
 }
 
 
-static void copy_plane(int16_t*& out_p, int& out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h, int padded_width, int padded_height)
+static void copy_plane(int16_t*& out_p, size_t& out_stride, const uint8_t* in_p, size_t in_stride, int w, int h, int padded_width, int padded_height)
 {
   out_stride = padded_width;
   out_p = new int16_t[out_stride * w * h];
@@ -530,50 +530,50 @@ static struct heif_error vvenc_encode_im
   int16_t* yptr = nullptr;
   int16_t* cbptr = nullptr;
   int16_t* crptr = nullptr;
-  int ystride = 0;
-  int cbstride = 0;
-  int crstride = 0;
+  size_t ystride = 0;
+  size_t cbstride = 0;
+  size_t crstride = 0;
 
   if (isGreyscale) {
-    int stride;
-    const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    size_t stride;
+    const uint8_t* data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
 
     copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height);
 
     yuvbuf->planes[0].ptr = yptr;
     yuvbuf->planes[0].width = encoded_width;
     yuvbuf->planes[0].height = encoded_height;
-    yuvbuf->planes[0].stride = ystride;
+    yuvbuf->planes[0].stride = (int)ystride;
   }
   else {
-    int stride;
+    size_t stride;
     const uint8_t* data;
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Y, &stride);
     copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride);
     copy_plane(cbptr, cbstride, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
 
-    data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride);
+    data = heif_image_get_plane_readonly2(image, heif_channel_Cr, &stride);
     copy_plane(crptr, crstride, data, stride, input_chroma_width, input_chroma_height,
                encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift);
 
     yuvbuf->planes[0].ptr = yptr;
     yuvbuf->planes[0].width = encoded_width;
     yuvbuf->planes[0].height = encoded_height;
-    yuvbuf->planes[0].stride = ystride;
+    yuvbuf->planes[0].stride = (int)ystride;
 
     yuvbuf->planes[1].ptr = cbptr;
     yuvbuf->planes[1].width = encoded_width >> chroma_stride_shift;
     yuvbuf->planes[1].height = encoded_height >> chroma_height_shift;
-    yuvbuf->planes[1].stride = cbstride;
+    yuvbuf->planes[1].stride = (int)cbstride;
 
     yuvbuf->planes[2].ptr = crptr;
     yuvbuf->planes[2].width = encoded_width >> chroma_stride_shift;
     yuvbuf->planes[2].height = encoded_height >> chroma_height_shift;
-    yuvbuf->planes[2].stride = crstride;
+    yuvbuf->planes[2].stride = (int)crstride;
   }
 
   //yuvbuf->cts     = frame->pts;
diff -pruN 1.19.8-1/libheif/security_limits.cc 1.20.1-1/libheif/security_limits.cc
--- 1.19.8-1/libheif/security_limits.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/security_limits.cc	2025-07-02 13:05:31.000000000 +0000
@@ -20,10 +20,12 @@
 
 #include "security_limits.h"
 #include <limits>
+#include <map>
+#include <mutex>
 
 
-struct heif_security_limits global_security_limits {
-    .version = 1,
+struct heif_security_limits global_security_limits{
+    .version = 2,
 
     // --- version 1
 
@@ -31,22 +33,28 @@ struct heif_security_limits global_secur
     // 32768^2 = 1.5 GB as YUV-4:2:0 or 4 GB as RGB32
     .max_image_size_pixels = 32768 * 32768,
     .max_number_of_tiles = 4096 * 4096,
-    .max_bayer_pattern_pixels = 16*16,
+    .max_bayer_pattern_pixels = 16 * 16,
     .max_items = 1000,
 
     .max_color_profile_size = 100 * 1024 * 1024, // 100 MB
-    .max_memory_block_size = 512 * 1024 * 1024,  // 512 MB
+    .max_memory_block_size = UINT64_C(4) * 1024 * 1024 * 1024,  // 4 GB
 
     .max_components = 256,
     .max_iloc_extents_per_item = 32,
     .max_size_entity_group = 64,
 
-    .max_children_per_box = 100
+    .max_children_per_box = 100,
+
+    // --- version 2
+
+    .max_total_memory = UINT64_C(4) * 1024 * 1024 * 1024,  // 4 GB
+    .max_sample_description_box_entries = 1024,
+    .max_sample_group_description_box_entries = 1024
 };
 
 
 struct heif_security_limits disabled_security_limits{
-        .version = 1
+    .version = 2
 };
 
 
@@ -78,3 +86,164 @@ Error check_for_valid_image_size(const h
 
   return Error::Ok;
 }
+
+
+struct memory_stats {
+  size_t total_memory_usage = 0;
+  size_t max_memory_usage = 0;
+};
+
+std::mutex& get_memory_usage_mutex()
+{
+  static std::mutex sMutex;
+  return sMutex;
+}
+
+static std::map<const heif_security_limits*, memory_stats> sMemoryUsage;
+
+TotalMemoryTracker::TotalMemoryTracker(const heif_security_limits* limits)
+{
+  std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+
+  sMemoryUsage[limits] = {};
+  m_limits_context = limits;
+}
+
+TotalMemoryTracker::~TotalMemoryTracker()
+{
+  std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+  sMemoryUsage.erase(m_limits_context);
+}
+
+
+size_t TotalMemoryTracker::get_max_total_memory_used() const
+{
+  std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+
+  auto it = sMemoryUsage.find(m_limits_context);
+  if (it != sMemoryUsage.end()) {
+    return it->second.max_memory_usage;
+  }
+  else {
+    assert(false);
+    return 0;
+  }
+}
+
+
+Error MemoryHandle::alloc(size_t memory_amount, const heif_security_limits* limits_context,
+                          const char* reason_description)
+{
+  // we allow several allocations on the same handle, but they have to be for the same context
+  if (m_limits_context) {
+    assert(m_limits_context == limits_context);
+  }
+
+
+  // --- check whether limits are exceeded
+
+  if (!limits_context) {
+    return Error::Ok;
+  }
+
+  // check against maximum memory block size
+
+  if (limits_context->max_memory_block_size != 0 &&
+      memory_amount > limits_context->max_memory_block_size) {
+    std::stringstream sstr;
+
+    if (reason_description) {
+      sstr << "Allocating " << memory_amount << " bytes for " << reason_description <<" exceeds the security limit of "
+           << limits_context->max_memory_block_size << " bytes";
+    }
+    else {
+      sstr << "Allocating " << memory_amount << " bytes exceeds the security limit of "
+           << limits_context->max_memory_block_size << " bytes";
+    }
+
+    return {heif_error_Memory_allocation_error,
+            heif_suberror_Security_limit_exceeded,
+            sstr.str()};
+  }
+
+  if (limits_context == &global_security_limits ||
+      limits_context == &disabled_security_limits) {
+    return Error::Ok;
+  }
+
+  std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+  auto it = sMemoryUsage.find(limits_context);
+  if (it == sMemoryUsage.end()) {
+    assert(false);
+    return Error::Ok;
+  }
+
+  // check against maximum total memory usage
+
+  if (limits_context->max_total_memory != 0 &&
+      it->second.total_memory_usage + memory_amount > limits_context->max_total_memory) {
+    std::stringstream sstr;
+
+    if (reason_description) {
+      sstr << "Memory usage of " << it->second.total_memory_usage + memory_amount
+           << " bytes for " << reason_description << " exceeds the security limit of "
+           << limits_context->max_total_memory << " bytes of total memory usage";
+    }
+    else {
+      sstr << "Memory usage of " << it->second.total_memory_usage + memory_amount
+           << " bytes exceeds the security limit of "
+           << limits_context->max_total_memory << " bytes of total memory usage";
+    }
+
+    return {heif_error_Memory_allocation_error,
+            heif_suberror_Security_limit_exceeded,
+            sstr.str()};
+  }
+
+
+  // --- register memory usage
+
+  m_limits_context = limits_context;
+  m_memory_amount += memory_amount;
+
+  it->second.total_memory_usage += memory_amount;
+
+  // remember maximum memory usage (for informational purpose)
+  if (it->second.total_memory_usage > it->second.max_memory_usage) {
+    it->second.max_memory_usage = it->second.total_memory_usage;
+  }
+
+  return Error::Ok;
+}
+
+
+void MemoryHandle::free()
+{
+  if (m_limits_context) {
+    std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+
+    auto it = sMemoryUsage.find(m_limits_context);
+    if (it != sMemoryUsage.end()) {
+      it->second.total_memory_usage -= m_memory_amount;
+    }
+
+    m_limits_context = nullptr;
+    m_memory_amount = 0;
+  }
+}
+
+
+void MemoryHandle::free(size_t memory_amount)
+{
+  if (m_limits_context) {
+    std::lock_guard<std::mutex> lock(get_memory_usage_mutex());
+
+    auto it = sMemoryUsage.find(m_limits_context);
+    if (it != sMemoryUsage.end()) {
+      it->second.total_memory_usage -= memory_amount;
+    }
+
+    m_memory_amount -= memory_amount;
+  }
+}
+
diff -pruN 1.19.8-1/libheif/security_limits.h 1.20.1-1/libheif/security_limits.h
--- 1.19.8-1/libheif/security_limits.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/libheif/security_limits.h	2025-07-02 13:05:31.000000000 +0000
@@ -41,4 +41,44 @@ static const int MAX_FRACTION_VALUE = 0x
 
 Error check_for_valid_image_size(const heif_security_limits* limits, uint32_t width, uint32_t height);
 
+
+class TotalMemoryTracker
+{
+public:
+  explicit TotalMemoryTracker(const heif_security_limits* limits_context);
+  ~TotalMemoryTracker();
+
+  size_t get_max_total_memory_used() const;
+
+  void operator=(const TotalMemoryTracker&) = delete;
+  TotalMemoryTracker(const TotalMemoryTracker&) = delete;
+
+private:
+  const heif_security_limits* m_limits_context = nullptr;
+};
+
+
+class MemoryHandle
+{
+public:
+  MemoryHandle() = default;
+  ~MemoryHandle() { free(); }
+
+  Error alloc(size_t memory_amount, const heif_security_limits* limits_context, const char* reason_description);
+
+  void free();
+
+  void free(size_t memory_amount);
+
+  const heif_security_limits* get_security_limits() const { return m_limits_context; }
+
+  void operator=(const MemoryHandle&) = delete;
+  MemoryHandle(const MemoryHandle&) = delete;
+
+private:
+  const heif_security_limits* m_limits_context = nullptr;
+  size_t m_memory_amount = 0;
+};
+
+
 #endif  // LIBHEIF_SECURITY_LIMITS_H
diff -pruN 1.19.8-1/libheif/sequences/chunk.cc 1.20.1-1/libheif/sequences/chunk.cc
--- 1.19.8-1/libheif/sequences/chunk.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/chunk.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,113 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "chunk.h"
+#include "context.h"
+#include "codecs/hevc_enc.h"
+#include "codecs/avif_enc.h"
+#include "codecs/vvc_enc.h"
+#include "codecs/jpeg2000_enc.h"
+#include "codecs/jpeg_enc.h"
+
+#if WITH_UNCOMPRESSED_CODEC
+#include "codecs/uncompressed/unc_enc.h"
+#endif
+
+
+Chunk::Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format)
+    : m_ctx(ctx),
+      m_track_id(track_id),
+      m_compression_format(format)
+{
+  switch (format) {
+    case heif_compression_HEVC:
+      m_encoder = std::make_shared<Encoder_HEVC>();
+      break;
+    case heif_compression_AV1:
+      m_encoder = std::make_shared<Encoder_AVIF>();
+      break;
+    case heif_compression_VVC:
+      m_encoder = std::make_shared<Encoder_VVC>();
+      break;
+    case heif_compression_JPEG2000:
+      m_encoder = std::make_shared<Encoder_JPEG2000>();
+      break;
+    case heif_compression_HTJ2K:
+      m_encoder = std::make_shared<Encoder_HTJ2K>();
+      break;
+    case heif_compression_JPEG:
+      m_encoder = std::make_shared<Encoder_JPEG>();
+      break;
+#if WITH_UNCOMPRESSED_CODEC
+    case heif_compression_uncompressed:
+      m_encoder = std::make_shared<Encoder_uncompressed>();
+      break;
+#endif
+    case heif_compression_undefined:
+    default:
+      m_encoder = nullptr;
+      break;
+  }
+}
+
+
+Chunk::Chunk(HeifContext* ctx, uint32_t track_id, std::shared_ptr<const Box> sample_description_box,
+             uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr<const Box_stsz>& stsz)
+{
+  m_ctx = ctx;
+  m_track_id = track_id;
+
+  m_first_sample = first_sample;
+  m_last_sample = first_sample + num_samples - 1;
+
+  m_next_sample_to_be_decoded = first_sample;
+
+  for (uint32_t i=0;i<num_samples;i++) {
+    SampleFileRange range;
+    range.offset = file_offset;
+    if (stsz->has_fixed_sample_size()) {
+      range.size = stsz->get_fixed_sample_size();
+    }
+    else {
+      range.size = stsz->get_sample_sizes()[first_sample + i];
+    }
+
+    m_sample_ranges.push_back(range);
+
+    file_offset += range.size;
+  }
+
+  if (auto visualSampleDescription = std::dynamic_pointer_cast<const Box_VisualSampleEntry>(sample_description_box)) {
+    m_decoder = Decoder::alloc_for_sequence_sample_description_box(visualSampleDescription);
+  }
+}
+
+
+DataExtent Chunk::get_data_extent_for_sample(uint32_t n) const
+{
+  assert(n>= m_first_sample);
+  assert(n<= m_last_sample);
+
+  DataExtent extent;
+  extent.set_file_range(m_ctx->get_heif_file(),
+                        m_sample_ranges[n - m_first_sample].offset,
+                        m_sample_ranges[n - m_first_sample].size);
+  return extent;
+}
diff -pruN 1.19.8-1/libheif/sequences/chunk.h 1.20.1-1/libheif/sequences/chunk.h
--- 1.19.8-1/libheif/sequences/chunk.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/chunk.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,82 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_CHUNK_H
+#define LIBHEIF_CHUNK_H
+
+#include "codecs/decoder.h"
+#include <memory>
+#include <vector>
+
+
+class HeifContext;
+
+class Box_VisualSampleEntry;
+
+
+class Chunk
+{
+public:
+  Chunk(HeifContext* ctx, uint32_t track_id, heif_compression_format format);
+
+  Chunk(HeifContext* ctx, uint32_t track_id, std::shared_ptr<const Box> sample_description_box,
+        uint32_t first_sample, uint32_t num_samples, uint64_t file_offset, const std::shared_ptr<const Box_stsz>& sample_sizes);
+
+  virtual ~Chunk() = default;
+
+  heif_compression_format get_compression_format() const { return m_compression_format; }
+
+  virtual std::shared_ptr<class Decoder> get_decoder() const { return m_decoder; }
+
+  virtual std::shared_ptr<class Encoder> get_encoder() const { return m_encoder; }
+
+  uint32_t first_sample_number() const { return m_first_sample; }
+
+  uint32_t last_sample_number() const { return m_last_sample; }
+
+  DataExtent get_data_extent_for_sample(uint32_t n) const;
+
+private:
+  HeifContext* m_ctx = nullptr;
+  uint32_t m_track_id = 0;
+
+  heif_compression_format m_compression_format = heif_compression_undefined;
+
+  uint32_t m_first_sample = 0;
+  uint32_t m_last_sample = 0;
+
+  //uint32_t m_sample_description_index = 0;
+
+  uint32_t m_next_sample_to_be_decoded = 0;
+
+  struct SampleFileRange
+  {
+    uint64_t offset = 0;
+    uint32_t size = 0;
+  };
+
+  std::vector<SampleFileRange> m_sample_ranges;
+
+  std::shared_ptr<class Decoder> m_decoder;
+  std::shared_ptr<class Encoder> m_encoder;
+};
+
+
+#endif //LIBHEIF_CHUNK_H
diff -pruN 1.19.8-1/libheif/sequences/seq_boxes.cc 1.20.1-1/libheif/sequences/seq_boxes.cc
--- 1.19.8-1/libheif/sequences/seq_boxes.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/seq_boxes.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,1981 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sequences/seq_boxes.h"
+#include <iomanip>
+#include <set>
+#include <limits>
+#include <utility>
+
+
+Error Box_container::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  return read_children(range, READ_CHILDREN_ALL, limits);
+}
+
+
+std::string Box_container::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << Box::dump(indent);
+  sstr << dump_children(indent);
+
+  return sstr.str();
+}
+
+
+double Box_mvhd::get_matrix_element(int idx) const
+{
+  if (idx == 8) {
+    return 1.0;
+  }
+
+  return m_matrix[idx] / double(0x10000);
+}
+
+
+Error Box_mvhd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 1) {
+    return unsupported_version_error("mvhd");
+  }
+
+  if (get_version() == 1) {
+    m_creation_time = range.read64();
+    m_modification_time = range.read64();
+    m_timescale = range.read32();
+    m_duration = range.read64();
+  }
+  else {
+    // version==0
+    m_creation_time = range.read32();
+    m_modification_time = range.read32();
+    m_timescale = range.read32();
+    m_duration = range.read32();
+  }
+
+  m_rate = range.read32();
+  m_volume = range.read16();
+  range.skip(2);
+  range.skip(8);
+  for (uint32_t& m : m_matrix) {
+    m = range.read32();
+  }
+  for (int i = 0; i < 6; i++) {
+    range.skip(4);
+  }
+
+  m_next_track_ID = range.read32();
+
+  return range.get_error();
+}
+
+
+std::string Box_mvhd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "creation time:     " << m_creation_time << "\n"
+      << indent << "modification time: " << m_modification_time << "\n"
+      << indent << "timescale: " << m_timescale << "\n"
+      << indent << "duration: " << m_duration << "\n";
+  sstr << indent << "rate: " << get_rate() << "\n"
+      << indent << "volume: " << get_volume() << "\n"
+      << indent << "matrix:\n";
+  for (int y = 0; y < 3; y++) {
+    sstr << indent << "  ";
+    for (int i = 0; i < 3; i++) {
+      sstr << get_matrix_element(i + 3 * y) << " ";
+    }
+    sstr << "\n";
+  }
+  sstr << indent << "next_track_ID: " << m_next_track_ID << "\n";
+
+  return sstr.str();
+}
+
+
+void Box_mvhd::derive_box_version()
+{
+  if (m_creation_time > 0xFFFFFFFF ||
+      m_modification_time > 0xFFFFFFFF ||
+      m_timescale > 0xFFFFFFFF ||
+      m_duration > 0xFFFFFFFF) {
+    set_version(1);
+  }
+  else {
+    set_version(0);
+  }
+}
+
+
+Error Box_mvhd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  if (get_version() == 1) {
+    writer.write64(m_creation_time);
+    writer.write64(m_modification_time);
+    writer.write64(m_timescale);
+    writer.write64(m_duration);
+  }
+  else {
+    // version==0
+    writer.write32(static_cast<uint32_t>(m_creation_time));
+    writer.write32(static_cast<uint32_t>(m_modification_time));
+    writer.write32(static_cast<uint32_t>(m_timescale));
+    writer.write32(static_cast<uint32_t>(m_duration));
+  }
+
+  writer.write32(m_rate);
+  writer.write16(m_volume);
+  writer.write16(0);
+  writer.write64(0);
+  for (uint32_t m : m_matrix) {
+    writer.write32(m);
+  }
+  for (int i = 0; i < 6; i++) {
+    writer.write32(0);
+  }
+
+  writer.write32(m_next_track_ID);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+double Box_tkhd::get_matrix_element(int idx) const
+{
+  if (idx == 8) {
+    return 1.0;
+  }
+
+  return m_matrix[idx] / double(0x10000);
+}
+
+
+Error Box_tkhd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 1) {
+    return unsupported_version_error("tkhd");
+  }
+
+  if (get_version() == 1) {
+    m_creation_time = range.read64();
+    m_modification_time = range.read64();
+    m_track_id = range.read32();
+    range.skip(4);
+    m_duration = range.read64();
+  }
+  else {
+    // version==0
+    m_creation_time = range.read32();
+    m_modification_time = range.read32();
+    m_track_id = range.read32();
+    range.skip(4);
+    m_duration = range.read32();
+  }
+
+  range.skip(8);
+  m_layer = range.read16();
+  m_alternate_group = range.read16();
+  m_volume = range.read16();
+  range.skip(2);
+  for (uint32_t& m : m_matrix) {
+    m = range.read32();
+  }
+
+  m_width = range.read32();
+  m_height = range.read32();
+
+  return range.get_error();
+}
+
+
+std::string Box_tkhd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "creation time:     " << m_creation_time << "\n"
+      << indent << "modification time: " << m_modification_time << "\n"
+      << indent << "track ID: " << m_track_id << "\n"
+      << indent << "duration: " << m_duration << "\n";
+  sstr << indent << "layer: " << m_layer << "\n"
+      << indent << "alternate_group: " << m_alternate_group << "\n"
+      << indent << "volume: " << get_volume() << "\n"
+      << indent << "matrix:\n";
+  for (int y = 0; y < 3; y++) {
+    sstr << indent << "  ";
+    for (int i = 0; i < 3; i++) {
+      sstr << get_matrix_element(i + 3 * y) << " ";
+    }
+    sstr << "\n";
+  }
+
+  sstr << indent << "width: " << get_width() << "\n"
+      << indent << "height: " << get_height() << "\n";
+
+  return sstr.str();
+}
+
+
+void Box_tkhd::derive_box_version()
+{
+  if (m_creation_time > 0xFFFFFFFF ||
+      m_modification_time > 0xFFFFFFFF ||
+      m_duration > 0xFFFFFFFF) {
+    set_version(1);
+  }
+  else {
+    set_version(0);
+  }
+}
+
+
+Error Box_tkhd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  if (get_version() == 1) {
+    writer.write64(m_creation_time);
+    writer.write64(m_modification_time);
+    writer.write32(m_track_id);
+    writer.write32(0);
+    writer.write64(m_duration);
+  }
+  else {
+    // version==0
+    writer.write32(static_cast<uint32_t>(m_creation_time));
+    writer.write32(static_cast<uint32_t>(m_modification_time));
+    writer.write32(m_track_id);
+    writer.write32(0);
+    writer.write32(static_cast<uint32_t>(m_duration));
+  }
+
+  writer.write64(0);
+  writer.write16(m_layer);
+  writer.write16(m_alternate_group);
+  writer.write16(m_volume);
+  writer.write16(0);
+  for (uint32_t m : m_matrix) {
+    writer.write32(m);
+  }
+
+  writer.write32(m_width);
+  writer.write32(m_height);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_mdhd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 1) {
+    return unsupported_version_error("mdhd");
+  }
+
+  if (get_version() == 1) {
+    m_creation_time = range.read64();
+    m_modification_time = range.read64();
+    m_timescale = range.read32();
+    m_duration = range.read64();
+  }
+  else {
+    // version==0
+    m_creation_time = range.read32();
+    m_modification_time = range.read32();
+    m_timescale = range.read32();
+    m_duration = range.read32();
+  }
+
+  uint16_t language_packed = range.read16();
+  m_language[0] = ((language_packed >> 10) & 0x1F) + 0x60;
+  m_language[1] = ((language_packed >> 5) & 0x1F) + 0x60;
+  m_language[2] = ((language_packed >> 0) & 0x1F) + 0x60;
+  m_language[3] = 0;
+
+  range.skip(2);
+
+  return range.get_error();
+}
+
+
+std::string Box_mdhd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "creation time:     " << m_creation_time << "\n"
+      << indent << "modification time: " << m_modification_time << "\n"
+      << indent << "timescale: " << m_timescale << "\n"
+      << indent << "duration: " << m_duration << "\n";
+  sstr << indent << "language: " << m_language << "\n";
+
+  return sstr.str();
+}
+
+
+void Box_mdhd::derive_box_version()
+{
+  if (m_creation_time > 0xFFFFFFFF ||
+      m_modification_time > 0xFFFFFFFF ||
+      m_duration > 0xFFFFFFFF) {
+    set_version(1);
+  }
+  else {
+    set_version(0);
+  }
+}
+
+
+Error Box_mdhd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  if (get_version() == 1) {
+    writer.write64(m_creation_time);
+    writer.write64(m_modification_time);
+    writer.write32(m_timescale);
+    writer.write64(m_duration);
+  }
+  else {
+    // version==0
+    writer.write32(static_cast<uint32_t>(m_creation_time));
+    writer.write32(static_cast<uint32_t>(m_modification_time));
+    writer.write32(m_timescale);
+    writer.write32(static_cast<uint32_t>(m_duration));
+  }
+
+  auto language_packed = static_cast<uint16_t>((((m_language[0] - 0x60) & 0x1F) << 10) |
+                                               (((m_language[1] - 0x60) & 0x1F) << 5) |
+                                               (((m_language[2] - 0x60) & 0x1F) << 0));
+  writer.write16(language_packed);
+  writer.write16(0);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+
+Error Box_vmhd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("vmhd");
+  }
+
+  m_graphics_mode = range.read16();
+  for (uint16_t& c : m_op_color) {
+    c = range.read16();
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_vmhd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "graphics mode: " << m_graphics_mode;
+  if (m_graphics_mode == 0) {
+    sstr << " (copy)";
+  }
+  sstr << "\n"
+       << indent << "op color: " << m_op_color[0] << "; " << m_op_color[1] << "; " << m_op_color[2] << "\n";
+
+  return sstr.str();
+}
+
+
+Error Box_vmhd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write16(m_graphics_mode);
+  for (uint16_t c : m_op_color) {
+    writer.write16(c);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_nmhd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("nmhd");
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_nmhd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+
+  return sstr.str();
+}
+
+
+Error Box_nmhd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_stsd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stsd");
+  }
+
+  uint32_t entry_count = range.read32();
+
+  if (limits->max_sample_description_box_entries &&
+      entry_count > limits->max_sample_description_box_entries) {
+    std::stringstream sstr;
+    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample description items exceeds the security limit of "
+         << limits->max_sample_description_box_entries << " items";
+
+    return {heif_error_Memory_allocation_error,
+            heif_suberror_Security_limit_exceeded,
+            sstr.str()};
+
+  }
+
+  for (uint32_t i = 0; i < entry_count; i++) {
+    std::shared_ptr<Box> entrybox;
+    Error err = Box::read(range, &entrybox, limits);
+    if (err) {
+      return err;
+    }
+
+#if 0
+    auto visualSampleEntry_box = std::dynamic_pointer_cast<Box_VisualSampleEntry>(entrybox);
+    if (!visualSampleEntry_box) {
+      return Error{heif_error_Invalid_input,
+                   heif_suberror_Unspecified,
+                   "Invalid or unknown VisualSampleEntry in stsd box."};
+    }
+#endif
+
+    m_sample_entries.push_back(entrybox);
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stsd::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  for (size_t i = 0; i < m_sample_entries.size(); i++) {
+    sstr << indent << "[" << i << "]\n";
+    indent++;
+    sstr << m_sample_entries[i]->dump(indent);
+    indent--;
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stsd::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(static_cast<uint32_t>(m_sample_entries.size()));
+  for (const auto& sample : m_sample_entries) {
+    sample->write(writer);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_stts::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stts");
+  }
+
+  uint32_t entry_count = range.read32();
+
+  if (auto err = m_memory_handle.alloc(entry_count * sizeof(TimeToSample),
+                                       limits, "the 'stts' table")) {
+    return err;
+  }
+
+  m_entries.resize(entry_count);
+
+  for (uint32_t i = 0; i < entry_count; i++) {
+    TimeToSample entry{};
+    entry.sample_count = range.read32();
+    entry.sample_delta = range.read32();
+    m_entries[i] = entry;
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stts::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  for (size_t i = 0; i < m_entries.size(); i++) {
+    sstr << indent << "[" << i << "] : cnt=" << m_entries[i].sample_count << ", delta=" << m_entries[i].sample_delta << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stts::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(static_cast<uint32_t>(m_entries.size()));
+  for (const auto& sample : m_entries) {
+    writer.write32(sample.sample_count);
+    writer.write32(sample.sample_delta);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+uint32_t Box_stts::get_sample_duration(uint32_t sample_idx)
+{
+  size_t i = 0;
+  while (i < m_entries.size()) {
+    if (sample_idx < m_entries[i].sample_count) {
+      return m_entries[i].sample_delta;
+    }
+    else {
+      sample_idx -= m_entries[i].sample_count;
+    }
+  }
+
+  return 0;
+}
+
+
+void Box_stts::append_sample_duration(uint32_t duration)
+{
+  if (m_entries.empty() || m_entries.back().sample_delta != duration) {
+    TimeToSample entry{};
+    entry.sample_delta = duration;
+    entry.sample_count = 1;
+    m_entries.push_back(entry);
+    return;
+  }
+
+  m_entries.back().sample_count++;
+}
+
+
+uint64_t Box_stts::get_total_duration(bool include_last_frame_duration)
+{
+  uint64_t total = 0;
+
+  for (const auto& entry : m_entries) {
+    total += entry.sample_count * uint64_t(entry.sample_delta);
+  }
+
+  if (!include_last_frame_duration && !m_entries.empty()) {
+    total -= m_entries.back().sample_delta;
+  }
+
+  return total;
+}
+
+
+Error Box_stsc::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stsc");
+  }
+
+  uint32_t entry_count = range.read32();
+
+  if (auto err = m_memory_handle.alloc(entry_count * sizeof(SampleToChunk),
+                                       limits, "the 'stsc' table")) {
+    return err;
+  }
+
+  m_entries.resize(entry_count);
+
+  for (uint32_t i = 0; i < entry_count; i++) {
+    SampleToChunk entry{};
+    entry.first_chunk = range.read32();
+    entry.samples_per_chunk = range.read32();
+    entry.sample_description_index = range.read32();
+    m_entries[i] = entry;
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stsc::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  for (size_t i = 0; i < m_entries.size(); i++) {
+    sstr << indent << "[" << i << "]\n"
+        << indent << "  first chunk: " << m_entries[i].first_chunk << "\n"
+        << indent << "  samples per chunk: " << m_entries[i].samples_per_chunk << "\n"
+        << indent << "  sample description index: " << m_entries[i].sample_description_index << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stsc::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(static_cast<uint32_t>(m_entries.size()));
+  for (const auto& sample : m_entries) {
+    writer.write32(sample.first_chunk);
+    writer.write32(sample.samples_per_chunk);
+    writer.write32(sample.sample_description_index);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+const Box_stsc::SampleToChunk* Box_stsc::get_chunk(uint32_t idx) const
+{
+  assert(idx>=1);
+  for (size_t i = 0 ; i < m_entries.size();i++) {
+    if (idx >= m_entries[i].first_chunk && (i==m_entries.size()-1 || idx < m_entries[i+1].first_chunk)) {
+      return &m_entries[i];
+    }
+  }
+
+  return nullptr;
+}
+
+
+void Box_stsc::add_chunk(uint32_t description_index)
+{
+  SampleToChunk entry{};
+  entry.first_chunk = 1; // TODO
+  entry.samples_per_chunk = 0;
+  entry.sample_description_index = description_index;
+  m_entries.push_back(entry);
+}
+
+
+void Box_stsc::increase_samples_in_chunk(uint32_t nFrames)
+{
+  assert(!m_entries.empty());
+
+  m_entries.back().samples_per_chunk += nFrames;
+}
+
+
+Error Box_stco::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stco");
+  }
+
+  uint32_t entry_count = range.read32();
+
+  // check required memory
+
+  uint64_t mem_size = entry_count * sizeof(uint32_t);
+  if (auto err = m_memory_handle.alloc(mem_size,
+                                       limits, "the 'stco' table")) {
+    return err;
+  }
+
+  for (uint32_t i = 0; i < entry_count; i++) {
+    m_offsets.push_back(range.read32());
+
+    if (range.error()) {
+      return range.get_error();
+    }
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stco::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  for (size_t i = 0; i < m_offsets.size(); i++) {
+    sstr << indent << "[" << i << "] : 0x" << std::hex << m_offsets[i] << std::dec << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stco::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(static_cast<uint32_t>(m_offsets.size()));
+
+  m_offset_start_pos = writer.get_position();
+
+  for (uint32_t offset : m_offsets) {
+    writer.write32(offset);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+void Box_stco::patch_file_pointers(StreamWriter& writer, size_t offset)
+{
+  size_t oldPosition = writer.get_position();
+
+  writer.set_position(m_offset_start_pos);
+
+  for (uint32_t chunk_offset : m_offsets) {
+    if (chunk_offset + offset > std::numeric_limits<uint32_t>::max()) {
+      writer.write32(0); // TODO: error
+    }
+    else {
+      writer.write32(static_cast<uint32_t>(chunk_offset + offset));
+    }
+  }
+
+  writer.set_position(oldPosition);
+}
+
+
+
+Error Box_stsz::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stsz");
+  }
+
+  m_fixed_sample_size = range.read32();
+  m_sample_count = range.read32();
+
+  if (m_fixed_sample_size == 0) {
+    // check required memory
+
+    uint64_t mem_size = m_sample_count * sizeof(uint32_t);
+    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stsz' table")) {
+      return err;
+    }
+
+    for (uint32_t i = 0; i < m_sample_count; i++) {
+      m_sample_sizes.push_back(range.read32());
+
+      if (range.error()) {
+        return range.get_error();
+      }
+    }
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stsz::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "sample count: " << m_sample_count << "\n";
+  if (m_fixed_sample_size == 0) {
+    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
+      sstr << indent << "[" << i << "] : " << m_sample_sizes[i] << "\n";
+    }
+  }
+  else {
+    sstr << indent << "fixed sample size: " << m_fixed_sample_size << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stsz::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(m_fixed_sample_size);
+  writer.write32(m_sample_count);
+  if (m_fixed_sample_size == 0) {
+    assert(m_sample_count == m_sample_sizes.size());
+
+    for (uint32_t size : m_sample_sizes) {
+      writer.write32(size);
+    }
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+void Box_stsz::append_sample_size(uint32_t size)
+{
+  if (m_sample_count == 0 && size != 0) {
+    m_fixed_sample_size = size;
+    m_sample_count = 1;
+    return;
+  }
+
+  if (m_fixed_sample_size == size && size != 0) {
+    m_sample_count++;
+    return;
+  }
+
+  if (m_fixed_sample_size != 0) {
+    for (uint32_t i = 0; i < m_sample_count; i++) {
+      m_sample_sizes.push_back(m_fixed_sample_size);
+    }
+
+    m_fixed_sample_size = 0;
+  }
+
+  m_sample_sizes.push_back(size);
+  m_sample_count++;
+
+  assert(m_sample_count == m_sample_sizes.size());
+}
+
+
+Error Box_stss::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("stss");
+  }
+
+  uint32_t sample_count = range.read32();
+
+  // check required memory
+
+  uint64_t mem_size = sample_count * sizeof(uint32_t);
+  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'stss' table")) {
+    return err;
+  }
+
+  for (uint32_t i = 0; i < sample_count; i++) {
+    m_sync_samples.push_back(range.read32());
+
+    if (range.error()) {
+      return range.get_error();
+    }
+  }
+
+  return range.get_error();
+}
+
+
+std::string Box_stss::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  for (size_t i = 0; i < m_sync_samples.size(); i++) {
+    sstr << indent << "[" << i << "] : " << m_sync_samples[i] << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_stss::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(static_cast<uint32_t>(m_sync_samples.size()));
+  for (uint32_t sample : m_sync_samples) {
+    writer.write32(sample);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  (void)limits;
+
+  range.skip(6);
+  data_reference_index = range.read16();
+
+  pre_defined = range.read16();
+  range.skip(2);
+  for (uint32_t& p : pre_defined2) {
+    p = range.read32();
+  }
+  width = range.read16();
+  height = range.read16();
+  horizresolution = range.read32();
+  vertresolution = range.read32();
+  range.skip(4);
+  frame_count = range.read16();
+  compressorname = range.read_fixed_string(32);
+  depth = range.read16();
+  pre_defined3 = range.read16s();
+
+  // other boxes from derived specifications
+  //std::shared_ptr<Box_clap> clap; // optional // TODO
+  //std::shared_ptr<Box_pixi> pixi; // optional // TODO
+
+  return Error::Ok;
+}
+
+
+Error VisualSampleEntry::write(StreamWriter& writer) const
+{
+  writer.write32(0);
+  writer.write16(0);
+  writer.write16(data_reference_index);
+
+  writer.write16(pre_defined);
+  writer.write16(0);
+  for (uint32_t p : pre_defined2) {
+    writer.write32(p);
+  }
+
+  writer.write16(width);
+  writer.write16(height);
+  writer.write32(horizresolution);
+  writer.write32(vertresolution);
+  writer.write32(0);
+  writer.write16(frame_count);
+  writer.write_fixed_string(compressorname, 32);
+  writer.write16(depth);
+  writer.write16(pre_defined3);
+
+  return Error::Ok;
+}
+
+
+std::string VisualSampleEntry::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << indent << "data reference index: " << data_reference_index << "\n"
+       << indent << "width: " << width << "\n"
+       << indent << "height: " << height << "\n"
+       << indent << "horiz. resolution: " << get_horizontal_resolution() << "\n"
+       << indent << "vert. resolution: " << get_vertical_resolution() << "\n"
+       << indent << "frame count: " << frame_count << "\n"
+       << indent << "compressorname: " << compressorname << "\n"
+       << indent << "depth: " << depth << "\n";
+
+  return sstr.str();
+}
+
+
+Error Box_URIMetaSampleEntry::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(0);
+  writer.write16(0);
+  writer.write16(data_reference_index);
+
+  write_children(writer);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+std::string Box_URIMetaSampleEntry::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << Box::dump(indent);
+  sstr << indent << "data reference index: " << data_reference_index << "\n";
+  sstr << dump_children(indent);
+  return sstr.str();
+}
+
+
+Error Box_URIMetaSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  range.skip(6);
+  data_reference_index = range.read16();
+
+  Error err = read_children(range, READ_CHILDREN_ALL, limits);
+  if (err) {
+    return err;
+  }
+
+  return Error::Ok;
+}
+
+
+Error Box_uri::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("uri ");
+  }
+
+  m_uri = range.read_string();
+
+  return range.get_error();
+}
+
+
+std::string Box_uri::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "uri: " << m_uri << "\n";
+
+  return sstr.str();
+}
+
+
+Error Box_uri::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write(m_uri);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+
+Error Box_ccst::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 0) {
+    return unsupported_version_error("ccst");
+  }
+
+  uint32_t bits = range.read32();
+
+  auto& constraints = m_codingConstraints;
+
+  constraints.all_ref_pics_intra = (bits & 0x80000000) != 0;
+  constraints.intra_pred_used = (bits & 0x40000000) != 0;
+  constraints.max_ref_per_pic = (bits >> 26) & 0x0F;
+
+  return range.get_error();
+}
+
+
+std::string Box_ccst::dump(Indent& indent) const
+{
+  const auto& constraints = m_codingConstraints;
+
+  std::ostringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "all ref pics intra: " << std::boolalpha <<constraints.all_ref_pics_intra << "\n"
+       << indent << "intra pred used: " << constraints.intra_pred_used << "\n"
+       << indent << "max ref per pic: " << ((int) constraints.max_ref_per_pic) << "\n";
+
+  return sstr.str();
+}
+
+
+Error Box_ccst::write(StreamWriter& writer) const
+{
+  const auto& constraints = m_codingConstraints;
+
+  size_t box_start = reserve_box_header_space(writer);
+
+  uint32_t bits = 0;
+
+  if (constraints.all_ref_pics_intra) {
+    bits |= 0x80000000;
+  }
+
+  if (constraints.intra_pred_used) {
+    bits |= 0x40000000;
+  }
+
+  bits |= constraints.max_ref_per_pic << 26;
+
+  writer.write32(bits);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_VisualSampleEntry::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  Error err = get_VisualSampleEntry_const().write(writer);
+  if (err) {
+    return err;
+  }
+
+  write_children(writer);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+std::string Box_VisualSampleEntry::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << Box::dump(indent);
+  sstr << m_visualSampleEntry.dump(indent);
+  sstr << dump_children(indent);
+  return sstr.str();
+}
+
+
+Error Box_VisualSampleEntry::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  auto err = m_visualSampleEntry.parse(range, limits);
+  if (err) {
+    return err;
+  }
+
+  err = read_children(range, READ_CHILDREN_ALL, limits);
+  if (err) {
+    return err;
+  }
+
+  return Error::Ok;
+}
+
+
+std::string Box_sbgp::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << FullBox::dump(indent);
+  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
+
+  if (m_grouping_type_parameter) {
+    sstr << indent << "grouping_type_parameter: " << *m_grouping_type_parameter << "\n";
+  }
+
+  uint32_t total_samples = 0;
+  for (size_t i = 0; i < m_entries.size(); i++) {
+    sstr << indent << "[" << std::setw(2) << (i + 1) << "] : " << std::setw(3) << m_entries[i].sample_count << "x " << m_entries[i].group_description_index << "\n";
+    total_samples += m_entries[i].sample_count;
+  }
+  sstr << indent << "total samples: " << total_samples << "\n";
+
+  return sstr.str();
+}
+
+
+void Box_sbgp::derive_box_version()
+{
+  if (m_grouping_type_parameter) {
+    set_version(1);
+  }
+  else {
+    set_version(0);
+  }
+}
+
+
+Error Box_sbgp::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(m_grouping_type);
+  if (m_grouping_type_parameter) {
+    writer.write32(*m_grouping_type_parameter);
+  }
+
+  if (m_entries.size() > 0xFFFFFFFF) {
+    return {heif_error_Usage_error,
+            heif_suberror_Invalid_parameter_value,
+            "Too many sbgp entries."};
+  }
+
+  writer.write32(static_cast<uint32_t>(m_entries.size()));
+  for (const auto& entry : m_entries) {
+    writer.write32(entry.sample_count);
+    writer.write32(entry.group_description_index);
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_sbgp::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_version() > 1) {
+    return unsupported_version_error("sbgp");
+  }
+
+  m_grouping_type = range.read32();
+
+  if (get_version() == 1) {
+    m_grouping_type_parameter = range.read32();
+  }
+
+  uint32_t count = range.read32();
+  if (auto err = m_memory_handle.alloc(count * sizeof(Entry),
+                                       limits, "the 'sample to group' table")) {
+    return err;
+  }
+
+  for (uint32_t i = 0; i < count; i++) {
+    Entry e{};
+    e.sample_count = range.read32();
+    e.group_description_index = range.read32();
+    m_entries.push_back(e);
+    if (range.error()) {
+      return range.get_error();
+    }
+  }
+
+  return range.get_error();
+}
+
+
+std::string SampleGroupEntry_refs::dump() const
+{
+  std::stringstream sstr;
+  if (m_sample_id==0) {
+    sstr << "0 (non-ref) refs =";
+  }
+  else {
+    sstr << m_sample_id << " refs =";
+  }
+  for (uint32_t ref : m_direct_reference_sample_id) {
+    sstr << ' ' << ref;
+  }
+
+  return sstr.str();
+}
+
+Error SampleGroupEntry_refs::write(StreamWriter& writer) const
+{
+  return {};
+}
+
+Error SampleGroupEntry_refs::parse(BitstreamRange& range, const heif_security_limits*)
+{
+  m_sample_id = range.read32();
+  uint8_t cnt = range.read8();
+  for (uint8_t i = 0; i < cnt; i++) {
+    m_direct_reference_sample_id.push_back(range.read32());
+  }
+
+  return Error::Ok;
+}
+
+
+void Box_sgpd::derive_box_version()
+{
+  if (m_default_length) {
+    set_version(1);
+    assert(!m_default_sample_description_index);
+    return;
+  }
+
+  if (m_default_sample_description_index) {
+    set_version(2);
+    return;
+  }
+
+  set_version(0);
+}
+
+
+std::string Box_sgpd::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << FullBox::dump(indent);
+
+  sstr << indent << "grouping_type: " << fourcc_to_string(m_grouping_type) << "\n";
+  if (m_default_length) {
+    sstr << indent << "default_length: " << *m_default_length << "\n";
+  }
+  if (m_default_sample_description_index) {
+    sstr << indent << "default_sample_description_index: " << *m_default_sample_description_index << "\n";
+  }
+
+  for (size_t i=0; i<m_entries.size(); i++) {
+    sstr << indent << "[" << (i+1) << "] : ";
+    if (m_entries[i].sample_group_entry) {
+      sstr << m_entries[i].sample_group_entry->dump() << "\n";
+    }
+    else {
+      sstr << "empty (description_length=" << m_entries[i].description_length << ")\n";
+    }
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_sgpd::write(StreamWriter& writer) const
+{
+return {};
+}
+
+
+Error Box_sgpd::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  m_grouping_type = range.read32();
+
+  if (get_version() == 1) {
+    m_default_length = range.read32();
+  }
+
+  if (get_version() >= 2) {
+    m_default_sample_description_index = range.read32();
+  }
+
+  uint32_t entry_count = range.read32();
+
+  if (limits->max_sample_group_description_box_entries &&
+      entry_count > limits->max_sample_group_description_box_entries) {
+    std::stringstream sstr;
+    sstr << "Allocating " << static_cast<uint64_t>(entry_count) << " sample group description items exceeds the security limit of "
+         << limits->max_sample_group_description_box_entries << " items";
+
+    return {heif_error_Memory_allocation_error,
+            heif_suberror_Security_limit_exceeded,
+            sstr.str()};
+
+  }
+
+  for (uint32_t i = 0; i < entry_count; i++) {
+    Entry entry;
+
+    if (get_version() == 1) {
+      if (*m_default_length == 0) {
+        entry.description_length = range.read32();
+      }
+    }
+
+    switch (m_grouping_type) {
+      case fourcc("refs"): {
+        entry.sample_group_entry = std::make_shared<SampleGroupEntry_refs>();
+        Error err = entry.sample_group_entry->parse(range, limits);
+        if (err) {
+          return err;
+        }
+
+        break;
+      }
+
+      default:
+        break;
+    }
+
+    m_entries.emplace_back(std::move(entry));
+  }
+
+  return Error::Ok;
+}
+
+
+std::string Box_btrt::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << FullBox::dump(indent);
+
+  sstr << indent << "bufferSizeDB: " << m_bufferSizeDB << " bytes\n";
+  sstr << indent << "max bitrate: " << m_maxBitrate << " bits/sec\n";
+  sstr << indent << "avg bitrate: " << m_avgBitrate << " bits/sec\n";
+
+  return sstr.str();
+}
+
+
+Error Box_btrt::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  writer.write32(m_bufferSizeDB);
+  writer.write32(m_maxBitrate);
+  writer.write32(m_avgBitrate);
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_btrt::parse(BitstreamRange& range, const heif_security_limits*)
+{
+  m_bufferSizeDB = range.read32();
+  m_maxBitrate = range.read32();
+  m_avgBitrate = range.read32();
+
+  return Error::Ok;
+}
+
+
+
+void Box_saiz::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
+{
+  m_aux_info_type = aux_info_type;
+  m_aux_info_type_parameter = aux_info_type_parameter;
+
+  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
+  set_flags(nonnull ? 1 : 0);
+}
+
+
+void Box_saiz::add_sample_size(uint8_t s)
+{
+  // --- it is the first sample -> put into default size (except if it is a zero size = no sample aux info)
+
+  if (s != 0 && m_num_samples == 0) {
+    m_default_sample_info_size = s;
+    m_num_samples = 1;
+    return;
+  }
+
+  // --- if it's the default size, just add more to the number of default sizes
+
+  if (s != 0 && s == m_default_sample_info_size) {
+    m_num_samples++;
+    return;
+  }
+
+  // --- it is different from the default size -> add the list
+
+  // first copy samples with the default size into the list
+
+  if (m_default_sample_info_size != 0) {
+    for (uint32_t i = 0; i < m_num_samples; i++) {
+      m_sample_sizes.push_back(m_default_sample_info_size);
+    }
+
+    m_default_sample_info_size = 0;
+  }
+
+  // add the new sample size
+
+  m_num_samples++;
+  m_sample_sizes.push_back(s);
+}
+
+
+uint8_t Box_saiz::get_sample_size(uint32_t idx)
+{
+  if (m_default_sample_info_size != 0) {
+    return m_default_sample_info_size;
+  }
+
+  if (idx >= m_sample_sizes.size()) {
+    return 0;
+  }
+
+  return m_sample_sizes[idx];
+}
+
+
+std::string Box_saiz::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << FullBox::dump(indent);
+
+  sstr << indent << "aux_info_type: ";
+  if (m_aux_info_type == 0) {
+    sstr << "0\n";
+  }
+  else {
+    sstr << fourcc_to_string(m_aux_info_type) << "\n";
+  }
+
+  sstr << indent << "aux_info_type_parameter: ";
+  if (m_aux_info_type_parameter == 0) {
+    sstr << "0\n";
+  }
+  else {
+    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
+  }
+
+  sstr << indent << "default sample size: ";
+  if (m_default_sample_info_size == 0) {
+    sstr << "0 (variable)\n";
+  }
+  else {
+    sstr << ((int)m_default_sample_info_size) << "\n";
+  }
+
+  if (m_default_sample_info_size == 0) {
+    for (size_t i = 0; i < m_sample_sizes.size(); i++) {
+      sstr << indent << "[" << i << "] : " << ((int) m_sample_sizes[i]) << "\n";
+    }
+  }
+
+  return sstr.str();
+}
+
+
+Error Box_saiz::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  if (get_flags() & 1) {
+    writer.write32(m_aux_info_type);
+    writer.write32(m_aux_info_type_parameter);
+  }
+
+  writer.write8(m_default_sample_info_size);
+  writer.write32(m_num_samples);
+
+  if (m_default_sample_info_size == 0) {
+    assert(m_num_samples == m_sample_sizes.size());
+
+    for (uint8_t size : m_sample_sizes) {
+      writer.write8(size);
+    }
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_saiz::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_flags() & 1) {
+    m_aux_info_type = range.read32();
+    m_aux_info_type_parameter = range.read32();
+  }
+
+  m_default_sample_info_size = range.read8();
+  m_num_samples = range.read32();
+
+  if (m_default_sample_info_size == 0) {
+    // check required memory
+
+    uint64_t mem_size = m_num_samples;
+    if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'sample aux info sizes' (saiz) table")) {
+      return err;
+    }
+
+    // read whole table at once
+
+    m_sample_sizes.resize(m_num_samples);
+    range.read(m_sample_sizes.data(), m_num_samples);
+  }
+
+  return range.get_error();
+}
+
+
+
+void Box_saio::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
+{
+  m_aux_info_type = aux_info_type;
+  m_aux_info_type_parameter = aux_info_type_parameter;
+
+  bool nonnull = (m_aux_info_type != 0 || m_aux_info_type_parameter != 0);
+  set_flags(nonnull ? 1 : 0);
+}
+
+
+void Box_saio::add_sample_offset(uint64_t s)
+{
+  if (s > 0xFFFFFFFF) {
+    m_need_64bit = true;
+    set_version(1);
+  }
+
+  m_sample_offset.push_back(s);
+}
+
+
+uint64_t Box_saio::get_sample_offset(uint32_t idx) const
+{
+  if (idx >= m_sample_offset.size()) {
+    return 0;
+  }
+  else {
+    return m_sample_offset[idx];
+  }
+}
+
+
+std::string Box_saio::dump(Indent& indent) const
+{
+  std::stringstream sstr;
+  sstr << FullBox::dump(indent);
+
+  sstr << indent << "aux_info_type: ";
+  if (m_aux_info_type == 0) {
+    sstr << "0\n";
+  }
+  else {
+    sstr << fourcc_to_string(m_aux_info_type) << "\n";
+  }
+
+  sstr << indent << "aux_info_type_parameter: ";
+  if (m_aux_info_type_parameter == 0) {
+    sstr << "0\n";
+  }
+  else {
+    sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
+  }
+
+  for (size_t i = 0; i < m_sample_offset.size(); i++) {
+    sstr << indent << "[" << i << "] : 0x" << std::hex << m_sample_offset[i] << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+void Box_saio::patch_file_pointers(StreamWriter& writer, size_t offset)
+{
+  size_t oldPosition = writer.get_position();
+
+  writer.set_position(m_offset_start_pos);
+
+  for (uint64_t ptr : m_sample_offset) {
+    if (get_version() == 0 && ptr + offset > std::numeric_limits<uint32_t>::max()) {
+      writer.write32(0); // TODO: error
+    } else if (get_version() == 0) {
+      writer.write32(static_cast<uint32_t>(ptr + offset));
+    } else {
+      writer.write64(ptr + offset);
+    }
+  }
+
+  writer.set_position(oldPosition);
+}
+
+
+Error Box_saio::write(StreamWriter& writer) const
+{
+  size_t box_start = reserve_box_header_space(writer);
+
+  if (get_flags() & 1) {
+    writer.write32(m_aux_info_type);
+    writer.write32(m_aux_info_type_parameter);
+  }
+
+  if (m_sample_offset.size() > std::numeric_limits<uint32_t>::max()) {
+    return Error{heif_error_Unsupported_feature,
+                 heif_suberror_Unspecified,
+                 "Maximum number of samples exceeded"};
+  }
+  writer.write32(static_cast<uint32_t>(m_sample_offset.size()));
+
+  m_offset_start_pos = writer.get_position();
+
+  for (uint64_t size : m_sample_offset) {
+    if (m_need_64bit) {
+      writer.write64(size);
+    } else {
+      writer.write32(static_cast<uint32_t>(size));
+    }
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  parse_full_box_header(range);
+
+  if (get_flags() & 1) {
+    m_aux_info_type = range.read32();
+    m_aux_info_type_parameter = range.read32();
+  }
+
+  uint32_t num_samples = range.read32();
+
+  // check required memory
+  uint64_t mem_size = num_samples * sizeof(uint64_t);
+
+  if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'saio' table")) {
+    return err;
+  }
+
+  m_sample_offset.resize(num_samples);
+
+  for (uint32_t i = 0; i < num_samples; i++) {
+    uint64_t offset;
+    if (get_version() == 1) {
+      offset = range.read64();
+    }
+    else {
+      offset = range.read32();
+    }
+
+    m_sample_offset[i] = offset;
+
+    if (range.error()) {
+      return range.get_error();
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+Error Box_tref::parse(BitstreamRange& range, const heif_security_limits* limits)
+{
+  while (!range.eof()) {
+    BoxHeader header;
+    Error err = header.parse_header(range);
+    if (err != Error::Ok) {
+      return err;
+    }
+
+    if (header.get_box_size() < header.get_header_size()) {
+      return {heif_error_Invalid_input,
+              heif_suberror_Unspecified,
+              "Invalid box size (smaller than header)"};
+    }
+
+    uint64_t dataSize = (header.get_box_size() - header.get_header_size());
+
+    if (dataSize % 4 != 0 || dataSize < 4) {
+      return {heif_error_Invalid_input,
+              heif_suberror_Unspecified,
+              "Input file has a 'tref' TrackReferenceTypeBox with invalid size."};
+    }
+
+    uint64_t nRefs = dataSize / 4;
+
+    if (limits->max_items && nRefs > limits->max_items) {
+      std::stringstream sstr;
+      sstr << "Number of references in tref box (" << nRefs << ") exceeds the security limits of " << limits->max_items << " references.";
+
+      return {heif_error_Invalid_input,
+              heif_suberror_Security_limit_exceeded,
+              sstr.str()};
+    }
+
+    Reference ref;
+    ref.reference_type = header.get_short_type();
+
+    for (uint64_t i = 0; i < nRefs; i++) {
+      if (range.eof()) {
+        std::stringstream sstr;
+        sstr << "tref box should contain " << nRefs << " references, but we can only read " << i << " references.";
+
+        return {heif_error_Invalid_input,
+                heif_suberror_End_of_data,
+                sstr.str()};
+      }
+
+      ref.to_track_id.push_back(static_cast<uint32_t>(range.read32()));
+    }
+
+    m_references.push_back(ref);
+  }
+
+
+  // --- check for duplicate references
+
+  if (auto error = check_for_double_references()) {
+    return error;
+  }
+
+  return range.get_error();
+}
+
+
+Error Box_tref::check_for_double_references() const
+{
+  for (const auto& ref : m_references) {
+    std::set<uint32_t> to_ids;
+    for (const auto to_id : ref.to_track_id) {
+      if (to_ids.find(to_id) == to_ids.end()) {
+        to_ids.insert(to_id);
+      }
+      else {
+        return {heif_error_Invalid_input,
+                heif_suberror_Unspecified,
+                "'tref' has double references"};
+      }
+    }
+  }
+
+  return Error::Ok;
+}
+
+
+Error Box_tref::write(StreamWriter& writer) const
+{
+  if (auto error = check_for_double_references()) {
+    return error;
+  }
+
+  size_t box_start = reserve_box_header_space(writer);
+
+  for (const auto& ref : m_references) {
+    uint32_t box_size = 8 + uint32_t(ref.to_track_id.size() * 4);
+
+    // we write the BoxHeader ourselves since it is very simple
+    writer.write32(box_size);
+    writer.write32(ref.reference_type);
+
+    for (uint32_t r : ref.to_track_id) {
+      writer.write32(r);
+    }
+  }
+
+  prepend_header(writer, box_start);
+
+  return Error::Ok;
+}
+
+
+std::string Box_tref::dump(Indent& indent) const
+{
+  std::ostringstream sstr;
+  sstr << Box::dump(indent);
+
+  for (const auto& ref : m_references) {
+    sstr << indent << "reference with type '" << fourcc_to_string(ref.reference_type) << "'"
+         << " to track IDs: ";
+    for (uint32_t id : ref.to_track_id) {
+      sstr << id << " ";
+    }
+    sstr << "\n";
+  }
+
+  return sstr.str();
+}
+
+
+std::vector<uint32_t> Box_tref::get_references(uint32_t ref_type) const
+{
+  for (const Reference& ref : m_references) {
+    if (ref.reference_type == ref_type) {
+      return ref.to_track_id;
+    }
+  }
+
+  return {};
+}
+
+
+size_t Box_tref::get_number_of_references_of_type(uint32_t ref_type) const
+{
+  for (const Reference& ref : m_references) {
+    if (ref.reference_type == ref_type) {
+      return ref.to_track_id.size();
+    }
+  }
+
+  return 0;
+}
+
+
+std::vector<uint32_t> Box_tref::get_reference_types() const
+{
+  std::vector<uint32_t> types;
+  types.reserve(m_references.size());
+  for (const auto& ref : m_references) {
+    types.push_back(ref.reference_type);
+  }
+
+  return types;
+}
+
+
+void Box_tref::add_references(uint32_t to_track_id, uint32_t type)
+{
+  for (auto& ref : m_references) {
+    if (ref.reference_type == type) {
+      ref.to_track_id.push_back(to_track_id);
+      return;
+    }
+  }
+
+  Reference ref;
+  ref.reference_type = type;
+  ref.to_track_id = {to_track_id};
+
+  m_references.push_back(ref);
+}
diff -pruN 1.19.8-1/libheif/sequences/seq_boxes.h 1.20.1-1/libheif/sequences/seq_boxes.h
--- 1.19.8-1/libheif/sequences/seq_boxes.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/seq_boxes.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,862 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SEQ_BOXES_H
+#define SEQ_BOXES_H
+
+#include "box.h"
+#include "security_limits.h"
+
+#include <string>
+#include <memory>
+#include <vector>
+
+
+class Box_container : public Box {
+public:
+  Box_container(const char* type)
+  {
+    set_short_type(fourcc(type));
+  }
+
+  std::string dump(Indent&) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+};
+
+
+// Movie Box
+class Box_moov : public Box_container {
+public:
+  Box_moov() : Box_container("moov") {}
+
+  const char* debug_box_name() const override { return "Movie"; }
+};
+
+
+// Movie Header Box
+class Box_mvhd : public FullBox {
+public:
+  Box_mvhd()
+  {
+    set_short_type(fourcc("mvhd"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Movie Header"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void derive_box_version() override;
+
+  double get_rate() const { return m_rate / double(0x10000); }
+
+  float get_volume() const { return float(m_volume) / float(0x100); }
+
+  double get_matrix_element(int idx) const;
+
+  uint32_t get_time_scale() const { return m_timescale; }
+
+  uint64_t get_duration() const { return m_duration; }
+
+  void set_duration(uint64_t duration) { m_duration = duration; }
+
+  void set_time_scale(uint32_t timescale) { m_timescale = timescale; }
+
+  void set_next_track_id(uint32_t next_id) { m_next_track_ID = next_id; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint64_t m_creation_time = 0;
+  uint64_t m_modification_time = 0;
+  uint32_t m_timescale = 0;
+  uint64_t m_duration = 0;
+
+  uint32_t m_rate = 0x00010000; // typically 1.0
+  uint16_t m_volume = 0x0100; // typically, full volume
+
+  uint32_t m_matrix[9] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
+  uint32_t m_next_track_ID = 0;
+};
+
+
+// Track Box
+class Box_trak : public Box_container {
+public:
+  Box_trak() : Box_container("trak") {}
+
+  const char* debug_box_name() const override { return "Track"; }
+};
+
+
+// Track Header Box
+class Box_tkhd : public FullBox {
+public:
+  Box_tkhd()
+  {
+    set_short_type(fourcc("tkhd"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Track Header"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void derive_box_version() override;
+
+  float get_volume() const { return float(m_volume) / float(0x100); }
+
+  double get_matrix_element(int idx) const;
+
+  double get_width() const { return float(m_width) / double(0x10000); }
+
+  double get_height() const { return float(m_height) / double(0x10000); }
+
+  uint32_t get_track_id() const { return m_track_id; }
+
+  void set_track_id(uint32_t track_id) { m_track_id = track_id; }
+
+  void set_resolution(double width, double height)
+  {
+    m_width = (uint32_t) (width * 0x10000);
+    m_height = (uint32_t) (height * 0x10000);
+  }
+
+  uint64_t get_duration() const { return m_duration; }
+
+  void set_duration(uint64_t duration) { m_duration = duration; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint64_t m_creation_time = 0;
+  uint64_t m_modification_time = 0;
+  uint32_t m_track_id = 0;
+  uint64_t m_duration = 0;
+
+  uint16_t m_layer = 0;
+  uint16_t m_alternate_group = 0;
+  uint16_t m_volume = 0x0100; // typically, full volume
+
+  uint32_t m_matrix[9] = {0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
+
+  uint32_t m_width = 0;
+  uint32_t m_height = 0;
+};
+
+
+// Media Box
+class Box_mdia : public Box_container {
+public:
+  Box_mdia() : Box_container("mdia") {}
+
+  const char* debug_box_name() const override { return "Media"; }
+};
+
+
+// Media Header Box
+class Box_mdhd : public FullBox {
+public:
+  Box_mdhd()
+  {
+    set_short_type(fourcc("mdhd"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Media Header"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void derive_box_version() override;
+
+  double get_matrix_element(int idx) const;
+
+  uint32_t get_timescale() const { return m_timescale; }
+
+  void set_timescale(uint32_t timescale) { m_timescale = timescale; }
+
+  uint64_t get_duration() const { return m_duration; }
+
+  void set_duration(uint64_t duration) { m_duration = duration; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint64_t m_creation_time = 0;
+  uint64_t m_modification_time = 0;
+  uint32_t m_timescale = 0;
+  uint64_t m_duration = 0;
+
+  char m_language[4] = {'u', 'n', 'k', 0};
+};
+
+
+// Media Information Box (container)
+class Box_minf : public Box_container {
+public:
+  Box_minf() : Box_container("minf") {}
+
+  const char* debug_box_name() const override { return "Media Information"; }
+};
+
+
+// Video Media Header
+class Box_vmhd : public FullBox {
+public:
+  Box_vmhd()
+  {
+    set_short_type(fourcc("vmhd"));
+    set_flags(1);
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Video Media Header"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint16_t m_graphics_mode = 0;
+  uint16_t m_op_color[3] = {0, 0, 0};
+};
+
+
+// Null Media Header
+class Box_nmhd : public FullBox {
+public:
+  Box_nmhd()
+  {
+    set_short_type(fourcc("nmhd"));
+    set_flags(1);
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Null Media Header"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+};
+
+
+// Sample Table Box (container)
+class Box_stbl : public Box_container {
+public:
+  Box_stbl() : Box_container("stbl") {}
+
+  const char* debug_box_name() const override { return "Sample Table"; }
+};
+
+
+// Sample Description Box
+class Box_stsd : public FullBox {
+public:
+  Box_stsd()
+  {
+    set_short_type(fourcc("stsd"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Description"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  std::shared_ptr<const class Box> get_sample_entry(size_t idx) const
+  {
+    if (idx >= m_sample_entries.size()) {
+      return nullptr;
+    } else {
+      return m_sample_entries[idx];
+    }
+  }
+
+  void add_sample_entry(std::shared_ptr<class Box> entry)
+  {
+    m_sample_entries.push_back(entry);
+  }
+
+  size_t get_num_sample_entries() const { return m_sample_entries.size(); }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  std::vector<std::shared_ptr<class Box>> m_sample_entries;
+};
+
+
+// Decoding Time to Sample Box
+class Box_stts : public FullBox {
+public:
+  Box_stts()
+  {
+    set_short_type(fourcc("stts"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Decoding Time to Sample"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  struct TimeToSample {
+    uint32_t sample_count;
+    uint32_t sample_delta;
+  };
+
+  uint32_t get_sample_duration(uint32_t sample_idx);
+
+  void append_sample_duration(uint32_t duration);
+
+  uint64_t get_total_duration(bool include_last_frame_duration);
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  std::vector<TimeToSample> m_entries;
+  MemoryHandle m_memory_handle;
+};
+
+
+// Sample to Chunk Box
+class Box_stsc : public FullBox {
+public:
+  Box_stsc()
+  {
+    set_short_type(fourcc("stsc"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample to Chunk"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  struct SampleToChunk {
+    uint32_t first_chunk;
+    uint32_t samples_per_chunk;
+    uint32_t sample_description_index;
+  };
+
+  const std::vector<SampleToChunk>& get_chunks() const { return m_entries; }
+
+  // idx counting starts at 1
+  const SampleToChunk* get_chunk(uint32_t idx) const;
+
+  void add_chunk(uint32_t description_index);
+
+  void increase_samples_in_chunk(uint32_t nFrames);
+
+  bool last_chunk_empty() const {
+    assert(!m_entries.empty());
+
+    return m_entries.back().samples_per_chunk == 0;
+  }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  std::vector<SampleToChunk> m_entries;
+  MemoryHandle m_memory_handle;
+};
+
+
+// Chunk Offset Box
+class Box_stco : public FullBox {
+public:
+  Box_stco()
+  {
+    set_short_type(fourcc("stco"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Offset"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void add_chunk_offset(uint32_t offset) { m_offsets.push_back(offset); }
+
+  const std::vector<uint32_t>& get_offsets() const { return m_offsets; }
+
+  void patch_file_pointers(StreamWriter&, size_t offset) override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  std::vector<uint32_t> m_offsets;
+  MemoryHandle m_memory_handle;
+
+  mutable size_t m_offset_start_pos = 0;
+};
+
+
+// Sample Size Box
+class Box_stsz : public FullBox {
+public:
+  Box_stsz()
+  {
+    set_short_type(fourcc("stsz"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Size"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  bool has_fixed_sample_size() const { return m_fixed_sample_size != 0; }
+
+  uint32_t get_fixed_sample_size() const { return m_fixed_sample_size; }
+
+  const std::vector<uint32_t>& get_sample_sizes() const { return m_sample_sizes; }
+
+  void append_sample_size(uint32_t size);
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_fixed_sample_size = 0;
+  uint32_t m_sample_count = 0;
+  std::vector<uint32_t> m_sample_sizes;
+  MemoryHandle m_memory_handle;
+};
+
+
+// Sync Sample Box
+class Box_stss : public FullBox {
+public:
+  Box_stss()
+  {
+    set_short_type(fourcc("stss"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sync Sample"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void add_sync_sample(uint32_t sample_idx) { m_sync_samples.push_back(sample_idx); }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  std::vector<uint32_t> m_sync_samples;
+  MemoryHandle m_memory_handle;
+};
+
+
+struct CodingConstraints {
+  bool all_ref_pics_intra = false;
+  bool intra_pred_used = false;
+  uint8_t max_ref_per_pic = 0; // 4 bit
+};
+
+
+// Coding Constraints Box
+class Box_ccst : public FullBox {
+public:
+  Box_ccst()
+  {
+    set_short_type(fourcc("ccst"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Coding Constraints"; }
+
+  Error write(StreamWriter& writer) const override;
+
+  void set_coding_constraints(const CodingConstraints& c) { m_codingConstraints = c; }
+
+  const CodingConstraints& get_coding_constraints() const { return m_codingConstraints; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  CodingConstraints m_codingConstraints;
+};
+
+
+struct VisualSampleEntry {
+  // from SampleEntry
+  //const unsigned int(8)[6] reserved = 0;
+  uint16_t data_reference_index;
+
+  // VisualSampleEntry
+
+  uint16_t pre_defined = 0;
+  //uint16_t reserved = 0;
+  uint32_t pre_defined2[3] = {0, 0, 0};
+  uint16_t width = 0;
+  uint16_t height = 0;
+  uint32_t horizresolution = 0x00480000; // 72 dpi
+  uint32_t vertresolution = 0x00480000; // 72 dpi
+  //uint32_t reserved = 0;
+  uint16_t frame_count = 1;
+  std::string compressorname; // max 32 characters
+  uint16_t depth = 0x0018;
+  int16_t pre_defined3 = -1;
+  // other boxes from derived specifications
+  //std::shared_ptr<Box_clap> clap; // optional
+  //std::shared_ptr<Box_pixi> pixi; // optional
+
+  double get_horizontal_resolution() const { return horizresolution / double(0x10000); }
+
+  double get_vertical_resolution() const { return vertresolution / double(0x10000); }
+
+  Error parse(BitstreamRange& range, const heif_security_limits*);
+
+  Error write(StreamWriter& writer) const;
+
+  std::string dump(Indent&) const;
+};
+
+
+class Box_VisualSampleEntry : public Box {
+public:
+  Error write(StreamWriter& writer) const override;
+
+  std::string dump(Indent&) const override;
+
+  const VisualSampleEntry& get_VisualSampleEntry_const() const { return m_visualSampleEntry; }
+
+  VisualSampleEntry& get_VisualSampleEntry() { return m_visualSampleEntry; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
+
+private:
+  VisualSampleEntry m_visualSampleEntry;
+};
+
+
+class Box_URIMetaSampleEntry : public Box {
+public:
+  Box_URIMetaSampleEntry()
+  {
+    set_short_type(fourcc("urim"));
+  }
+
+  Error write(StreamWriter& writer) const override;
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "URI Meta Sample Entry"; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
+
+private:
+  // from SampleEntry
+  //const unsigned int(8)[6] reserved = 0;
+  uint16_t data_reference_index;
+};
+
+
+class Box_uri : public FullBox {
+public:
+  Box_uri()
+  {
+    set_short_type(fourcc("uri "));
+  }
+
+  void set_uri(std::string uri) { m_uri = uri; }
+
+  std::string get_uri() const { return m_uri; }
+
+  Error write(StreamWriter& writer) const override;
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "URI"; }
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
+
+private:
+  std::string m_uri;
+};
+
+
+// Sample to Group
+class Box_sbgp : public FullBox {
+public:
+  Box_sbgp()
+  {
+    set_short_type(fourcc("sbgp"));
+  }
+
+  void derive_box_version() override;
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample to Group"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_grouping_type = 0; // 4cc
+  std::optional<uint32_t> m_grouping_type_parameter;
+
+  struct Entry {
+    uint32_t sample_count;
+    uint32_t group_description_index;
+  };
+
+  std::vector<Entry> m_entries;
+  MemoryHandle m_memory_handle;
+};
+
+
+class SampleGroupEntry
+{
+public:
+  virtual ~SampleGroupEntry() = default;
+
+  virtual std::string dump() const = 0;
+
+  virtual Error write(StreamWriter& writer) const = 0;
+
+  virtual Error parse(BitstreamRange& range, const heif_security_limits*) = 0;
+};
+
+
+class SampleGroupEntry_refs : public SampleGroupEntry
+{
+public:
+  std::string dump() const override;
+
+  Error write(StreamWriter& writer) const override;
+
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_sample_id;
+  std::vector<uint32_t> m_direct_reference_sample_id;
+};
+
+
+// Sample Group Description
+class Box_sgpd : public FullBox {
+public:
+  Box_sgpd()
+  {
+    set_short_type(fourcc("sgpd"));
+  }
+
+  void derive_box_version() override;
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Group Description"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_grouping_type = 0; // 4cc
+  std::optional<uint32_t> m_default_length; // version 1  (0 -> variable length)
+  std::optional<uint32_t> m_default_sample_description_index;; // version >= 2
+
+  struct Entry {
+    uint32_t description_length = 0; // if version==1 && m_default_length == 0
+    std::shared_ptr<SampleGroupEntry> sample_group_entry;
+  };
+
+  std::vector<Entry> m_entries;
+};
+
+// Bitrate
+class Box_btrt : public FullBox {
+public:
+  Box_btrt()
+  {
+    set_short_type(fourcc("btrt"));
+  }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Bitrate"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_bufferSizeDB;
+  uint32_t m_maxBitrate;
+  uint32_t m_avgBitrate;
+};
+
+
+class Box_saiz : public FullBox {
+public:
+  Box_saiz()
+  {
+    set_short_type(fourcc("saiz"));
+  }
+
+  void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0);
+
+  uint32_t get_aux_info_type() const { return m_aux_info_type; }
+
+  uint32_t get_aux_info_type_parameter() const { return m_aux_info_type_parameter; }
+
+  void add_sample_size(uint8_t s);
+
+  void add_nonpresent_sample() { add_sample_size(0); }
+
+  uint8_t get_sample_size(uint32_t idx);
+
+  uint32_t get_num_samples() const { return m_num_samples; }
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Auxiliary Information Sizes"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+private:
+  uint32_t m_aux_info_type = 0;
+  uint32_t m_aux_info_type_parameter = 0;
+  uint8_t  m_default_sample_info_size = 0;  // 0 -> variable length
+  uint32_t m_num_samples = 0; // needed in case we are using the default sample size
+
+  std::vector<uint8_t> m_sample_sizes;
+  MemoryHandle m_memory_handle;
+};
+
+
+class Box_saio : public FullBox {
+public:
+  Box_saio()
+  {
+    set_short_type(fourcc("saio"));
+  }
+
+  void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0);
+
+  uint32_t get_aux_info_type() const { return m_aux_info_type; }
+
+  uint32_t get_aux_info_type_parameter() const { return m_aux_info_type_parameter; }
+
+  void add_sample_offset(uint64_t offset);
+
+  // This will be 1 if all infos are written contiguously
+  size_t get_num_samples() const { return m_sample_offset.size(); }
+
+  uint64_t get_sample_offset(uint32_t idx) const;
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Sample Auxiliary Information Offsets"; }
+
+  Error write(StreamWriter& writer) const override;
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+  void patch_file_pointers(StreamWriter& writer, size_t offset) override;
+
+private:
+  uint32_t m_aux_info_type = 0;
+  uint32_t m_aux_info_type_parameter = 0;
+
+  bool m_need_64bit = false;
+  mutable uint64_t m_offset_start_pos;
+
+  // If sample_offset==1, all samples are stored contiguous in the file
+  std::vector<uint64_t> m_sample_offset;
+
+  MemoryHandle m_memory_handle;
+};
+
+
+class Box_tref : public Box
+{
+public:
+  Box_tref()
+  {
+    set_short_type(fourcc("tref"));
+  }
+
+  struct Reference {
+    uint32_t reference_type;
+    std::vector<uint32_t> to_track_id;
+  };
+
+  std::string dump(Indent&) const override;
+
+  const char* debug_box_name() const override { return "Track Reference"; }
+
+  std::vector<uint32_t> get_references(uint32_t ref_type) const;
+
+  size_t get_number_of_references_of_type(uint32_t ref_type) const;
+
+  size_t get_number_of_reference_types() const { return m_references.size(); }
+
+  std::vector<uint32_t> get_reference_types() const;
+
+  void add_references(uint32_t to_track_id, uint32_t type);
+
+protected:
+  Error parse(BitstreamRange& range, const heif_security_limits*) override;
+
+  Error write(StreamWriter& writer) const override;
+
+  Error check_for_double_references() const;
+
+private:
+  std::vector<Reference> m_references;
+};
+
+
+#endif //SEQ_BOXES_H
diff -pruN 1.19.8-1/libheif/sequences/track.cc 1.20.1-1/libheif/sequences/track.cc
--- 1.19.8-1/libheif/sequences/track.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,749 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cstring>
+#include "track.h"
+#include "context.h"
+#include "sequences/seq_boxes.h"
+#include "sequences/chunk.h"
+#include "sequences/track_visual.h"
+#include "sequences/track_metadata.h"
+#include "libheif/api_structs.h"
+#include <limits>
+
+
+TrackOptions& TrackOptions::operator=(const TrackOptions& src)
+{
+  if (&src == this) {
+    return *this;
+  }
+
+  this->track_timescale = src.track_timescale;
+  this->write_sample_aux_infos_interleaved = src.write_sample_aux_infos_interleaved;
+  this->with_sample_tai_timestamps = src.with_sample_tai_timestamps;
+
+  if (src.tai_clock_info) {
+    this->tai_clock_info = heif_tai_clock_info_alloc();
+    heif_tai_clock_info_copy(this->tai_clock_info, src.tai_clock_info);
+  }
+  else {
+    this->tai_clock_info = nullptr;
+  }
+
+  this->with_sample_content_ids = src.with_sample_content_ids;
+  this->gimi_track_content_id = src.gimi_track_content_id;
+
+  return *this;
+}
+
+
+SampleAuxInfoHelper::SampleAuxInfoHelper(bool interleaved)
+    : m_interleaved(interleaved)
+{
+  m_saiz = std::make_shared<Box_saiz>();
+  m_saio = std::make_shared<Box_saio>();
+}
+
+
+void SampleAuxInfoHelper::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter)
+{
+  m_saiz->set_aux_info_type(aux_info_type, aux_info_type_parameter);
+  m_saio->set_aux_info_type(aux_info_type, aux_info_type_parameter);
+}
+
+Error SampleAuxInfoHelper::add_sample_info(const std::vector<uint8_t>& data)
+{
+  if (data.size() > 0xFF) {
+    return {heif_error_Encoding_error,
+            heif_suberror_Unspecified,
+            "Encoded sample auxiliary information exceeds maximum size"};
+  }
+
+  m_saiz->add_sample_size(static_cast<uint8_t>(data.size()));
+
+  m_data.insert(m_data.end(), data.begin(), data.end());
+
+  return Error::Ok;
+}
+
+void SampleAuxInfoHelper::add_nonpresent_sample()
+{
+  m_saiz->add_nonpresent_sample();
+}
+
+
+void SampleAuxInfoHelper::write_interleaved(const std::shared_ptr<class HeifFile>& file)
+{
+  if (m_interleaved && !m_data.empty()) {
+    uint64_t pos = file->append_mdat_data(m_data);
+    m_saio->add_sample_offset(pos);
+
+    m_data.clear();
+  }
+}
+
+void SampleAuxInfoHelper::write_all(const std::shared_ptr<class Box>& parent, const std::shared_ptr<class HeifFile>& file)
+{
+  parent->append_child_box(m_saiz);
+  parent->append_child_box(m_saio);
+
+  if (!m_data.empty()) {
+    uint64_t pos = file->append_mdat_data(m_data);
+    m_saio->add_sample_offset(pos);
+  }
+}
+
+
+SampleAuxInfoReader::SampleAuxInfoReader(std::shared_ptr<Box_saiz> saiz,
+                                         std::shared_ptr<Box_saio> saio)
+{
+  m_saiz = saiz;
+  m_saio = saio;
+
+  m_contiguous = (saio->get_num_samples() == 1);
+  if (m_contiguous) {
+    uint64_t offset = saio->get_sample_offset(0);
+    auto nSamples = saiz->get_num_samples();
+
+    for (uint32_t i=0;i<nSamples;i++) {
+      m_contiguous_offsets.push_back(offset);
+      offset += saiz->get_sample_size(i);
+    }
+
+    // TODO: we could add a special case for contiguous data with constant size
+  }
+}
+
+
+heif_sample_aux_info_type SampleAuxInfoReader::get_type() const
+{
+  heif_sample_aux_info_type type;
+  type.type = m_saiz->get_aux_info_type();
+  type.parameter = m_saiz->get_aux_info_type_parameter();
+  return type;
+}
+
+
+Result<std::vector<uint8_t>> SampleAuxInfoReader::get_sample_info(const HeifFile* file, uint32_t idx)
+{
+  uint64_t offset;
+  if (m_contiguous) {
+    offset = m_contiguous_offsets[idx];
+  }
+  else {
+    offset = m_saio->get_sample_offset(idx);
+  }
+
+  uint8_t size = m_saiz->get_sample_size(idx);
+
+  std::vector<uint8_t> data;
+  Error err = file->append_data_from_file_range(data, offset, size);
+  if (err) {
+    return err;
+  }
+
+  return data;
+}
+
+
+std::shared_ptr<class HeifFile> Track::get_file() const
+{
+  return m_heif_context->get_heif_file();
+}
+
+
+Track::Track(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak_box)
+{
+  m_heif_context = ctx;
+
+  m_trak = trak_box;
+
+  auto tkhd = trak_box->get_child_box<Box_tkhd>();
+  if (!tkhd) {
+    return; // TODO: error or dummy error track ?
+  }
+
+  m_id = tkhd->get_track_id();
+
+  auto mdia = trak_box->get_child_box<Box_mdia>();
+  if (!mdia) {
+    return;
+  }
+
+  m_tref = trak_box->get_child_box<Box_tref>();
+
+  auto hdlr = mdia->get_child_box<Box_hdlr>();
+  if (!hdlr) {
+    return;
+  }
+
+  m_handler_type = hdlr->get_handler_type();
+
+  m_minf = mdia->get_child_box<Box_minf>();
+  if (!m_minf) {
+    return;
+  }
+
+  m_mdhd = mdia->get_child_box<Box_mdhd>();
+  if (!m_mdhd) {
+    return;
+  }
+
+  auto stbl = m_minf->get_child_box<Box_stbl>();
+  if (!stbl) {
+    return;
+  }
+
+  m_stsd = stbl->get_child_box<Box_stsd>();
+  if (!m_stsd) {
+    return;
+  }
+
+  m_stsc = stbl->get_child_box<Box_stsc>();
+  if (!m_stsc) {
+    return;
+  }
+
+  m_stco = stbl->get_child_box<Box_stco>();
+  if (!m_stco) {
+    return;
+  }
+
+  m_stsz = stbl->get_child_box<Box_stsz>();
+  if (!m_stsz) {
+    return;
+  }
+
+  m_stts = stbl->get_child_box<Box_stts>();
+
+  const std::vector<uint32_t>& chunk_offsets = m_stco->get_offsets();
+  assert(chunk_offsets.size() <= (size_t) std::numeric_limits<uint32_t>::max()); // There cannot be more than uint32_t chunks.
+
+  uint32_t current_sample_idx = 0;
+
+  for (size_t chunk_idx = 0; chunk_idx < chunk_offsets.size(); chunk_idx++) {
+    auto* s2c = m_stsc->get_chunk(static_cast<uint32_t>(chunk_idx + 1));
+    if (!s2c) {
+      return;
+    }
+
+    Box_stsc::SampleToChunk sampleToChunk = *s2c;
+
+    auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1);
+    if (!sample_description) {
+      return;
+    }
+
+    if (m_first_taic == nullptr) {
+      auto taic = sample_description->get_child_box<Box_taic>();
+      if (taic) {
+        m_first_taic = taic;
+      }
+    }
+
+    auto chunk = std::make_shared<Chunk>(ctx, m_id, sample_description,
+                                         current_sample_idx, sampleToChunk.samples_per_chunk,
+                                         m_stco->get_offsets()[chunk_idx],
+                                         m_stsz);
+
+    m_chunks.push_back(chunk);
+
+    current_sample_idx += sampleToChunk.samples_per_chunk;
+  }
+
+  // --- read sample auxiliary information boxes
+
+  std::vector<std::shared_ptr<Box_saiz>> saiz_boxes = stbl->get_child_boxes<Box_saiz>();
+  std::vector<std::shared_ptr<Box_saio>> saio_boxes = stbl->get_child_boxes<Box_saio>();
+
+  for (const auto& saiz : saiz_boxes) {
+    uint32_t aux_info_type = saiz->get_aux_info_type();
+    uint32_t aux_info_type_parameter = saiz->get_aux_info_type_parameter();
+
+    // find the corresponding saio box
+
+    std::shared_ptr<Box_saio> saio;
+    for (const auto& candidate : saio_boxes) {
+      if (candidate->get_aux_info_type() == aux_info_type &&
+          candidate->get_aux_info_type_parameter() == aux_info_type_parameter) {
+        saio = candidate;
+        break;
+      }
+    }
+
+    if (saio) {
+      if (aux_info_type == fourcc("suid")) {
+        m_aux_reader_content_ids = std::make_unique<SampleAuxInfoReader>(saiz, saio);
+      }
+
+      if (aux_info_type == fourcc("stai")) {
+        m_aux_reader_tai_timestamps = std::make_unique<SampleAuxInfoReader>(saiz, saio);
+      }
+    }
+  }
+
+  // --- read track properties
+
+  if (auto meta = trak_box->get_child_box<Box_meta>()) {
+    auto iloc = meta->get_child_box<Box_iloc>();
+    auto idat = meta->get_child_box<Box_idat>();
+
+    auto iinf = meta->get_child_box<Box_iinf>();
+    if (iinf) {
+      auto infe_boxes = iinf->get_child_boxes<Box_infe>();
+      for (const auto& box : infe_boxes) {
+        if (box->get_item_type_4cc() == fourcc("uri ") &&
+            box->get_item_uri_type() == "urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73") {
+          heif_item_id id = box->get_item_ID();
+
+          std::vector<uint8_t> data;
+          Error err = iloc->read_data(id, ctx->get_heif_file()->get_reader(), idat, &data, ctx->get_security_limits());
+          if (err) {
+            // TODO
+          }
+
+          Result contentIdResult = vector_to_string(data);
+          if (contentIdResult.error) {
+            // TODO
+          }
+
+          m_track_info.gimi_track_content_id = contentIdResult.value;
+        }
+      }
+    }
+  }
+}
+
+
+Track::Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* options, uint32_t handler_type)
+{
+  m_heif_context = ctx;
+
+  m_moov = ctx->get_heif_file()->get_moov_box();
+  assert(m_moov);
+
+  // --- find next free track ID
+
+  if (track_id == 0) {
+    track_id = 1; // minimum track ID
+
+    for (const auto& track : m_moov->get_child_boxes<Box_trak>()) {
+      auto tkhd = track->get_child_box<Box_tkhd>();
+
+      if (tkhd->get_track_id() >= track_id) {
+        track_id = tkhd->get_track_id() + 1;
+      }
+    }
+
+    auto mvhd = m_moov->get_child_box<Box_mvhd>();
+    mvhd->set_next_track_id(track_id + 1);
+
+    m_id = track_id;
+  }
+
+  m_trak = std::make_shared<Box_trak>();
+  m_moov->append_child_box(m_trak);
+
+  m_tkhd = std::make_shared<Box_tkhd>();
+  m_trak->append_child_box(m_tkhd);
+  m_tkhd->set_track_id(track_id);
+
+  auto mdia = std::make_shared<Box_mdia>();
+  m_trak->append_child_box(mdia);
+
+  m_mdhd = std::make_shared<Box_mdhd>();
+  m_mdhd->set_timescale(options ? options->track_timescale : 90000);
+  mdia->append_child_box(m_mdhd);
+
+  m_hdlr = std::make_shared<Box_hdlr>();
+  mdia->append_child_box(m_hdlr);
+  m_hdlr->set_handler_type(handler_type);
+
+  m_minf = std::make_shared<Box_minf>();
+  mdia->append_child_box(m_minf);
+
+  // vmhd is added in Track_Visual
+
+  m_stbl = std::make_shared<Box_stbl>();
+  m_minf->append_child_box(m_stbl);
+
+  m_stsd = std::make_shared<Box_stsd>();
+  m_stbl->append_child_box(m_stsd);
+
+  m_stts = std::make_shared<Box_stts>();
+  m_stbl->append_child_box(m_stts);
+
+  m_stsc = std::make_shared<Box_stsc>();
+  m_stbl->append_child_box(m_stsc);
+
+  m_stsz = std::make_shared<Box_stsz>();
+  m_stbl->append_child_box(m_stsz);
+
+  m_stco = std::make_shared<Box_stco>();
+  m_stbl->append_child_box(m_stco);
+
+  m_stss = std::make_shared<Box_stss>();
+  m_stbl->append_child_box(m_stss);
+
+  if (options) {
+    m_track_info = *options;
+
+    if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
+      m_aux_helper_tai_timestamps = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved);
+      m_aux_helper_tai_timestamps->set_aux_info_type(fourcc("stai"));
+    }
+
+    if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) {
+      m_aux_helper_content_ids = std::make_unique<SampleAuxInfoHelper>(m_track_info.write_sample_aux_infos_interleaved);
+      m_aux_helper_content_ids->set_aux_info_type(fourcc("suid"));
+    }
+
+    if (!options->gimi_track_content_id.empty()) {
+      auto hdlr_box = std::make_shared<Box_hdlr>();
+      hdlr_box->set_handler_type(fourcc("meta"));
+
+      auto uuid_box = std::make_shared<Box_infe>();
+      uuid_box->set_item_type_4cc(fourcc("uri "));
+      uuid_box->set_item_uri_type("urn:uuid:15beb8e4-944d-5fc6-a3dd-cb5a7e655c73");
+      uuid_box->set_item_ID(1);
+
+      auto iinf_box = std::make_shared<Box_iinf>();
+      iinf_box->append_child_box(uuid_box);
+
+      std::vector<uint8_t> track_uuid_vector;
+      track_uuid_vector.insert(track_uuid_vector.begin(),
+                               options->gimi_track_content_id.c_str(),
+                               options->gimi_track_content_id.c_str() + options->gimi_track_content_id.length() + 1);
+
+      auto iloc_box = std::make_shared<Box_iloc>();
+      iloc_box->append_data(1, track_uuid_vector, 1);
+
+      auto meta_box = std::make_shared<Box_meta>();
+      meta_box->append_child_box(hdlr_box);
+      meta_box->append_child_box(iinf_box);
+      meta_box->append_child_box(iloc_box);
+
+      m_trak->append_child_box(meta_box);
+    }
+  }
+}
+
+
+std::shared_ptr<Track> Track::alloc_track(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak)
+{
+  auto mdia = trak->get_child_box<Box_mdia>();
+  if (!mdia) {
+    return nullptr;
+  }
+
+  auto hdlr = mdia->get_child_box<Box_hdlr>();
+  if (!mdia) {
+    return nullptr;
+  }
+
+  switch (hdlr->get_handler_type()) {
+    case fourcc("pict"):
+    case fourcc("vide"):
+      return std::make_shared<Track_Visual>(ctx, trak);
+    case fourcc("meta"):
+      return std::make_shared<Track_Metadata>(ctx, trak);
+    default:
+      return nullptr;
+  }
+}
+
+
+bool Track::is_visual_track() const
+{
+  return m_handler_type == fourcc("pict");
+}
+
+
+uint32_t Track::get_first_cluster_sample_entry_type() const
+{
+  if (m_stsd->get_num_sample_entries() == 0) {
+    return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry?
+  }
+
+  return m_stsd->get_sample_entry(0)->get_short_type();
+}
+
+
+Result<std::string> Track::get_first_cluster_urim_uri() const
+{
+  if (m_stsd->get_num_sample_entries() == 0) {
+    return Error{heif_error_Invalid_input,
+                 heif_suberror_Unspecified,
+                 "This track has no sample entries."};
+  }
+
+  std::shared_ptr<const Box> sampleEntry = m_stsd->get_sample_entry(0);
+  auto urim = std::dynamic_pointer_cast<const Box_URIMetaSampleEntry>(sampleEntry);
+  if (!urim) {
+    return Error{heif_error_Usage_error,
+                 heif_suberror_Unspecified,
+                 "This cluster is no 'urim' sample entry."};
+  }
+
+  std::shared_ptr<const Box_uri> uri = urim->get_child_box<const Box_uri>();
+  if (!uri) {
+    return Error{heif_error_Invalid_input,
+                 heif_suberror_Unspecified,
+                 "The 'urim' box has no 'uri' child box."};
+  }
+
+  return uri->get_uri();
+}
+
+
+bool Track::end_of_sequence_reached() const
+{
+  return (m_next_sample_to_be_processed > m_chunks.back()->last_sample_number());
+}
+
+
+void Track::finalize_track()
+{
+  if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_all(m_stbl, get_file());
+  if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_all(m_stbl, get_file());
+
+  uint64_t duration = m_stts->get_total_duration(false);
+  m_mdhd->set_duration(duration);
+}
+
+
+uint64_t Track::get_duration_in_media_units() const
+{
+  return m_mdhd->get_duration();
+}
+
+
+uint32_t Track::get_timescale() const
+{
+  return m_mdhd->get_timescale();
+}
+
+
+void Track::set_track_duration_in_movie_units(uint64_t total_duration)
+{
+  m_tkhd->set_duration(total_duration);
+}
+
+
+void Track::add_chunk(heif_compression_format format)
+{
+  auto chunk = std::make_shared<Chunk>(m_heif_context, m_id, format);
+  m_chunks.push_back(chunk);
+
+  int chunkIdx = (uint32_t) m_chunks.size();
+  m_stsc->add_chunk(chunkIdx);
+}
+
+void Track::set_sample_description_box(std::shared_ptr<Box> sample_description_box)
+{
+  // --- add 'taic' when we store timestamps as sample auxiliary information
+
+  if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
+    auto taic = std::make_shared<Box_taic>();
+    taic->set_from_tai_clock_info(m_track_info.tai_clock_info);
+    sample_description_box->append_child_box(taic);
+  }
+
+  m_stsd->add_sample_entry(sample_description_box);
+}
+
+
+Error Track::write_sample_data(const std::vector<uint8_t>& raw_data, uint32_t sample_duration, bool is_sync_sample,
+                               const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID)
+{
+  size_t data_start = m_heif_context->get_heif_file()->append_mdat_data(raw_data);
+
+  // first sample in chunk? -> write chunk offset
+
+  if (m_stsc->last_chunk_empty()) {
+    // if auxiliary data is interleaved, write it between the chunks
+    if (m_aux_helper_tai_timestamps) m_aux_helper_tai_timestamps->write_interleaved(get_file());
+    if (m_aux_helper_content_ids) m_aux_helper_content_ids->write_interleaved(get_file());
+
+    // TODO
+    assert(data_start < 0xFF000000); // add some headroom for header data
+    m_stco->add_chunk_offset(static_cast<uint32_t>(data_start));
+  }
+
+  m_stsc->increase_samples_in_chunk(1);
+
+  m_stsz->append_sample_size((uint32_t)raw_data.size());
+
+  if (is_sync_sample) {
+    m_stss->add_sync_sample(m_next_sample_to_be_processed + 1);
+  }
+
+  if (sample_duration == 0) {
+    return {heif_error_Usage_error,
+            heif_suberror_Unspecified,
+            "Sample duration may not be 0"};
+  }
+
+  m_stts->append_sample_duration(sample_duration);
+
+
+  // --- sample timestamp
+
+  if (m_track_info.with_sample_tai_timestamps != heif_sample_aux_info_presence_none) {
+    if (tai) {
+      std::vector<uint8_t> tai_data = Box_itai::encode_tai_to_bitstream(tai);
+      auto err = m_aux_helper_tai_timestamps->add_sample_info(tai_data);
+      if (err) {
+        return err;
+      }
+    }
+    else if (m_track_info.with_sample_tai_timestamps == heif_sample_aux_info_presence_optional) {
+      m_aux_helper_tai_timestamps->add_nonpresent_sample();
+    }
+    else {
+      return {heif_error_Encoding_error,
+              heif_suberror_Unspecified,
+              "Mandatory TAI timestamp missing"};
+    }
+  }
+
+  // --- sample content id
+
+  if (m_track_info.with_sample_content_ids != heif_sample_aux_info_presence_none) {
+    if (!gimi_contentID.empty()) {
+      auto id = gimi_contentID;
+      const char* id_str = id.c_str();
+      std::vector<uint8_t> id_vector;
+      id_vector.insert(id_vector.begin(), id_str, id_str + id.length() + 1);
+      auto err = m_aux_helper_content_ids->add_sample_info(id_vector);
+      if (err) {
+        return err;
+      }
+    } else if (m_track_info.with_sample_content_ids == heif_sample_aux_info_presence_optional) {
+      m_aux_helper_content_ids->add_nonpresent_sample();
+    } else {
+      return {heif_error_Encoding_error,
+              heif_suberror_Unspecified,
+              "Mandatory ContentID missing"};
+    }
+  }
+
+  m_next_sample_to_be_processed++;
+
+  return Error::Ok;
+}
+
+
+void Track::add_reference_to_track(uint32_t referenceType, uint32_t to_track_id)
+{
+  if (!m_tref) {
+    m_tref = std::make_shared<Box_tref>();
+    m_trak->append_child_box(m_tref);
+  }
+
+  m_tref->add_references(to_track_id, referenceType);
+}
+
+
+Result<heif_raw_sequence_sample*> Track::get_next_sample_raw_data()
+{
+  if (m_current_chunk > m_chunks.size()) {
+    return Error{heif_error_End_of_sequence,
+                 heif_suberror_Unspecified,
+                 "End of sequence"};
+  }
+
+  while (m_next_sample_to_be_processed > m_chunks[m_current_chunk]->last_sample_number()) {
+    m_current_chunk++;
+
+    if (m_current_chunk > m_chunks.size()) {
+      return Error{heif_error_End_of_sequence,
+                   heif_suberror_Unspecified,
+                   "End of sequence"};
+    }
+  }
+
+  const std::shared_ptr<Chunk>& chunk = m_chunks[m_current_chunk];
+
+  DataExtent extent = chunk->get_data_extent_for_sample(m_next_sample_to_be_processed);
+  auto readResult = extent.read_data();
+  if (readResult.error) {
+    return readResult.error;
+  }
+
+  heif_raw_sequence_sample* sample = new heif_raw_sequence_sample();
+  sample->data = *readResult.value;
+
+  // read sample duration
+
+  if (m_stts) {
+    sample->duration = m_stts->get_sample_duration(m_next_sample_to_be_processed);
+  }
+
+  // --- read sample auxiliary data
+
+  if (m_aux_reader_content_ids) {
+    auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_processed);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    if (!readResult.value.empty()) {
+      Result<std::string> convResult = vector_to_string(readResult.value);
+      if (convResult.error) {
+        return convResult.error;
+      }
+
+      sample->gimi_sample_content_id = convResult.value;
+    }
+  }
+
+  if (m_aux_reader_tai_timestamps) {
+    auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_processed);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    if (!readResult.value.empty()) {
+      auto resultTai = Box_itai::decode_tai_from_vector(readResult.value);
+      if (resultTai.error) {
+        return resultTai.error;
+      }
+
+      sample->timestamp = heif_tai_timestamp_packet_alloc();
+      heif_tai_timestamp_packet_copy(sample->timestamp, &resultTai.value);
+    }
+  }
+
+  m_next_sample_to_be_processed++;
+
+  return sample;
+}
+
+
+std::vector<heif_sample_aux_info_type> Track::get_sample_aux_info_types() const
+{
+  std::vector<heif_sample_aux_info_type> types;
+
+  if (m_aux_reader_tai_timestamps) types.emplace_back(m_aux_reader_tai_timestamps->get_type());
+  if (m_aux_reader_content_ids) types.emplace_back(m_aux_reader_content_ids->get_type());
+
+  return types;
+}
diff -pruN 1.19.8-1/libheif/sequences/track.h 1.20.1-1/libheif/sequences/track.h
--- 1.19.8-1/libheif/sequences/track.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,229 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2024 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_TRACK_H
+#define LIBHEIF_TRACK_H
+
+#include "error.h"
+#include "libheif/api_structs.h"
+#include "libheif/heif_plugin.h"
+#include "libheif/heif_sequences.h"
+#include <string>
+#include <memory>
+#include <vector>
+
+class HeifContext;
+
+class HeifPixelImage;
+
+class Chunk;
+
+class Box_trak;
+
+
+class SampleAuxInfoHelper
+{
+public:
+  SampleAuxInfoHelper(bool interleaved = false);
+
+  void set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_parameter = 0);
+
+  Error add_sample_info(const std::vector<uint8_t>& data);
+
+  void add_nonpresent_sample();
+
+  void write_interleaved(const std::shared_ptr<class HeifFile>& file);
+
+  void write_all(const std::shared_ptr<class Box>& parent, const std::shared_ptr<class HeifFile>& file);
+
+private:
+  std::shared_ptr<class Box_saiz> m_saiz;
+  std::shared_ptr<class Box_saio> m_saio;
+
+  std::vector<uint8_t> m_data;
+
+  bool m_interleaved;
+};
+
+
+class SampleAuxInfoReader
+{
+public:
+  SampleAuxInfoReader(std::shared_ptr<Box_saiz>,
+                      std::shared_ptr<Box_saio>);
+
+  heif_sample_aux_info_type get_type() const;
+
+  Result<std::vector<uint8_t>> get_sample_info(const HeifFile* file, uint32_t idx);
+
+private:
+  std::shared_ptr<class Box_saiz> m_saiz;
+  std::shared_ptr<class Box_saio> m_saio;
+
+  bool m_contiguous;
+  std::vector<uint64_t> m_contiguous_offsets;
+};
+
+
+/**
+ * This structure specifies what will be written in a track and how it will be laid out in the file.
+ */
+struct TrackOptions
+{
+  ~TrackOptions()
+  {
+    heif_tai_clock_info_release(tai_clock_info);
+  }
+
+  // Timescale (clock ticks per second) for this track.
+  uint32_t track_timescale = 90000;
+
+  // If 'true', the aux_info data blocks will be interleaved with the compressed image.
+  // This has the advantage that the aux_info is localized near the image data.
+  //
+  // If 'false', all aux_info will be written as one block after the compressed image data.
+  // This has the advantage that no aux_info offsets have to be written.
+  bool write_sample_aux_infos_interleaved = false;
+
+
+  // --- TAI timestamps for samples
+  enum heif_sample_aux_info_presence with_sample_tai_timestamps = heif_sample_aux_info_presence_none;
+  struct heif_tai_clock_info* tai_clock_info = nullptr;
+
+  // --- GIMI content IDs for samples
+
+  enum heif_sample_aux_info_presence with_sample_content_ids = heif_sample_aux_info_presence_none;
+
+  // --- GIMI content ID for the track
+
+  std::string gimi_track_content_id;
+
+  TrackOptions& operator=(const TrackOptions&);
+};
+
+
+class Track : public ErrorBuffer {
+public:
+  //Track(HeifContext* ctx);
+
+  Track(HeifContext* ctx, uint32_t track_id, const TrackOptions* info, uint32_t handler_type);
+
+  Track(HeifContext* ctx, const std::shared_ptr<Box_trak>&); // when reading the file
+
+  virtual ~Track() = default;
+
+  // Allocate a Track of the correct sub-class (visual or metadata)
+  static std::shared_ptr<Track> alloc_track(HeifContext*, const std::shared_ptr<Box_trak>&);
+
+  heif_item_id get_id() const { return m_id; }
+
+  std::shared_ptr<class HeifFile> get_file() const;
+
+  uint32_t get_handler() const { return m_handler_type; }
+
+  bool is_visual_track() const;
+
+  uint32_t get_first_cluster_sample_entry_type() const;
+
+  Result<std::string> get_first_cluster_urim_uri() const;
+
+  uint64_t get_duration_in_media_units() const;
+
+  uint32_t get_timescale() const;
+
+  // The context will compute the duration in global movie units and set this.
+  void set_track_duration_in_movie_units(uint64_t total_duration);
+
+  std::shared_ptr<class Box_taic> get_first_cluster_taic() { return m_first_taic; }
+
+  bool end_of_sequence_reached() const;
+
+  // Compute some parameters after all frames have been encoded (for example: track duration).
+  void finalize_track();
+
+  const TrackOptions& get_track_info() const { return m_track_info; }
+
+  void add_reference_to_track(uint32_t referenceType, uint32_t to_track_id);
+
+  std::shared_ptr<const class Box_tref> get_tref_box() const { return m_tref; }
+
+  Result<heif_raw_sequence_sample*> get_next_sample_raw_data();
+
+  std::vector<heif_sample_aux_info_type> get_sample_aux_info_types() const;
+
+protected:
+  HeifContext* m_heif_context = nullptr;
+  uint32_t m_id = 0;
+  uint32_t m_handler_type = 0;
+
+  TrackOptions m_track_info;
+
+  uint32_t m_num_samples = 0;
+  uint32_t m_current_chunk = 0;
+  uint32_t m_next_sample_to_be_processed = 0;
+
+  std::vector<std::shared_ptr<Chunk>> m_chunks;
+
+  std::shared_ptr<class Box_moov> m_moov;
+  std::shared_ptr<class Box_trak> m_trak;
+  std::shared_ptr<class Box_tkhd> m_tkhd;
+  std::shared_ptr<class Box_minf> m_minf;
+  std::shared_ptr<class Box_mdhd> m_mdhd;
+  std::shared_ptr<class Box_hdlr> m_hdlr;
+  std::shared_ptr<class Box_stbl> m_stbl;
+  std::shared_ptr<class Box_stsd> m_stsd;
+  std::shared_ptr<class Box_stsc> m_stsc;
+  std::shared_ptr<class Box_stco> m_stco;
+  std::shared_ptr<class Box_stts> m_stts;
+  std::shared_ptr<class Box_stss> m_stss;
+  std::shared_ptr<class Box_stsz> m_stsz;
+
+  std::shared_ptr<class Box_tref> m_tref; // optional
+
+  // --- sample auxiliary information
+
+  std::unique_ptr<SampleAuxInfoHelper> m_aux_helper_tai_timestamps;
+  std::unique_ptr<SampleAuxInfoHelper> m_aux_helper_content_ids;
+
+  std::unique_ptr<SampleAuxInfoReader> m_aux_reader_tai_timestamps;
+  std::unique_ptr<SampleAuxInfoReader> m_aux_reader_content_ids;
+
+  std::shared_ptr<class Box_taic> m_first_taic; // the TAIC of the first chunk
+
+
+  // --- Helper functions for writing samples.
+
+  // Call when we begin a new chunk of samples, e.g. because the compression format changed
+  void add_chunk(heif_compression_format format);
+
+  // Call to set the sample_description_box for the last added chunk.
+  // Has to be called when we call add_chunk().
+  // It is not merged with add_chunk() because the sample_description_box may need information from the
+  // first encoded frame.
+  void set_sample_description_box(std::shared_ptr<Box> sample_description_box);
+
+  // Write the actual sample data. `tai` may be null and `gimi_contentID` may be empty.
+  // In these cases, no timestamp or no contentID will be written, respectively.
+  Error write_sample_data(const std::vector<uint8_t>& raw_data, uint32_t sample_duration, bool is_sync_sample,
+                          const heif_tai_timestamp_packet* tai, const std::string& gimi_contentID);
+};
+
+
+#endif //LIBHEIF_TRACK_H
diff -pruN 1.19.8-1/libheif/sequences/track_metadata.cc 1.20.1-1/libheif/sequences/track_metadata.cc
--- 1.19.8-1/libheif/sequences/track_metadata.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track_metadata.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,164 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "track_metadata.h"
+#include "chunk.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+#include <utility>
+
+
+Track_Metadata::Track_Metadata(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak)
+    : Track(ctx, trak)
+{
+  const std::vector<uint32_t>& chunk_offsets = m_stco->get_offsets();
+
+  // Find sequence resolution
+
+  if (!chunk_offsets.empty())  {
+    auto* s2c = m_stsc->get_chunk(static_cast<uint32_t>(1));
+    if (!s2c) {
+      return;
+    }
+
+    Box_stsc::SampleToChunk sampleToChunk = *s2c;
+
+    auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1);
+    if (!sample_description) {
+      return;
+    }
+
+    // TODO: read URI
+  }
+}
+
+
+Track_Metadata::Track_Metadata(HeifContext* ctx, uint32_t track_id, std::string uri, const TrackOptions* options)
+    : Track(ctx, track_id, options, fourcc("meta")),
+      m_uri(std::move(uri))
+{
+  auto nmhd = std::make_shared<Box_nmhd>();
+  m_minf->append_child_box(nmhd);
+}
+
+
+#if 0
+Result<std::shared_ptr<const Track_Metadata::Metadata>> Track_Metadata::read_next_metadata_sample()
+{
+  if (m_current_chunk > m_chunks.size()) {
+    return Error{heif_error_End_of_sequence,
+                 heif_suberror_Unspecified,
+                 "End of sequence"};
+  }
+
+  while (m_next_sample_to_be_decoded > m_chunks[m_current_chunk]->last_sample_number()) {
+    m_current_chunk++;
+
+    if (m_current_chunk > m_chunks.size()) {
+      return Error{heif_error_End_of_sequence,
+                   heif_suberror_Unspecified,
+                   "End of sequence"};
+    }
+  }
+
+  const std::shared_ptr<Chunk>& chunk = m_chunks[m_current_chunk];
+
+  auto decoder = chunk->get_decoder();
+  assert(decoder);
+
+  decoder->set_data_extent(chunk->get_data_extent_for_sample(m_next_sample_to_be_decoded));
+
+  Result<std::shared_ptr<HeifPixelImage>> decodingResult = decoder->decode_single_frame_from_compressed_data(options);
+  if (decodingResult.error) {
+    m_next_sample_to_be_decoded++;
+    return decodingResult.error;
+  }
+
+  auto image = decodingResult.value;
+
+  if (m_stts) {
+    image->set_sample_duration(m_stts->get_sample_duration(m_next_sample_to_be_decoded));
+  }
+
+  // --- read sample auxiliary data
+
+  if (m_aux_reader_content_ids) {
+    auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_decoded);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    Result<std::string> convResult = vector_to_string(readResult.value);
+    if (convResult.error) {
+      return convResult.error;
+    }
+
+    image->set_gimi_content_id(convResult.value);
+  }
+
+  if (m_aux_reader_tai_timestamps) {
+    auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_decoded);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    auto resultTai = Box_itai::decode_tai_from_vector(readResult.value);
+    if (resultTai.error) {
+      return resultTai.error;
+    }
+
+    image->set_tai_timestamp(&resultTai.value);
+  }
+
+  m_next_sample_to_be_decoded++;
+
+  return image;
+}
+#endif
+
+
+Error Track_Metadata::write_raw_metadata(const heif_raw_sequence_sample* raw_sample)
+{
+  // generate new chunk for first metadata packet
+
+  if (m_chunks.empty()) {
+
+    // --- write URIMetaSampleEntry ('urim')
+
+    auto sample_description_box = std::make_shared<Box_URIMetaSampleEntry>();
+    auto uri = std::make_shared<Box_uri>();
+    uri->set_uri(m_uri);
+    sample_description_box->append_child_box(uri);
+
+    add_chunk(heif_compression_undefined);
+    set_sample_description_box(sample_description_box);
+  }
+
+  Error err = write_sample_data(raw_sample->data,
+                                raw_sample->duration,
+                                true,
+                                raw_sample->timestamp,
+                                raw_sample->gimi_sample_content_id);
+  if (err) {
+    return err;
+  }
+
+  return Error::Ok;
+}
diff -pruN 1.19.8-1/libheif/sequences/track_metadata.h 1.20.1-1/libheif/sequences/track_metadata.h
--- 1.19.8-1/libheif/sequences/track_metadata.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track_metadata.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,46 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_TRACK_METADATA_H
+#define LIBHEIF_TRACK_METADATA_H
+
+#include "track.h"
+#include <string>
+#include <memory>
+#include <vector>
+
+
+class Track_Metadata : public Track {
+public:
+  //Track(HeifContext* ctx);
+
+  Track_Metadata(HeifContext* ctx, uint32_t track_id, std::string uri, const TrackOptions* options);
+
+  Track_Metadata(HeifContext* ctx, const std::shared_ptr<Box_trak>&); // when reading the file
+
+  ~Track_Metadata() override = default;
+
+  Error write_raw_metadata(const heif_raw_sequence_sample*);
+
+private:
+  std::string m_uri;
+};
+
+#endif //LIBHEIF_TRACK_METADATA_H
diff -pruN 1.19.8-1/libheif/sequences/track_visual.cc 1.20.1-1/libheif/sequences/track_visual.cc
--- 1.19.8-1/libheif/sequences/track_visual.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track_visual.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,281 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "track_visual.h"
+#include "codecs/decoder.h"
+#include "codecs/encoder.h"
+#include "chunk.h"
+#include "pixelimage.h"
+#include "context.h"
+#include "libheif/api_structs.h"
+#include "codecs/hevc_boxes.h"
+
+
+Track_Visual::Track_Visual(HeifContext* ctx, const std::shared_ptr<Box_trak>& trak)
+    : Track(ctx, trak)
+{
+  const std::vector<uint32_t>& chunk_offsets = m_stco->get_offsets();
+
+  // Find sequence resolution
+
+  if (!chunk_offsets.empty())  {
+    auto* s2c = m_stsc->get_chunk(static_cast<uint32_t>(1));
+    if (!s2c) {
+      return;
+    }
+
+    Box_stsc::SampleToChunk sampleToChunk = *s2c;
+
+    auto sample_description = m_stsd->get_sample_entry(sampleToChunk.sample_description_index - 1);
+    if (!sample_description) {
+      return; // TODO
+    }
+
+    auto visual_sample_description = std::dynamic_pointer_cast<const Box_VisualSampleEntry>(sample_description);
+    if (!visual_sample_description) {
+      return; // TODO
+    }
+
+    m_width = visual_sample_description->get_VisualSampleEntry_const().width;
+    m_height = visual_sample_description->get_VisualSampleEntry_const().height;
+  }
+}
+
+
+Track_Visual::Track_Visual(HeifContext* ctx, uint32_t track_id, uint16_t width, uint16_t height,
+                           const TrackOptions* options, uint32_t handler_type)
+    : Track(ctx, track_id, options, handler_type)
+{
+  m_tkhd->set_resolution(width, height);
+  //m_hdlr->set_handler_type(handler_type);  already done in Track()
+
+  auto vmhd = std::make_shared<Box_vmhd>();
+  m_minf->append_child_box(vmhd);
+}
+
+
+Result<std::shared_ptr<HeifPixelImage>> Track_Visual::decode_next_image_sample(const struct heif_decoding_options& options)
+{
+  if (m_current_chunk > m_chunks.size()) {
+    return Error{heif_error_End_of_sequence,
+                 heif_suberror_Unspecified,
+                 "End of sequence"};
+  }
+
+  while (m_next_sample_to_be_processed > m_chunks[m_current_chunk]->last_sample_number()) {
+    m_current_chunk++;
+
+    if (m_current_chunk > m_chunks.size()) {
+      return Error{heif_error_End_of_sequence,
+                   heif_suberror_Unspecified,
+                   "End of sequence"};
+    }
+  }
+
+  const std::shared_ptr<Chunk>& chunk = m_chunks[m_current_chunk];
+
+  auto decoder = chunk->get_decoder();
+  assert(decoder);
+
+  decoder->set_data_extent(chunk->get_data_extent_for_sample(m_next_sample_to_be_processed));
+
+  Result<std::shared_ptr<HeifPixelImage>> decodingResult = decoder->decode_single_frame_from_compressed_data(options,
+                                                                                                             m_heif_context->get_security_limits());
+  if (decodingResult.error) {
+    m_next_sample_to_be_processed++;
+    return decodingResult.error;
+  }
+
+  auto image = decodingResult.value;
+
+  if (m_stts) {
+    image->set_sample_duration(m_stts->get_sample_duration(m_next_sample_to_be_processed));
+  }
+
+  // --- read sample auxiliary data
+
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+  if (m_aux_reader_content_ids) {
+    auto readResult = m_aux_reader_content_ids->get_sample_info(get_file().get(), m_next_sample_to_be_processed);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    Result<std::string> convResult = vector_to_string(readResult.value);
+    if (convResult.error) {
+      return convResult.error;
+    }
+
+    image->set_gimi_sample_content_id(convResult.value);
+  }
+#endif
+
+  if (m_aux_reader_tai_timestamps) {
+    auto readResult = m_aux_reader_tai_timestamps->get_sample_info(get_file().get(), m_next_sample_to_be_processed);
+    if (readResult.error) {
+      return readResult.error;
+    }
+
+    auto resultTai = Box_itai::decode_tai_from_vector(readResult.value);
+    if (resultTai.error) {
+      return resultTai.error;
+    }
+
+    image->set_tai_timestamp(&resultTai.value);
+  }
+
+  m_next_sample_to_be_processed++;
+
+  return image;
+}
+
+
+Error Track_Visual::encode_image(std::shared_ptr<HeifPixelImage> image,
+                                 struct heif_encoder* h_encoder,
+                                 const struct heif_encoding_options& in_options,
+                                 heif_image_input_class input_class)
+{
+  if (image->get_width() > 0xFFFF ||
+      image->get_height() > 0xFFFF) {
+    return {heif_error_Invalid_input,
+            heif_suberror_Unspecified,
+            "Input image resolution too high"};
+  }
+
+  // === generate compressed image bitstream
+
+  // generate new chunk for first image or when compression formats don't match
+
+  bool add_sample_description = false;
+
+  if (m_chunks.empty() || m_chunks.back()->get_compression_format() != h_encoder->plugin->compression_format) {
+    add_chunk(h_encoder->plugin->compression_format);
+    add_sample_description = true;
+  }
+
+  // --- check whether we have to convert the image color space
+
+  // The reason for doing the color conversion here is that the input might be an RGBA image and the color conversion
+  // will extract the alpha plane anyway. We can reuse that plane below instead of having to do a new conversion.
+
+  heif_encoding_options options = in_options;
+
+  auto encoder = m_chunks.back()->get_encoder();
+
+  if (const auto* nclx = encoder->get_forced_output_nclx()) {
+    options.output_nclx_profile = const_cast<heif_color_profile_nclx*>(nclx);
+  }
+
+  Result<std::shared_ptr<HeifPixelImage>> srcImageResult = encoder->convert_colorspace_for_encoding(image,
+                                                                                                    h_encoder,
+                                                                                                    options,
+                                                                                                    m_heif_context->get_security_limits());
+  if (srcImageResult.error) {
+    return srcImageResult.error;
+  }
+
+  std::shared_ptr<HeifPixelImage> colorConvertedImage = srcImageResult.value;
+
+  // --- encode image
+
+  Result<Encoder::CodedImageData> encodeResult = encoder->encode(colorConvertedImage, h_encoder, options, input_class);
+  if (encodeResult.error) {
+    return encodeResult.error;
+  }
+
+  const Encoder::CodedImageData& data = encodeResult.value;
+
+
+  // --- generate SampleDescriptionBox
+
+  if (add_sample_description) {
+    auto sample_description_box = encoder->get_sample_description_box(data);
+    VisualSampleEntry& visualSampleEntry = sample_description_box->get_VisualSampleEntry();
+    visualSampleEntry.width = static_cast<uint16_t>(colorConvertedImage->get_width());
+    visualSampleEntry.height = static_cast<uint16_t>(colorConvertedImage->get_height());
+
+    auto ccst = std::make_shared<Box_ccst>();
+    ccst->set_coding_constraints(data.codingConstraints);
+    sample_description_box->append_child_box(ccst);
+
+    set_sample_description_box(sample_description_box);
+  }
+
+  Error err = write_sample_data(data.bitstream,
+                                colorConvertedImage->get_sample_duration(),
+                                data.is_sync_frame,
+                                image->get_tai_timestamp(),
+#if HEIF_ENABLE_EXPERIMENTAL_FEATURES
+                                image->has_gimi_sample_content_id() ? image->get_gimi_sample_content_id() : std::string{});
+#else
+  std::string{});
+#endif
+
+  if (err) {
+    return err;
+  }
+
+  return Error::Ok;
+}
+
+
+heif_brand2 Track_Visual::get_compatible_brand() const
+{
+  if (m_stsd->get_num_sample_entries() == 0) {
+    return 0; // TODO: error ? Or can we assume at this point that there is at least one sample entry?
+  }
+
+  auto sampleEntry = m_stsd->get_sample_entry(0);
+
+  uint32_t sample_entry_type = sampleEntry->get_short_type();
+  switch (sample_entry_type) {
+    case fourcc("hvc1"): {
+      auto hvcC = sampleEntry->get_child_box<Box_hvcC>();
+      if (!hvcC) { return 0; }
+
+      const auto& config = hvcC->get_configuration();
+      if (config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_Main) ||
+          config.is_profile_compatibile(HEVCDecoderConfigurationRecord::Profile_MainStillPicture)) {
+        return heif_brand2_hevc;
+      }
+      else {
+        return heif_brand2_hevx;
+      }
+    }
+
+    case fourcc("avc1"):
+      return heif_brand2_avcs;
+
+    case fourcc("av01"):
+      return heif_brand2_avis;
+
+    case fourcc("j2ki"):
+      return heif_brand2_j2is;
+
+    case fourcc("mjpg"):
+      return heif_brand2_jpgs;
+
+    case fourcc("vvc1"):
+      return heif_brand2_vvis;
+
+    default:
+      return 0;
+  }
+}
diff -pruN 1.19.8-1/libheif/sequences/track_visual.h 1.20.1-1/libheif/sequences/track_visual.h
--- 1.19.8-1/libheif/sequences/track_visual.h	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/libheif/sequences/track_visual.h	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,61 @@
+/*
+ * HEIF image base codec.
+ * Copyright (c) 2025 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_TRACK_VISUAL_H
+#define LIBHEIF_TRACK_VISUAL_H
+
+#include "track.h"
+#include <string>
+#include <memory>
+#include <vector>
+
+
+class Track_Visual : public Track {
+public:
+  //Track(HeifContext* ctx);
+
+  Track_Visual(HeifContext* ctx, uint32_t track_id, uint16_t width, uint16_t height,
+               const TrackOptions* options, uint32_t handler_type);
+
+  Track_Visual(HeifContext* ctx, const std::shared_ptr<Box_trak>&); // when reading the file
+
+  ~Track_Visual() override = default;
+
+  uint16_t get_width() const { return m_width; }
+
+  uint16_t get_height() const { return m_height; }
+
+  Result<std::shared_ptr<HeifPixelImage>> decode_next_image_sample(const struct heif_decoding_options& options);
+
+  Error encode_image(std::shared_ptr<HeifPixelImage> image,
+                     struct heif_encoder* encoder,
+                     const struct heif_encoding_options& options,
+                     heif_image_input_class image_class);
+
+  heif_brand2 get_compatible_brand() const;
+
+private:
+  uint16_t m_width = 0;
+  uint16_t m_height = 0;
+};
+
+
+
+#endif //LIBHEIF_TRACK_VISUAL_H
diff -pruN 1.19.8-1/post.js 1.20.1-1/post.js
--- 1.19.8-1/post.js	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/post.js	2025-07-02 13:05:31.000000000 +0000
@@ -50,7 +50,7 @@ HeifImage.prototype.get_height = functio
 };
 
 HeifImage.prototype.is_primary = function() {
-    return !!heif_image_handle_is_primary_image(this.handle);
+    return !!Module.heif_image_handle_is_primary_image(this.handle);
 }
 
 HeifImage.prototype.display = function(image_data, callback) {
diff -pruN 1.19.8-1/scripts/check-emscripten-enums.sh 1.20.1-1/scripts/check-emscripten-enums.sh
--- 1.19.8-1/scripts/check-emscripten-enums.sh	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/scripts/check-emscripten-enums.sh	2025-07-02 13:05:31.000000000 +0000
@@ -29,16 +29,18 @@ DEFINE_TYPES="
     heif_channel_
     "
 
+HEADERS="libheif/api/libheif/heif_error.h libheif/api/libheif/heif_image.h libheif/api/libheif/heif_context.h libheif/api/libheif/heif_color.h"
+
 API_DEFINES=""
 for type in $DEFINE_TYPES; do
-    DEFINES=$(grep "^[ \t]*$type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g')
+    DEFINES=$(grep -h "^[ \t]*$type" $HEADERS | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g')
     if [ -z "$API_DEFINES" ]; then
         API_DEFINES="$DEFINES"
     else
         API_DEFINES="$API_DEFINES
 $DEFINES"
     fi
-    ALIASES=$(grep "^[ \t]*#define $type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g')
+    ALIASES=$(grep -h "^[ \t]*#define $type" $HEADERS | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g')
     if [ ! -z "$ALIASES" ]; then
         API_DEFINES="$API_DEFINES
 $ALIASES"
diff -pruN 1.19.8-1/scripts/check-go-enums.sh 1.20.1-1/scripts/check-go-enums.sh
--- 1.19.8-1/scripts/check-go-enums.sh	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/scripts/check-go-enums.sh	2025-07-02 13:05:31.000000000 +0000
@@ -29,16 +29,18 @@ DEFINE_TYPES="
     heif_channel_
     "
 
+HEADERS="libheif/api/libheif/heif_error.h libheif/api/libheif/heif_image.h libheif/api/libheif/heif_context.h libheif/api/libheif/heif_color.h"
+
 API_DEFINES=""
 for type in $DEFINE_TYPES; do
-    DEFINES=$(grep "^[ \t]*$type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g')
+    DEFINES=$(grep -h "^[ \t]*$type" $HEADERS | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g')
     if [ -z "$API_DEFINES" ]; then
         API_DEFINES="$DEFINES"
     else
         API_DEFINES="$API_DEFINES
 $DEFINES"
     fi
-    ALIASES=$(grep "^[ \t]*#define $type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g')
+    ALIASES=$(grep -h "^[ \t]*#define $type" $HEADERS | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g')
     if [ ! -z "$ALIASES" ]; then
         API_DEFINES="$API_DEFINES
 $ALIASES"
diff -pruN 1.19.8-1/scripts/install-ci-linux.sh 1.20.1-1/scripts/install-ci-linux.sh
--- 1.19.8-1/scripts/install-ci-linux.sh	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/scripts/install-ci-linux.sh	2025-07-02 13:05:31.000000000 +0000
@@ -22,7 +22,7 @@ set -e
 
 ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
 
-INSTALL_PACKAGES=
+INSTALL_PACKAGES="gdb "
 REMOVE_PACKAGES=
 BUILD_ROOT=$ROOT/..
 UPDATE_APT=
diff -pruN 1.19.8-1/scripts/run-ci.sh 1.20.1-1/scripts/run-ci.sh
--- 1.19.8-1/scripts/run-ci.sh	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/scripts/run-ci.sh	2025-07-02 13:05:31.000000000 +0000
@@ -153,6 +153,9 @@ CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_IN
 # turn on warnings-as-errors
 CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_COMPILE_WARNING_AS_ERROR=1"
 
+# compilation mode
+CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_BUILD_TYPE=Release"
+
 
 if [ ! -z "$FUZZER" ] && [ "$CURRENT_OS" = "linux" ]; then
     export ASAN_SYMBOLIZER="$BUILD_ROOT/clang/bin/llvm-symbolizer"
@@ -174,6 +177,7 @@ if [ -z "$EMSCRIPTEN_VERSION" ] && [ -z
         ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} --list-decoders
 
         echo "Dumping information of sample file ..."
+        #${BIN_WRAPPER} gdb -batch -ex "run" -ex "bt" --args ./examples/heif-info${BIN_SUFFIX} --dump-boxes examples/example.heic
         ${BIN_WRAPPER} ./examples/heif-info${BIN_SUFFIX} --dump-boxes examples/example.heic
         if [ ! -z "$WITH_GRAPHICS" ] && [ ! -z "$WITH_HEIF_DECODER" ]; then
             echo "Converting sample HEIF file to JPEG ..."
diff -pruN 1.19.8-1/tests/CMakeLists.txt 1.20.1-1/tests/CMakeLists.txt
--- 1.19.8-1/tests/CMakeLists.txt	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/tests/CMakeLists.txt	2025-07-02 13:05:31.000000000 +0000
@@ -58,6 +58,7 @@ endif()
 add_libheif_test(encode)
 add_libheif_test(extended_type)
 add_libheif_test(region)
+add_libheif_test(tai)
 
 if (WITH_OPENJPH_ENCODER AND SUPPORTS_J2K_HT_ENCODING)
     add_libheif_test(encode_htj2k)
diff -pruN 1.19.8-1/tests/conversion.cc 1.20.1-1/tests/conversion.cc
--- 1.19.8-1/tests/conversion.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/tests/conversion.cc	2025-07-02 13:05:31.000000000 +0000
@@ -271,6 +271,7 @@ static bool NclxMatches(heif_colorspace
 void TestConversion(const std::string& test_name, const ColorState& input_state,
                     const ColorState& target_state,
                     const heif_color_conversion_options& options,
+                    const heif_color_conversion_options_ext& options_ext,
                     bool require_supported = true) {
   INFO(test_name);
   INFO("downsampling=" << options.preferred_chroma_downsampling_algorithm
@@ -280,7 +281,7 @@ void TestConversion(const std::string& t
                        << (bool)options.only_use_preferred_chroma_algorithm);
 
   ColorConversionPipeline pipeline;
-  bool supported = pipeline.construct_pipeline(input_state, target_state, options);
+  bool supported = pipeline.construct_pipeline(input_state, target_state, options, options_ext);
   if (require_supported) REQUIRE(supported);
   if (!supported) return;
   INFO("conversion pipeline: " << pipeline.debug_dump_pipeline());
@@ -317,7 +318,7 @@ void TestConversion(const std::string& t
 
   // Convert back in the other direction (if supported).
   ColorConversionPipeline reverse_pipeline;
-  if (reverse_pipeline.construct_pipeline(target_state, input_state, options)) {
+  if (reverse_pipeline.construct_pipeline(target_state, input_state, options, options_ext)) {
     INFO("reverse pipeline: " << reverse_pipeline.debug_dump_pipeline());
     auto recovered_image_result =reverse_pipeline.convert_image(out_image, heif_get_disabled_security_limits());
     REQUIRE(recovered_image_result);
@@ -362,7 +363,8 @@ void TestConversion(const std::string& t
 void TestFailingConversion(const std::string& test_name,
                            const ColorState& input_state,
                            const ColorState& target_state,
-                           const heif_color_conversion_options& options = {}) {
+                           const heif_color_conversion_options& options,
+                           const heif_color_conversion_options_ext& options_ext) {
   INFO(test_name);
   INFO("downsampling=" << options.preferred_chroma_downsampling_algorithm
                        << " upsampling="
@@ -371,7 +373,7 @@ void TestFailingConversion(const std::st
                        << (bool)options.only_use_preferred_chroma_algorithm);
   ColorConversionPipeline pipeline;
   bool construct_pipeline_res =
-      pipeline.construct_pipeline(input_state, target_state, options);
+      pipeline.construct_pipeline(input_state, target_state, options, options_ext);
   INFO("conversion pipeline: " << pipeline.debug_dump_pipeline());
   REQUIRE_FALSE(construct_pipeline_res);
 }
@@ -485,6 +487,10 @@ TEST_CASE("All conversions", "[heif_imag
       .preferred_chroma_upsampling_algorithm = upsampling,
       .only_use_preferred_chroma_algorithm = only_use_preferred_chroma_algorithm};
 
+  heif_color_conversion_options_ext options_ext = {
+      .alpha_composition_mode = heif_alpha_composition_mode_none
+  };
+
   // Test all source and destination state combinations.
   ColorState src_state = GENERATE(
       from_range(GetAllColorStates(GetSupportedMatrices())));
@@ -510,7 +516,7 @@ TEST_CASE("All conversions", "[heif_imag
 
   std::ostringstream os;
   os << "from: " << src_state << "\nto:   " << dst_state;
-  TestConversion(os.str(), src_state, dst_state, options, require_supported);
+  TestConversion(os.str(), src_state, dst_state, options, options_ext, require_supported);
 }
 
 TEST_CASE("Unsupported matrices", "[heif_image]") {
@@ -531,6 +537,10 @@ TEST_CASE("Unsupported matrices", "[heif
       .preferred_chroma_upsampling_algorithm = upsampling,
       .only_use_preferred_chroma_algorithm = only_use_preferred_chroma_algorithm};
 
+  heif_color_conversion_options_ext options_ext = {
+      .alpha_composition_mode = heif_alpha_composition_mode_none
+  };
+
   ColorState src_state =
       GENERATE(from_range(GetAllColorStates(GetUnsupportedMatrices())));
   ColorState dst_state =
@@ -547,7 +557,7 @@ TEST_CASE("Unsupported matrices", "[heif
 
   std::ostringstream os;
   os << "from: " << src_state << "\nto:   " << dst_state;
-  TestFailingConversion(os.str(), src_state, dst_state, options);
+  TestFailingConversion(os.str(), src_state, dst_state, options, options_ext);
 }
 
 TEST_CASE("Sharp yuv conversion", "[heif_image]") {
@@ -557,53 +567,57 @@ TEST_CASE("Sharp yuv conversion", "[heif
       .preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear,
       .only_use_preferred_chroma_algorithm = true};
 
+  heif_color_conversion_options_ext options_ext = {
+      .alpha_composition_mode = heif_alpha_composition_mode_none
+  };
+
 #ifdef HAVE_LIBSHARPYUV
   TestConversion("### interleaved RGBA -> YCbCr 420 with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_interleaved_RGBA, true, 8},
                  {heif_colorspace_YCbCr, heif_chroma_420, true, 8},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### interleaved RGB 10bit -> YCbCr 420 10bit with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_interleaved_RGB, false, 8},
                  {heif_colorspace_YCbCr, heif_chroma_420, false, 8},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
 
   TestConversion("### interleaved RGBA 12bit big endian -> YCbCr 420 12bit with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_interleaved_RRGGBBAA_BE, true, 12},
                  {heif_colorspace_YCbCr, heif_chroma_420, true, 12},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### interleaved RGBA 12bit little endian -> YCbCr 420 12bit with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_interleaved_RRGGBBAA_LE, true, 12},
                  {heif_colorspace_YCbCr, heif_chroma_420, true, 12},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### planar RGB -> YCbCr 420 with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_444, false, 8},
                  {heif_colorspace_YCbCr, heif_chroma_420, false, 8},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### planar RGBA -> YCbCr 420 with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_444, true, 8},
                  {heif_colorspace_YCbCr, heif_chroma_420, true, 8},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### planar RGB 10bit -> YCbCr 420 10bit with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_444, false, 10},
                  {heif_colorspace_YCbCr, heif_chroma_420, false, 10},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
   TestConversion("### planar RGBA 10bit -> YCbCr 420 10bit with sharp yuv",
                  {heif_colorspace_RGB, heif_chroma_444, true, 10},
                  {heif_colorspace_YCbCr, heif_chroma_420, true, 10},
-                 sharp_yuv_options);
+                 sharp_yuv_options, options_ext);
 #else
   // Should fail if libsharpyuv is not compiled in.
   TestFailingConversion(
       "### interleaved RGBA -> YCbCr 420 with sharp yuv NOT COMPILED IN",
       {heif_colorspace_RGB, heif_chroma_interleaved_RGBA, true, 8},
-      {heif_colorspace_YCbCr, heif_chroma_420, false, 8}, sharp_yuv_options);
+      {heif_colorspace_YCbCr, heif_chroma_420, false, 8}, sharp_yuv_options, options_ext);
   WARN("Tests built without sharp yuv");
 #endif
 
   TestFailingConversion(
       "### interleaved RGBA -> YCbCr 422 with sharp yuv (not supported!)",
       {heif_colorspace_RGB, heif_chroma_interleaved_RGBA, true, 8},
-      {heif_colorspace_YCbCr, heif_chroma_422, false, 8}, sharp_yuv_options);
+      {heif_colorspace_YCbCr, heif_chroma_422, false, 8}, sharp_yuv_options, options_ext);
 }
 
 
@@ -661,7 +675,7 @@ TEST_CASE("Bilinear upsampling", "[heif_
              {255, 200,
               50, 0});
 
-  auto conversionResult = convert_colorspace(img, heif_colorspace_YCbCr, heif_chroma_444, nullptr, 8, options, heif_get_disabled_security_limits());
+  auto conversionResult = convert_colorspace(img, heif_colorspace_YCbCr, heif_chroma_444, nullptr, 8, options, nullptr, heif_get_disabled_security_limits());
   REQUIRE(conversionResult);
   std::shared_ptr<HeifPixelImage> out = *conversionResult;
 
@@ -714,7 +728,7 @@ TEST_CASE("RGB 5-6-5 to RGB")
     }
   }
 
-  auto conversionResult = convert_colorspace(img, heif_colorspace_RGB, heif_chroma_444, nullptr, 8, options, heif_get_disabled_security_limits());
+  auto conversionResult = convert_colorspace(img, heif_colorspace_RGB, heif_chroma_444, nullptr, 8, options, nullptr, heif_get_disabled_security_limits());
   REQUIRE(conversionResult);
   std::shared_ptr<HeifPixelImage> out = *conversionResult;
 
diff -pruN 1.19.8-1/tests/mini_box.cc 1.20.1-1/tests/mini_box.cc
--- 1.19.8-1/tests/mini_box.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/tests/mini_box.cc	2025-07-02 13:05:31.000000000 +0000
@@ -71,7 +71,7 @@ TEST_CASE("mini")
   REQUIRE(ftyp->list_brands().size() == 0);
   Indent indent;
   std::string dumpResult = box->dump(indent);
-  REQUIRE(dumpResult == "Box: ftyp -----\n"
+  REQUIRE(dumpResult == "Box: ftyp ----- (File Type)\n"
                         "size: 16   (header size: 8)\n"
                         "major brand: mif3\n"
                         "minor version: avif\n"
diff -pruN 1.19.8-1/tests/tai.cc 1.20.1-1/tests/tai.cc
--- 1.19.8-1/tests/tai.cc	1970-01-01 00:00:00.000000000 +0000
+++ 1.20.1-1/tests/tai.cc	2025-07-02 13:05:31.000000000 +0000
@@ -0,0 +1,137 @@
+/*
+  libheif unit tests
+
+  MIT License
+
+  Copyright (c) 2025 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 "catch_amalgamated.hpp"
+#include "test_utils.h"
+#include <libheif/heif.h>
+#include <libheif/heif_tai_timestamps.h>
+
+TEST_CASE( "image-tai" )
+{
+  heif_error err{};
+
+  std::string filename = get_tests_output_file_path("tai-1.heic");
+
+  heif_image* img = createImage_RGB_planar();
+  heif_encoder* enc = get_encoder_or_skip_test(heif_compression_HEVC);
+  heif_context* ctx = heif_context_alloc();
+
+  heif_image_handle* handle;
+  err = heif_context_encode_image(ctx, img, enc, nullptr, &handle);
+  REQUIRE(err.code == heif_error_Ok);
+  heif_item_id itemId = heif_image_handle_get_item_id(handle);
+
+  // add TAI clock info
+
+  heif_tai_clock_info* clock_info = heif_tai_clock_info_alloc();
+  clock_info->clock_resolution = 1000;
+  clock_info->clock_drift_rate = 123;
+  clock_info->clock_type = heif_tai_clock_info_clock_type_synchronized_to_atomic_source;
+  clock_info->time_uncertainty = 999;
+
+  err = heif_item_set_property_tai_clock_info(ctx, itemId, clock_info, nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  // check that adding a second timestamp leads to an error
+  err = heif_item_set_property_tai_clock_info(ctx, itemId, clock_info, nullptr);
+  REQUIRE(err.code != heif_error_Ok);
+
+  heif_tai_clock_info_release(clock_info);
+
+  // add TAI timestamp
+
+  heif_tai_timestamp_packet* timestamp = heif_tai_timestamp_packet_alloc();
+  timestamp->tai_timestamp = 1234567890;
+  timestamp->synchronization_state = 1;
+  timestamp->timestamp_generation_failure = 0;
+  timestamp->timestamp_is_modified = 0;
+
+  err = heif_item_set_property_tai_timestamp(ctx, itemId, timestamp, nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  // check that adding a second timestamp leads to an error
+  err = heif_item_set_property_tai_timestamp(ctx, itemId, timestamp, nullptr);
+  REQUIRE(err.code != heif_error_Ok);
+
+  heif_tai_timestamp_packet_release(timestamp);
+
+  err = heif_context_write_to_file(ctx, filename.c_str());
+  REQUIRE(err.code == heif_error_Ok);
+
+  heif_image_handle_release(handle);
+  heif_context_free(ctx);
+  heif_image_release(img);
+
+
+  // --- read file
+
+  ctx = heif_context_alloc();
+  err = heif_context_read_from_file(ctx, filename.c_str(), nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  err = heif_context_get_primary_image_handle(ctx, &handle);
+  REQUIRE(err.code == heif_error_Ok);
+
+  itemId = heif_image_handle_get_item_id(handle);
+
+  clock_info = nullptr; // make sure that we are not accidentally using old data
+  err = heif_item_get_property_tai_clock_info(ctx, itemId, &clock_info);
+  REQUIRE(err.code == heif_error_Ok);
+  REQUIRE(clock_info != nullptr);
+
+  timestamp = nullptr; // make sure that we are not accidentally using old data
+  err = heif_item_get_property_tai_timestamp(ctx, itemId, &timestamp);
+  REQUIRE(err.code == heif_error_Ok);
+  REQUIRE(timestamp != nullptr);
+
+  REQUIRE(clock_info->clock_resolution == 1000);
+  REQUIRE(clock_info->clock_drift_rate == 123);
+  REQUIRE(clock_info->clock_type == heif_tai_clock_info_clock_type_synchronized_to_atomic_source);
+  REQUIRE(clock_info->time_uncertainty == 999);
+  heif_tai_clock_info_release(clock_info);
+
+  REQUIRE(timestamp->tai_timestamp == 1234567890);
+  REQUIRE(timestamp->synchronization_state == 1);
+  REQUIRE(timestamp->timestamp_generation_failure == 0);
+  REQUIRE(timestamp->timestamp_is_modified == 0);
+  heif_tai_timestamp_packet_release(timestamp);
+
+  // check whether we can get the timestamp also from the decoded image
+
+  err = heif_decode_image(handle, &img, heif_colorspace_undefined, heif_chroma_undefined, nullptr);
+  REQUIRE(err.code == heif_error_Ok);
+
+  timestamp = nullptr; // make sure that we are not accidentally using old data
+  err = heif_image_get_tai_timestamp(img, &timestamp);
+  REQUIRE(err.code == heif_error_Ok);
+  REQUIRE(timestamp != nullptr);
+
+  REQUIRE(timestamp->tai_timestamp == 1234567890);
+  REQUIRE(timestamp->synchronization_state == 1);
+  REQUIRE(timestamp->timestamp_generation_failure == 0);
+  REQUIRE(timestamp->timestamp_is_modified == 0);
+  heif_tai_timestamp_packet_release(timestamp);
+}
\ No newline at end of file
diff -pruN 1.19.8-1/tests/test_utils.cc 1.20.1-1/tests/test_utils.cc
--- 1.19.8-1/tests/test_utils.cc	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/tests/test_utils.cc	2025-07-02 13:05:31.000000000 +0000
@@ -30,6 +30,7 @@
 #include <cstring>
 #include "catch_amalgamated.hpp"
 
+
 struct heif_context * get_context_for_test_file(std::string filename)
 {
   return get_context_for_local_file(tests_data_directory + "/" + filename);
@@ -194,3 +195,27 @@ heif_encoder* get_encoder_or_skip_test(h
 
   return encoder;
 }
+
+
+fs::path get_tests_output_dir()
+{
+  if (const char* env_p = std::getenv("LIBHEIF_TEST_OUTPUT_DIR")) {
+    return fs::path(env_p);
+  }
+
+  static const fs::path output_dir = fs::current_path() / "libheif_test_output";
+
+  if (!fs::exists(output_dir)) {
+    fs::create_directories(output_dir);
+  }
+
+  return output_dir;
+}
+
+
+std::string get_tests_output_file_path(const char* filename)
+{
+  fs::path dir = get_tests_output_dir();
+  dir /= filename;
+  return dir.string();
+}
diff -pruN 1.19.8-1/tests/test_utils.h 1.20.1-1/tests/test_utils.h
--- 1.19.8-1/tests/test_utils.h	2025-04-27 18:28:09.000000000 +0000
+++ 1.20.1-1/tests/test_utils.h	2025-07-02 13:05:31.000000000 +0000
@@ -27,6 +27,10 @@
 #include <string>
 #include "libheif/heif.h"
 
+#include <filesystem>
+
+namespace fs = std::filesystem;
+
 struct heif_context * get_context_for_test_file(std::string filename);
 struct heif_context * get_context_for_local_file(std::string filename);
 
@@ -42,4 +46,8 @@ struct heif_image * createImage_RGB_plan
 
 std::string get_path_for_heifio_test_file(std::string filename);
 
-heif_encoder* get_encoder_or_skip_test(heif_compression_format format);
\ No newline at end of file
+heif_encoder* get_encoder_or_skip_test(heif_compression_format format);
+
+fs::path get_tests_output_dir();
+
+std::string get_tests_output_file_path(const char* filename);
