Commit 7cd3669b for libheif

commit 7cd3669b8267034e990f7edbc5694975f0f5a779
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sat Sep 19 15:43:49 2026 +0200

    Return the 'pclr' entry count as uint16_t

    NE is a 16-bit field (1 to 1024 per ISO/IEC 15444-1 Table I.12), but
    get_num_entries() returned uint8_t, so the writer emitted a truncated
    count for palettes with more than 255 entries. Also drop the pointless
    const on the by-value return types.

    Add a 300-entry write and parse round trip.

diff --git a/libheif/codecs/jpeg2000_boxes.h b/libheif/codecs/jpeg2000_boxes.h
index 5d1e61c5..fd165ae9 100644
--- a/libheif/codecs/jpeg2000_boxes.h
+++ b/libheif/codecs/jpeg2000_boxes.h
@@ -222,14 +222,15 @@ public:
         return m_bitDepths;
     }

-    const uint8_t get_num_entries() const
+    // NE is a 16-bit field (1 to 1024 per ISO/IEC 15444-1 Table I.12).
+    uint16_t get_num_entries() const
     {
-        return (uint8_t)(m_entries.size());
+        return static_cast<uint16_t>(m_entries.size());
     }

-    const uint8_t get_num_columns() const
+    uint8_t get_num_columns() const
     {
-        return (uint8_t)(m_bitDepths.size());
+        return static_cast<uint8_t>(m_bitDepths.size());
     }

     void add_entry(const PaletteEntry& entry)
diff --git a/tests/jpeg2000.cc b/tests/jpeg2000.cc
index ead451e6..143675f5 100644
--- a/tests/jpeg2000.cc
+++ b/tests/jpeg2000.cc
@@ -863,6 +863,36 @@ TEST_CASE("pclr write and parse round trip")
 }


+TEST_CASE("pclr more than 255 entries round trip")
+{
+  // NE is a 16-bit field; get_num_entries() used to return uint8_t, so the
+  // writer emitted a truncated count for palettes above 255 entries.
+  auto pclr = std::make_shared<Box_pclr>();
+  pclr->set_columns(1, 8);
+  for (int i = 0; i < 300; i++) {
+    Box_pclr::PaletteEntry entry;
+    entry.columns = {static_cast<uint16_t>(i & 0xff)};
+    pclr->add_entry(entry);
+  }
+  REQUIRE(pclr->get_num_entries() == 300);
+
+  StreamWriter writer;
+  REQUIRE(pclr->write(writer) == Error::Ok);
+  const std::vector<uint8_t> bytes = writer.get_data();
+  REQUIRE(bytes.size() == 8 + 2 + 1 + 1 + 300);
+  REQUIRE(bytes[8] == 0x01);  // NE = 0x012c
+  REQUIRE(bytes[9] == 0x2c);
+
+  std::shared_ptr<Box> box;
+  Error err = parse_box(bytes, heif_get_global_security_limits(), &box);
+  REQUIRE(err == Error::Ok);
+  auto parsed = std::dynamic_pointer_cast<Box_pclr>(box);
+  REQUIRE(parsed);
+  REQUIRE(parsed->get_num_entries() == 300);
+  REQUIRE(parsed->get_entries()[299].columns == std::vector<uint16_t>{299 & 0xff});
+}
+
+
 TEST_CASE("pclr zero columns rejected")
 {
   // NE=65535, NPC=0: the 11-byte box from the advisory.