Standardise HEIF effort option name, deprecate speed

This commit is contained in:
Lovell Fuller 2021-11-24 19:54:04 +00:00
parent 2b1f5cbe07
commit e1ba2a7fd8
8 changed files with 50 additions and 29 deletions

View File

@ -282,7 +282,7 @@ Use these WebP options for output image.
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
* `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
* `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
* `options.effort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 6 (slowest) (optional, default `4`)
* `options.pageHeight` **[number][9]?** page height for animated output
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
* `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
@ -460,7 +460,7 @@ AVIF image sequences are not supported.
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
* `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest) (optional, default `5`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
<!---->
@ -487,7 +487,7 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
* `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
* `options.compression` **[string][2]** compression format: av1, hevc (optional, default `'av1'`)
* `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
* `options.speed` **[number][9]** CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest) (optional, default `5`)
* `options.effort` **[number][9]** CPU effort, between 0 (fastest) and 9 (slowest) (optional, default `4`)
* `options.chromaSubsampling` **[string][2]** set to '4:2:0' to use chroma subsampling (optional, default `'4:4:4'`)
<!---->

View File

@ -12,6 +12,8 @@ Requires libvips v8.12.0
* Standardise WebP `effort` option name, deprecate `reductionEffort`.
* Standardise HEIF `effort` option name, deprecate `speed`.
* Expose control over CPU effort for palette-based PNG output.
[#2541](https://github.com/lovell/sharp/issues/2541)

View File

@ -264,7 +264,7 @@ const Sharp = function (input, options) {
heifQuality: 50,
heifLossless: false,
heifCompression: 'av1',
heifSpeed: 5,
heifEffort: 4,
heifChromaSubsampling: '4:4:4',
rawDepth: 'uchar',
tileSize: 256,

View File

@ -457,7 +457,7 @@ function png (options) {
* @param {boolean} [options.lossless=false] - use lossless compression mode
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
* @param {number} [options.effort=4] - level of CPU effort to reduce file size, integer 0-6
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
* @param {number} [options.pageHeight] - page height for animated output
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
@ -806,7 +806,7 @@ function tiff (options) {
* @param {Object} [options] - output options
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest)
* @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
* @returns {Sharp}
* @throws {Error} Invalid options
@ -827,7 +827,7 @@ function avif (options) {
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {string} [options.compression='av1'] - compression format: av1, hevc
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 9 (fastest/largest)
* @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
* @returns {Sharp}
* @throws {Error} Invalid options
@ -855,9 +855,15 @@ function heif (options) {
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
}
}
if (is.defined(options.speed)) {
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
this.options.heifEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 0 and 9', options.effort);
}
} else if (is.defined(options.speed)) {
if (is.integer(options.speed) && is.inRange(options.speed, 0, 9)) {
this.options.heifSpeed = options.speed;
this.options.heifEffort = 9 - options.speed;
} else {
throw is.invalidParameterError('speed', 'integer between 0 and 9', options.speed);
}

View File

@ -899,7 +899,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("speed", baton->heifSpeed)
->set("effort", baton->heifEffort)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless)));
@ -1053,7 +1053,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("speed", baton->heifSpeed)
->set("effort", baton->heifEffort)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless));
@ -1512,7 +1512,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
sharp::AttrAsStr(options, "heifCompression").data()));
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
// Raw output

View File

@ -176,7 +176,7 @@ struct PipelineBaton {
double tiffYres;
int heifQuality;
VipsForeignHeifCompression heifCompression;
int heifSpeed;
int heifEffort;
std::string heifChromaSubsampling;
bool heifLossless;
VipsBandFormat rawDepth;
@ -314,7 +314,7 @@ struct PipelineBaton {
tiffYres(1.0),
heifQuality(50),
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
heifSpeed(5),
heifEffort(4),
heifChromaSubsampling("4:4:4"),
heifLossless(false),
rawDepth(VIPS_FORMAT_UCHAR),

View File

@ -17,10 +17,9 @@ describe('AVIF', () => {
.resize(32)
.jpeg()
.toBuffer();
const metadata = await sharp(data)
const { size, ...metadata } = await sharp(data)
.metadata();
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
assert.deepStrictEqual(metadata, {
channels: 3,
chromaSubsampling: '4:2:0',
density: 72,
@ -38,13 +37,13 @@ describe('AVIF', () => {
it('can convert JPEG to AVIF', async () => {
const data = await sharp(inputJpg)
.resize(32)
.avif()
.avif({ effort: 0 })
.toBuffer();
const metadata = await sharp(data)
const { size, ...metadata } = await sharp(data)
.metadata();
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
assert.deepStrictEqual(metadata, {
channels: 3,
compression: 'av1',
depth: 'uchar',
format: 'heif',
hasAlpha: false,
@ -63,11 +62,11 @@ describe('AVIF', () => {
const data = await sharp(inputAvif)
.resize(32)
.toBuffer();
const metadata = await sharp(data)
const { size, ...metadata } = await sharp(data)
.metadata();
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
assert.deepStrictEqual(metadata, {
channels: 3,
compression: 'av1',
depth: 'uchar',
format: 'heif',
hasAlpha: false,
@ -85,12 +84,11 @@ describe('AVIF', () => {
it('can convert animated GIF to non-animated AVIF', async () => {
const data = await sharp(inputGifAnimated, { animated: true })
.resize(10)
.avif({ speed: 8 })
.avif({ effort: 0 })
.toBuffer();
const metadata = await sharp(data)
const { size, ...metadata } = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
assert.deepStrictEqual(metadata, {
channels: 4,
compression: 'av1',
depth: 'uchar',

View File

@ -50,6 +50,21 @@ describe('HEIF', () => {
sharp().heif({ compression: 1 });
});
});
it('valid effort does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ speed: 6 });
});
});
it('out of range effort should throw an error', () => {
assert.throws(() => {
sharp().heif({ effort: 10 });
});
});
it('invalid effort should throw an error', () => {
assert.throws(() => {
sharp().heif({ effort: 'fail' });
});
});
it('valid speed does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ speed: 6 });
@ -62,7 +77,7 @@ describe('HEIF', () => {
});
it('invalid speed should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'fail' });
sharp().heif({ speed: 'fail' });
});
});
it('invalid chromaSubsampling should throw an error', () => {