Commit 7867a80b for libheif

commit 7867a80b3f954d200e65723d81acfb33e0de897c
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Sun Aug 30 13:44:39 2026 +0200

    OpenJPEG encoder: do not rate-limit lossless output

    heif_encoder_set_lossless() only switched to the reversible 5/3 wavelet,
    but the rate allocation still used the quality-derived compression ratio
    (16:1 at the default quality of 70), so "lossless" codestreams were
    truncated and decoded with visible errors. A rate of 0 tells OpenJPEG to
    not limit the codestream size.

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01QAo5hPipqTKvaQDDpSEtxT

diff --git a/libheif/plugins/encoder_openjpeg.cc b/libheif/plugins/encoder_openjpeg.cc
index 3bbbe0b2..6a29c51c 100644
--- a/libheif/plugins/encoder_openjpeg.cc
+++ b/libheif/plugins/encoder_openjpeg.cc
@@ -370,7 +370,14 @@ static heif_error generate_codestream(opj_image_t* image, encoder_struct_opj* en

   encoder->parameters.cp_disto_alloc = 1;
   encoder->parameters.tcp_numlayers = 1;
-  encoder->parameters.tcp_rates[0] = (float)(1 + (100 - encoder->quality)/2);
+  if (encoder->parameters.irreversible == 0) {
+    // Lossless: the reversible wavelet alone is not sufficient, the rate allocation must not
+    // truncate the codestream either. A rate of 0 means "no rate limit" in OpenJPEG.
+    encoder->parameters.tcp_rates[0] = 0;
+  }
+  else {
+    encoder->parameters.tcp_rates[0] = (float)(1 + (100 - encoder->quality)/2);
+  }

 #if 0
   //Insert a human readable comment into the codestream