diff --git a/docs/src/content/docs/api-output.md b/docs/src/content/docs/api-output.md
index 357dada9..b6729c63 100644
--- a/docs/src/content/docs/api-output.md
+++ b/docs/src/content/docs/api-output.md
@@ -758,7 +758,7 @@ When using Windows ARM64, this feature requires a CPU with ARM64v8.4 or later.
| [options.effort] | number | 4 | CPU effort, between 0 (fastest) and 9 (slowest) |
| [options.chromaSubsampling] | string | "'4:4:4'" | set to '4:2:0' to use chroma subsampling |
| [options.bitdepth] | number | 8 | set bitdepth to 8, 10 or 12 bit |
-| [options.tune] | string | "'iq'" | tune output for a quality metric, one of 'iq' (default), 'ssim' or 'psnr' |
+| [options.tune] | string | "'iq'" | tune output for a quality metric, one of 'iq' (default), 'ssim' (default when lossless) or 'psnr' |
**Example**
```js
diff --git a/lib/output.js b/lib/output.js
index 7de53ed1..d0e1d3d5 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -1175,7 +1175,7 @@ function tiff (options) {
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
- * @param {string} [options.tune='iq'] - tune output for a quality metric, one of 'iq' (default), 'ssim' or 'psnr'
+ * @param {string} [options.tune='iq'] - tune output for a quality metric, one of 'iq' (default), 'ssim' (default when lossless) or 'psnr'
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@@ -1255,7 +1255,11 @@ function heif (options) {
}
if (is.defined(options.tune)) {
if (is.string(options.tune) && is.inArray(options.tune, ['iq', 'ssim', 'psnr'])) {
- this.options.heifTune = options.tune;
+ if (this.options.heifLossless && options.tune === 'iq') {
+ this.options.heifTune = 'ssim';
+ } else {
+ this.options.heifTune = options.tune;
+ }
} else {
throw is.invalidParameterError('tune', 'one of: psnr, ssim, iq', options.tune);
}
diff --git a/test/unit/avif.js b/test/unit/avif.js
index 0a47b4e0..aa08b438 100644
--- a/test/unit/avif.js
+++ b/test/unit/avif.js
@@ -7,7 +7,7 @@ const { describe, it } = require('node:test');
const assert = require('node:assert');
const sharp = require('../../');
-const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures');
+const { inputAvif, inputJpg, inputGifAnimated, inputPng } = require('../fixtures');
describe('AVIF', () => {
it('called without options does not throw an error', () => {
@@ -74,6 +74,35 @@ describe('AVIF', () => {
});
});
+ it('can convert PNG to lossless AVIF', async () => {
+ const data = await sharp(inputPng)
+ .resize(32)
+ .avif({ lossless: true, effort: 0 })
+ .toBuffer();
+ const { size, ...metadata } = await sharp(data).metadata();
+ void size;
+ assert.deepStrictEqual(metadata, {
+ autoOrient: {
+ height: 24,
+ width: 32
+ },
+ channels: 3,
+ compression: 'av1',
+ depth: 'uchar',
+ format: 'heif',
+ hasAlpha: false,
+ hasProfile: false,
+ height: 24,
+ isProgressive: false,
+ isPalette: false,
+ bitsPerSample: 8,
+ pagePrimary: 0,
+ pages: 1,
+ space: 'srgb',
+ width: 32
+ });
+ });
+
it('can passthrough AVIF', async () => {
const data = await sharp(inputAvif)
.resize(32)