mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Expose control over CPU effort for palette PNG #2541
This commit is contained in:
parent
140eeebb3d
commit
72025051c5
@ -243,6 +243,7 @@ Set `palette` to `true` for slower, indexed PNG output.
|
|||||||
* `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
* `options.adaptiveFiltering` **[boolean][7]** use adaptive row filtering (optional, default `false`)
|
||||||
* `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
* `options.palette` **[boolean][7]** quantise to a palette-based image with alpha transparency support (optional, default `false`)
|
||||||
* `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
* `options.quality` **[number][9]** use the lowest number of colours needed to achieve given quality, sets `palette` to `true` (optional, default `100`)
|
||||||
|
* `options.effort` **[number][9]** CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true` (optional, default `7`)
|
||||||
* `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
* `options.colours` **[number][9]** maximum number of palette entries, sets `palette` to `true` (optional, default `256`)
|
||||||
* `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
* `options.colors` **[number][9]** alternative spelling of `options.colours`, sets `palette` to `true` (optional, default `256`)
|
||||||
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
* `options.dither` **[number][9]** level of Floyd-Steinberg error diffusion, sets `palette` to `true` (optional, default `1.0`)
|
||||||
|
@ -10,6 +10,9 @@ Requires libvips v8.12.0
|
|||||||
|
|
||||||
* Reduce minimum Linux ARM64v8 glibc requirement to 2.17.
|
* Reduce minimum Linux ARM64v8 glibc requirement to 2.17.
|
||||||
|
|
||||||
|
* Expose control over CPU effort for palette-based PNG output.
|
||||||
|
[#2541](https://github.com/lovell/sharp/issues/2541)
|
||||||
|
|
||||||
* Ensure 16-bit PNG output uses correct bitdepth.
|
* Ensure 16-bit PNG output uses correct bitdepth.
|
||||||
[#2958](https://github.com/lovell/sharp/pull/2958)
|
[#2958](https://github.com/lovell/sharp/pull/2958)
|
||||||
[@gforge](https://github.com/gforge)
|
[@gforge](https://github.com/gforge)
|
||||||
|
@ -234,6 +234,7 @@ const Sharp = function (input, options) {
|
|||||||
pngAdaptiveFiltering: false,
|
pngAdaptiveFiltering: false,
|
||||||
pngPalette: false,
|
pngPalette: false,
|
||||||
pngQuality: 100,
|
pngQuality: 100,
|
||||||
|
pngEffort: 7,
|
||||||
pngBitdepth: 8,
|
pngBitdepth: 8,
|
||||||
pngDither: 1,
|
pngDither: 1,
|
||||||
jp2Quality: 80,
|
jp2Quality: 80,
|
||||||
|
@ -373,6 +373,7 @@ function jpeg (options) {
|
|||||||
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
||||||
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
|
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
|
||||||
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
|
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
|
||||||
|
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true`
|
||||||
* @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
|
* @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
|
||||||
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
|
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
|
||||||
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
|
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
|
||||||
@ -397,7 +398,7 @@ function png (options) {
|
|||||||
}
|
}
|
||||||
if (is.defined(options.palette)) {
|
if (is.defined(options.palette)) {
|
||||||
this._setBooleanOption('pngPalette', options.palette);
|
this._setBooleanOption('pngPalette', options.palette);
|
||||||
} else if (is.defined(options.quality) || is.defined(options.colours || options.colors) || is.defined(options.dither)) {
|
} else if ([options.quality, options.effort, options.colours, options.colors, options.dither].some(is.defined)) {
|
||||||
this._setBooleanOption('pngPalette', true);
|
this._setBooleanOption('pngPalette', true);
|
||||||
}
|
}
|
||||||
if (this.options.pngPalette) {
|
if (this.options.pngPalette) {
|
||||||
@ -408,6 +409,13 @@ function png (options) {
|
|||||||
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.effort)) {
|
||||||
|
if (is.integer(options.effort) && is.inRange(options.effort, 1, 10)) {
|
||||||
|
this.options.pngEffort = options.effort;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
|
||||||
|
}
|
||||||
|
}
|
||||||
const colours = options.colours || options.colors;
|
const colours = options.colours || options.colors;
|
||||||
if (is.defined(colours)) {
|
if (is.defined(colours)) {
|
||||||
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||||
|
@ -824,6 +824,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
||||||
->set("palette", baton->pngPalette)
|
->set("palette", baton->pngPalette)
|
||||||
->set("Q", baton->pngQuality)
|
->set("Q", baton->pngQuality)
|
||||||
|
->set("effort", baton->pngEffort)
|
||||||
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
||||||
->set("dither", baton->pngDither)));
|
->set("dither", baton->pngDither)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
@ -994,6 +995,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("palette", baton->pngPalette)
|
->set("palette", baton->pngPalette)
|
||||||
->set("Q", baton->pngQuality)
|
->set("Q", baton->pngQuality)
|
||||||
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
->set("bitdepth", sharp::Is16Bit(image.interpretation()) ? 16 : baton->pngBitdepth)
|
||||||
|
->set("effort", baton->pngEffort)
|
||||||
->set("dither", baton->pngDither));
|
->set("dither", baton->pngDither));
|
||||||
baton->formatOut = "png";
|
baton->formatOut = "png";
|
||||||
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
||||||
@ -1470,6 +1472,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
|
baton->pngAdaptiveFiltering = sharp::AttrAsBool(options, "pngAdaptiveFiltering");
|
||||||
baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
|
baton->pngPalette = sharp::AttrAsBool(options, "pngPalette");
|
||||||
baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
|
baton->pngQuality = sharp::AttrAsUint32(options, "pngQuality");
|
||||||
|
baton->pngEffort = sharp::AttrAsUint32(options, "pngEffort");
|
||||||
baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
|
baton->pngBitdepth = sharp::AttrAsUint32(options, "pngBitdepth");
|
||||||
baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
|
baton->pngDither = sharp::AttrAsDouble(options, "pngDither");
|
||||||
baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
|
baton->jp2Quality = sharp::AttrAsUint32(options, "jp2Quality");
|
||||||
|
@ -147,6 +147,7 @@ struct PipelineBaton {
|
|||||||
bool pngAdaptiveFiltering;
|
bool pngAdaptiveFiltering;
|
||||||
bool pngPalette;
|
bool pngPalette;
|
||||||
int pngQuality;
|
int pngQuality;
|
||||||
|
int pngEffort;
|
||||||
int pngBitdepth;
|
int pngBitdepth;
|
||||||
double pngDither;
|
double pngDither;
|
||||||
int jp2Quality;
|
int jp2Quality;
|
||||||
@ -287,6 +288,7 @@ struct PipelineBaton {
|
|||||||
pngAdaptiveFiltering(false),
|
pngAdaptiveFiltering(false),
|
||||||
pngPalette(false),
|
pngPalette(false),
|
||||||
pngQuality(100),
|
pngQuality(100),
|
||||||
|
pngEffort(7),
|
||||||
pngBitdepth(8),
|
pngBitdepth(8),
|
||||||
pngDither(1.0),
|
pngDither(1.0),
|
||||||
jp2Quality(80),
|
jp2Quality(80),
|
||||||
|
@ -143,8 +143,8 @@ describe('PNG', function () {
|
|||||||
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sharp(inputPngBuffer).resize(10).png({ quality: 80 }).toBuffer(),
|
sharp(inputPngBuffer).resize(10).png({ effort: 1, quality: 80 }).toBuffer(),
|
||||||
sharp(inputPngBuffer).resize(10).png({ quality: 100 }).toBuffer()
|
sharp(inputPngBuffer).resize(10).png({ effort: 1, quality: 100 }).toBuffer()
|
||||||
]).then(function (data) {
|
]).then(function (data) {
|
||||||
assert.strictEqual(true, data[0].length <= data[1].length);
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
});
|
});
|
||||||
@ -156,6 +156,12 @@ describe('PNG', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid effort value throws error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().png({ effort: 0.1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user