Commit d3efe0ca for libheif
commit d3efe0caa36e51894f75a628bf1d1677493c8a4f
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun Sep 6 14:21:35 2026 +0200
aom: do not auto-select tune=iq for lossless encoding
libaom's handle_tuning() enables chroma delta-q for AOM_TUNE_IQ, and it then
refuses to combine that with lossless coding. Since we auto-select AOM_TUNE_IQ
for still images, lossless encoding failed with the confusing message
"Only --enable_chroma_deltaq=0 can be used with --lossless=1".
Keep AOM_TUNE_SSIM for lossless images, which is what was used before the IQ
auto-selection was added. Images encoded with an identity matrix were not
affected because those already skip the IQ tune. When the user explicitly asks
for both tune=iq and lossless, report which combination is unsupported instead
of passing the request on to libaom.
diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc
index 800576d2..4cad0b2e 100644
--- a/libheif/plugins/encoder_aom.cc
+++ b/libheif/plugins/encoder_aom.cc
@@ -199,6 +199,13 @@ static const char* const kParam_tune_valid_values[] = {
};
#if defined(AOM_HAVE_TUNE_IQ)
+static heif_error heif_error_lossless_with_tune_iq = {
+ heif_error_Usage_error,
+ heif_suberror_Invalid_parameter_value,
+ "AOM 'tune=iq' cannot be combined with lossless encoding because libaom enables "
+ "chroma delta-q for this tune. Use 'tune=ssim' or 'tune=psnr' for lossless images."
+};
+
// This table has been copied from libavif/src/codec_aom.c
// Quality (q) to quantizer (qp) formula for tune=iq (Image Quality), expressed as a look-up table for more clarity.
@@ -1071,6 +1078,9 @@ static heif_error aom_start_sequence_encoding_intern(void* encoder_raw, const he
options->version >= 3 &&
options->content_kind == heif_sequence_content_kind_video);
+ bool is_lossless = (encoder->lossless ||
+ (input_class == heif_image_input_class_alpha && encoder->lossless_alpha));
+
aom_tune_metric effective_tune = encoder->tune;
if (encoder->tune_auto) {
if (tune_as_video) {
@@ -1097,7 +1107,9 @@ static heif_error aom_start_sequence_encoding_intern(void* encoder_raw, const he
int aom_version = aom_codec_version();
bool iq_supports_inter = (aom_version >= aom_version_3_14_0);
- if (!is_identity_matrix &&
+ // libaom turns on chroma delta-q for AOM_TUNE_IQ and then refuses to combine
+ // that with lossless coding, so keep AOM_TUNE_SSIM for lossless images.
+ if (!is_identity_matrix && !is_lossless &&
(cfg.g_usage == AOM_USAGE_ALL_INTRA || iq_supports_inter) &&
aom_version >= aom_version_3_13_0) {
effective_tune = AOM_TUNE_IQ;
@@ -1106,6 +1118,12 @@ static heif_error aom_start_sequence_encoding_intern(void* encoder_raw, const he
}
}
+#if defined(AOM_HAVE_TUNE_IQ)
+ if (is_lossless && effective_tune == AOM_TUNE_IQ) {
+ return heif_error_lossless_with_tune_iq;
+ }
+#endif
+
int cq_level;
#if defined(AOM_HAVE_TUNE_IQ)
if (effective_tune == AOM_TUNE_IQ) {
@@ -1187,7 +1205,7 @@ static heif_error aom_start_sequence_encoding_intern(void* encoder_raw, const he
aom_error = aom_codec_control(&codec, AOME_SET_TUNING, effective_tune); CHECK_ERROR;
- if (encoder->lossless || (input_class == heif_image_input_class_alpha && encoder->lossless_alpha)) {
+ if (is_lossless) {
aom_error = aom_codec_control(&codec, AV1E_SET_LOSSLESS, 1); CHECK_ERROR;
}