mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Expose PNG output options requiring libimagequant #1484
This commit is contained in:
parent
bd377438b6
commit
98797445de
@ -154,6 +154,11 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
||||||
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
||||||
|
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libimagequant (optional, default `false`)
|
||||||
|
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libimagequant (optional, default `100`)
|
||||||
|
- `options.colours` **[Number][8]** maximum number of palette entries, requires libimagequant (optional, default `256`)
|
||||||
|
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libimagequant (optional, default `256`)
|
||||||
|
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libimagequant (optional, default `1.0`)
|
||||||
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
@ -17,6 +17,9 @@ Requires libvips v8.7.0.
|
|||||||
* Expose `pages` and `pageHeight` metadata for multi-page input images.
|
* Expose `pages` and `pageHeight` metadata for multi-page input images.
|
||||||
[#1205](https://github.com/lovell/sharp/issues/1205)
|
[#1205](https://github.com/lovell/sharp/issues/1205)
|
||||||
|
|
||||||
|
* Expose PNG output options requiring libimagequant.
|
||||||
|
[#1484](https://github.com/lovell/sharp/issues/1484)
|
||||||
|
|
||||||
* Expose underlying error message for invalid input.
|
* Expose underlying error message for invalid input.
|
||||||
[#1505](https://github.com/lovell/sharp/issues/1505)
|
[#1505](https://github.com/lovell/sharp/issues/1505)
|
||||||
|
|
||||||
|
@ -171,6 +171,10 @@ const Sharp = function (input, options) {
|
|||||||
pngProgressive: false,
|
pngProgressive: false,
|
||||||
pngCompressionLevel: 9,
|
pngCompressionLevel: 9,
|
||||||
pngAdaptiveFiltering: false,
|
pngAdaptiveFiltering: false,
|
||||||
|
pngPalette: false,
|
||||||
|
pngQuality: 100,
|
||||||
|
pngColours: 256,
|
||||||
|
pngDither: 1,
|
||||||
webpQuality: 80,
|
webpQuality: 80,
|
||||||
webpAlphaQuality: 100,
|
webpAlphaQuality: 100,
|
||||||
webpLossless: false,
|
webpLossless: false,
|
||||||
|
@ -221,6 +221,11 @@ function jpeg (options) {
|
|||||||
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
|
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
|
||||||
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
|
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
|
||||||
* @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, requires libimagequant
|
||||||
|
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
|
||||||
|
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
|
||||||
|
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
|
||||||
|
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
|
||||||
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@ -240,6 +245,33 @@ function png (options) {
|
|||||||
if (is.defined(options.adaptiveFiltering)) {
|
if (is.defined(options.adaptiveFiltering)) {
|
||||||
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.palette)) {
|
||||||
|
this._setBooleanOption('pngPalette', options.palette);
|
||||||
|
if (this.options.pngPalette) {
|
||||||
|
if (is.defined(options.quality)) {
|
||||||
|
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
|
||||||
|
this.options.pngQuality = options.quality;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const colours = options.colours || options.colors;
|
||||||
|
if (is.defined(colours)) {
|
||||||
|
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||||
|
this.options.pngColours = colours;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.dither)) {
|
||||||
|
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
||||||
|
this.options.pngDither = options.dither;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this._updateFormatOut('png', options);
|
return this._updateFormatOut('png', options);
|
||||||
}
|
}
|
||||||
|
@ -739,7 +739,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("interlace", baton->pngProgressive)
|
->set("interlace", baton->pngProgressive)
|
||||||
->set("compression", baton->pngCompressionLevel)
|
->set("compression", baton->pngCompressionLevel)
|
||||||
->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("Q", baton->pngQuality)
|
||||||
|
->set("colours", baton->pngColours)
|
||||||
|
->set("dither", baton->pngDither)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
area->free_fn = nullptr;
|
area->free_fn = nullptr;
|
||||||
@ -849,7 +853,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("interlace", baton->pngProgressive)
|
->set("interlace", baton->pngProgressive)
|
||||||
->set("compression", baton->pngCompressionLevel)
|
->set("compression", baton->pngCompressionLevel)
|
||||||
->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("Q", baton->pngQuality)
|
||||||
|
->set("colours", baton->pngColours)
|
||||||
|
->set("dither", baton->pngDither));
|
||||||
baton->formatOut = "png";
|
baton->formatOut = "png";
|
||||||
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
||||||
(willMatchInput && inputImageType == ImageType::WEBP)) {
|
(willMatchInput && inputImageType == ImageType::WEBP)) {
|
||||||
@ -1284,6 +1292,10 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
|
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
|
||||||
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
||||||
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
||||||
|
baton->pngPalette = AttrTo<bool>(options, "pngPalette");
|
||||||
|
baton->pngQuality = AttrTo<uint32_t>(options, "pngQuality");
|
||||||
|
baton->pngColours = AttrTo<uint32_t>(options, "pngColours");
|
||||||
|
baton->pngDither = AttrTo<double>(options, "pngDither");
|
||||||
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
||||||
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||||
|
@ -115,6 +115,10 @@ struct PipelineBaton {
|
|||||||
bool pngProgressive;
|
bool pngProgressive;
|
||||||
int pngCompressionLevel;
|
int pngCompressionLevel;
|
||||||
bool pngAdaptiveFiltering;
|
bool pngAdaptiveFiltering;
|
||||||
|
bool pngPalette;
|
||||||
|
int pngQuality;
|
||||||
|
int pngColours;
|
||||||
|
double pngDither;
|
||||||
int webpQuality;
|
int webpQuality;
|
||||||
int webpAlphaQuality;
|
int webpAlphaQuality;
|
||||||
bool webpNearLossless;
|
bool webpNearLossless;
|
||||||
@ -216,6 +220,10 @@ struct PipelineBaton {
|
|||||||
pngProgressive(false),
|
pngProgressive(false),
|
||||||
pngCompressionLevel(9),
|
pngCompressionLevel(9),
|
||||||
pngAdaptiveFiltering(false),
|
pngAdaptiveFiltering(false),
|
||||||
|
pngPalette(false),
|
||||||
|
pngQuality(100),
|
||||||
|
pngColours(256),
|
||||||
|
pngDither(1.0),
|
||||||
webpQuality(80),
|
webpQuality(80),
|
||||||
tiffQuality(80),
|
tiffQuality(80),
|
||||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||||
|
@ -628,78 +628,6 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PNG output', function () {
|
|
||||||
it('compression level is valid', function () {
|
|
||||||
assert.doesNotThrow(function () {
|
|
||||||
sharp().png({ compressionLevel: 0 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('compression level is invalid', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp().png({ compressionLevel: -1 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('default compressionLevel generates smaller file than compressionLevel=6', function (done) {
|
|
||||||
// First generate with default compressionLevel
|
|
||||||
sharp(fixtures.inputPng)
|
|
||||||
.resize(320, 240)
|
|
||||||
.png()
|
|
||||||
.toBuffer(function (err, defaultData, defaultInfo) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, defaultData.length > 0);
|
|
||||||
assert.strictEqual('png', defaultInfo.format);
|
|
||||||
// Then generate with compressionLevel=6
|
|
||||||
sharp(fixtures.inputPng)
|
|
||||||
.resize(320, 240)
|
|
||||||
.png({ compressionLevel: 6 })
|
|
||||||
.toBuffer(function (err, largerData, largerInfo) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, largerData.length > 0);
|
|
||||||
assert.strictEqual('png', largerInfo.format);
|
|
||||||
assert.strictEqual(true, defaultData.length < largerData.length);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('without adaptiveFiltering generates smaller file', function (done) {
|
|
||||||
// First generate with adaptive filtering
|
|
||||||
sharp(fixtures.inputPng)
|
|
||||||
.resize(320, 240)
|
|
||||||
.png({ adaptiveFiltering: true })
|
|
||||||
.toBuffer(function (err, adaptiveData, adaptiveInfo) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, adaptiveData.length > 0);
|
|
||||||
assert.strictEqual(adaptiveData.length, adaptiveInfo.size);
|
|
||||||
assert.strictEqual('png', adaptiveInfo.format);
|
|
||||||
assert.strictEqual(320, adaptiveInfo.width);
|
|
||||||
assert.strictEqual(240, adaptiveInfo.height);
|
|
||||||
// Then generate without
|
|
||||||
sharp(fixtures.inputPng)
|
|
||||||
.resize(320, 240)
|
|
||||||
.png({ adaptiveFiltering: false })
|
|
||||||
.toBuffer(function (err, withoutAdaptiveData, withoutAdaptiveInfo) {
|
|
||||||
if (err) throw err;
|
|
||||||
assert.strictEqual(true, withoutAdaptiveData.length > 0);
|
|
||||||
assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size);
|
|
||||||
assert.strictEqual('png', withoutAdaptiveInfo.format);
|
|
||||||
assert.strictEqual(320, withoutAdaptiveInfo.width);
|
|
||||||
assert.strictEqual(240, withoutAdaptiveInfo.height);
|
|
||||||
assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Invalid PNG adaptiveFiltering value throws error', function () {
|
|
||||||
assert.throws(function () {
|
|
||||||
sharp().png({ adaptiveFiltering: 1 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Without chroma subsampling generates larger file', function (done) {
|
it('Without chroma subsampling generates larger file', function (done) {
|
||||||
// First generate with chroma subsampling (default)
|
// First generate with chroma subsampling (default)
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
|
145
test/unit/png.js
Normal file
145
test/unit/png.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('PNG output', function () {
|
||||||
|
it('compression level is valid', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().png({ compressionLevel: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compression level is invalid', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ compressionLevel: -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('default compressionLevel generates smaller file than compressionLevel=6', function (done) {
|
||||||
|
// First generate with default compressionLevel
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png()
|
||||||
|
.toBuffer(function (err, defaultData, defaultInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, defaultData.length > 0);
|
||||||
|
assert.strictEqual('png', defaultInfo.format);
|
||||||
|
// Then generate with compressionLevel=6
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
|
.toBuffer(function (err, largerData, largerInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, largerData.length > 0);
|
||||||
|
assert.strictEqual('png', largerInfo.format);
|
||||||
|
assert.strictEqual(true, defaultData.length < largerData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('without adaptiveFiltering generates smaller file', function (done) {
|
||||||
|
// First generate with adaptive filtering
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ adaptiveFiltering: true })
|
||||||
|
.toBuffer(function (err, adaptiveData, adaptiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, adaptiveData.length > 0);
|
||||||
|
assert.strictEqual(adaptiveData.length, adaptiveInfo.size);
|
||||||
|
assert.strictEqual('png', adaptiveInfo.format);
|
||||||
|
assert.strictEqual(320, adaptiveInfo.width);
|
||||||
|
assert.strictEqual(240, adaptiveInfo.height);
|
||||||
|
// Then generate without
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ adaptiveFiltering: false })
|
||||||
|
.toBuffer(function (err, withoutAdaptiveData, withoutAdaptiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutAdaptiveData.length > 0);
|
||||||
|
assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size);
|
||||||
|
assert.strictEqual('png', withoutAdaptiveInfo.format);
|
||||||
|
assert.strictEqual(320, withoutAdaptiveInfo.width);
|
||||||
|
assert.strictEqual(240, withoutAdaptiveInfo.height);
|
||||||
|
assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG adaptiveFiltering value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ adaptiveFiltering: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant palette value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().png({ palette: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant palette value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 80 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 100 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant quality value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 100 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 200 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant colours value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, colours: -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant colors value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, colors: 0.1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant dither value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.1 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.9 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant dither value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, dither: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user