Commit 8f5e2e77 for libheif

commit 8f5e2e77e44edab05163fd6dc42b1ebc0b55bd63
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Mon May 11 19:31:03 2026 +0200

    heif-dec: add option to control number of tile decoding threads (#1787)

diff --git a/examples/heif_dec.cc b/examples/heif_dec.cc
index 14cbaa3c..0fab4555 100644
--- a/examples/heif_dec.cc
+++ b/examples/heif_dec.cc
@@ -87,6 +87,8 @@ static void show_help(const char* argv0)

   std::string title = sstr.str();

+  int default_tile_threads = heif_context_get_max_decoding_threads(nullptr);
+
   std::cerr << title << "\n"
             << std::string(title.length() + 1, '-') << "\n"
             << "Usage: " << filename << " [options]  <input-image> [output-image]\n"
@@ -116,7 +118,8 @@ static void show_help(const char* argv0)
                "                                            support transparency. MODE must be one of: white, black, checkerboard.\n"
                "      --disable-limits           disable all security limits (do not use in production environment)\n"
                "      --codec-threads #          number of threads to use in the codec plugin (0 = default)\n"
-               "      --extract-mime-item TYPE   extract the MIME item with the given content type into a file (mime-item.data)\n";
+            << "      --tile-threads #           max number of tiles to decode in parallel (default = " << default_tile_threads << ")\n"
+            << "      --extract-mime-item TYPE   extract the MIME item with the given content type into a file (mime-item.data)\n";
 }


@@ -149,6 +152,7 @@ int option_disable_limits = 0;
 int option_sequence = 0;
 int option_ignore_editlist = 0;
 int option_num_codec_threads = 0;
+int option_num_tile_threads = -1;  // -1 means "do not override library default"
 std::string option_extract_mime_item_type;
 std::string output_filename;

@@ -159,6 +163,7 @@ std::string transparency_composition_mode = "checkerboard";
 #define OPTION_TRANSPARENCY_COMPOSITION_MODE 1001
 #define OPTION_CODEC_THREADS 1002
 #define OPTION_EXTRACT_MIME_ITEM 1003
+#define OPTION_TILE_THREADS 1004

 static option long_options[] = {
     {(char* const) "quality",          required_argument, 0,                        'q'},
@@ -182,6 +187,7 @@ static option long_options[] = {
     {(char* const) "disable-limits", no_argument, &option_disable_limits, 1},
     {(char* const) "ignore-editlist", no_argument, &option_ignore_editlist, 1},
     {(char* const) "codec-threads", required_argument, 0,                     OPTION_CODEC_THREADS},
+    {(char* const) "tile-threads",  required_argument, 0,                     OPTION_TILE_THREADS},
     {(char* const) "extract-mime-item", required_argument, 0, OPTION_EXTRACT_MIME_ITEM},
     {nullptr, no_argument, nullptr, 0}
 };
@@ -677,6 +683,9 @@ int main(int argc, char** argv)
       case OPTION_CODEC_THREADS:
         option_num_codec_threads = atoi(optarg);
         break;
+      case OPTION_TILE_THREADS:
+        option_num_tile_threads = atoi(optarg);
+        break;
       case OPTION_EXTRACT_MIME_ITEM:
         option_extract_mime_item_type = optarg;
         break;
@@ -797,6 +806,10 @@ int main(int argc, char** argv)
     heif_context_set_security_limits(ctx, heif_get_disabled_security_limits());
   }

+  if (option_num_tile_threads >= 0) {
+    heif_context_set_max_decoding_threads(ctx, option_num_tile_threads);
+  }
+
   ContextReleaser cr(ctx);
   heif_error err;
   err = heif_context_read_from_file(ctx, input_filename.c_str(), nullptr);
diff --git a/libheif/api/libheif/heif_decoding.cc b/libheif/api/libheif/heif_decoding.cc
index 23ffa9b4..575790b9 100644
--- a/libheif/api/libheif/heif_decoding.cc
+++ b/libheif/api/libheif/heif_decoding.cc
@@ -34,6 +34,15 @@ void heif_context_set_max_decoding_threads(heif_context* ctx, int max_threads)
 }


+int heif_context_get_max_decoding_threads(const heif_context* ctx)
+{
+  if (!ctx) {
+    return HeifContext::default_max_decoding_threads;
+  }
+  return ctx->context->get_max_decoding_threads();
+}
+
+
 int heif_have_decoder_for_format(heif_compression_format format)
 {
   auto plugin = get_decoder(format, nullptr);
diff --git a/libheif/api/libheif/heif_decoding.h b/libheif/api/libheif/heif_decoding.h
index e580b162..3078327d 100644
--- a/libheif/api/libheif/heif_decoding.h
+++ b/libheif/api/libheif/heif_decoding.h
@@ -39,6 +39,13 @@ extern "C" {
 LIBHEIF_API
 void heif_context_set_max_decoding_threads(heif_context* ctx, int max_threads);

+// Returns the current maximum number of background threads used for parallel tile decoding.
+// This reflects either the library default or the value last passed to
+// heif_context_set_max_decoding_threads().
+// If 'ctx' is NULL, the library default is returned.
+LIBHEIF_API
+int heif_context_get_max_decoding_threads(const heif_context* ctx);
+
 // 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.
diff --git a/libheif/context.h b/libheif/context.h
index 18c3aa4b..e773ce0b 100644
--- a/libheif/context.h
+++ b/libheif/context.h
@@ -69,6 +69,8 @@ public:

   ~HeifContext();

+  static constexpr int default_max_decoding_threads = 4;
+
   void set_max_decoding_threads(int max_threads) { m_max_decoding_threads = max_threads; }

   int get_max_decoding_threads() const { return m_max_decoding_threads; }
@@ -306,7 +308,7 @@ private:

   std::shared_ptr<HeifFile> m_heif_file;

-  int m_max_decoding_threads = 4;
+  int m_max_decoding_threads = default_max_decoding_threads;

   heif_security_limits m_limits;
   TotalMemoryTracker m_memory_tracker;