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