mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 02:30:12 +02:00
Add support for GIF output using cgif in prebuilt binaries
This commit is contained in:
parent
b876abaf88
commit
f7f3e43490
@ -4,7 +4,7 @@
|
||||
|
||||
The typical use case for this high speed Node.js module
|
||||
is to convert large images in common formats to
|
||||
smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions.
|
||||
smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
The typical use case for this high speed Node.js module
|
||||
is to convert large images in common formats to
|
||||
smaller, web-friendly JPEG, PNG, AVIF and WebP images of varying dimensions.
|
||||
smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions.
|
||||
|
||||
Resizing an image is typically 4x-5x faster than using the
|
||||
quickest ImageMagick and GraphicsMagick settings
|
||||
@ -21,9 +21,9 @@ do not require any additional install or runtime dependencies.
|
||||
|
||||
### Formats
|
||||
|
||||
This module supports reading JPEG, PNG, WebP, AVIF, TIFF, GIF and SVG images.
|
||||
This module supports reading JPEG, PNG, WebP, GIF, AVIF, TIFF and SVG images.
|
||||
|
||||
Output images can be in JPEG, PNG, WebP, AVIF and TIFF formats as well as uncompressed raw pixel data.
|
||||
Output images can be in JPEG, PNG, WebP, GIF, AVIF and TIFF formats as well as uncompressed raw pixel data.
|
||||
|
||||
Streams, Buffer objects and the filesystem can be used for input and output.
|
||||
|
||||
|
@ -6,6 +6,8 @@ Requires libvips v8.12.0
|
||||
|
||||
### v0.30.0 - TBD
|
||||
|
||||
* Add support for GIF output to prebuilt binaries.
|
||||
|
||||
* Reduce minimum Linux ARM64v8 glibc requirement to 2.17.
|
||||
|
||||
* Properly emit close events for duplex streams.
|
||||
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP and AVIF images of varying dimensions">
|
||||
<meta name="description" content="Resize large images in common formats to smaller, web-friendly JPEG, PNG, WebP, GIF and AVIF images of varying dimensions">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
||||
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||
connect-src 'self' https://www.google-analytics.com;
|
||||
|
@ -19,7 +19,7 @@ Ready-compiled sharp and libvips binaries are provided for use on the most commo
|
||||
* macOS x64 (>= 10.13)
|
||||
* macOS ARM64
|
||||
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
||||
* Linux ARM64 (glibc >= 2.29, musl >= 1.1.24)
|
||||
* Linux ARM64 (glibc >= 2.17, musl >= 1.1.24)
|
||||
* Windows x64
|
||||
* Windows x86
|
||||
|
||||
@ -27,7 +27,7 @@ An ~7MB tarball containing libvips and its most commonly used dependencies
|
||||
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
|
||||
|
||||
This provides support for the
|
||||
JPEG, PNG, WebP, AVIF, TIFF, GIF (input) and SVG (input) image formats.
|
||||
JPEG, PNG, WebP, AVIF, TIFF, GIF and SVG (input) image formats.
|
||||
|
||||
The following platforms have prebuilt libvips but not sharp:
|
||||
|
||||
@ -38,8 +38,6 @@ The following platforms have prebuilt libvips but not sharp:
|
||||
The following platforms require compilation of both libvips and sharp from source:
|
||||
|
||||
* Linux x86
|
||||
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
|
||||
* Linux ARM64 (glibc <= 2.28)
|
||||
* Linux ARMv7 (glibc <= 2.27, musl)
|
||||
* Linux ARMv6 (glibc <= 2.27, musl)
|
||||
* Linux PowerPC
|
||||
|
@ -13,7 +13,7 @@ const debuglog = util.debuglog('sharp');
|
||||
/**
|
||||
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
|
||||
*
|
||||
* JPEG, PNG, WebP, AVIF or TIFF format image data can be streamed out from this object.
|
||||
* JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
|
||||
* When using Stream based output, derived attributes are available from the `info` event.
|
||||
*
|
||||
* Non-critical problems encountered during processing are emitted as `warning` events.
|
||||
@ -246,6 +246,9 @@ const Sharp = function (input, options) {
|
||||
webpNearLossless: false,
|
||||
webpSmartSubsample: false,
|
||||
webpReductionEffort: 4,
|
||||
gifBitdepth: 8,
|
||||
gifEffort: 7,
|
||||
gifDither: 1,
|
||||
tiffQuality: 80,
|
||||
tiffCompression: 'jpeg',
|
||||
tiffPredictor: 'horizontal',
|
||||
|
@ -22,14 +22,15 @@ const formats = new Map([
|
||||
['j2c', 'jp2']
|
||||
]);
|
||||
|
||||
const errMagickSave = new Error('GIF output requires libvips with support for ImageMagick');
|
||||
const errJp2Save = new Error('JP2 output requires libvips with support for OpenJPEG');
|
||||
|
||||
const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
|
||||
|
||||
/**
|
||||
* Write output image data to a file.
|
||||
*
|
||||
* If an explicit output format is not selected, it will be inferred from the extension,
|
||||
* with JPEG, PNG, WebP, AVIF, TIFF, DZI, and libvips' V format supported.
|
||||
* with JPEG, PNG, WebP, AVIF, TIFF, GIF, DZI, and libvips' V format supported.
|
||||
* Note that raw pixel data is only supported for buffer output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
@ -63,8 +64,6 @@ function toFile (fileOut, callback) {
|
||||
err = new Error('Missing output file path');
|
||||
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
|
||||
err = new Error('Cannot use same file for input and output');
|
||||
} else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) {
|
||||
err = errMagickSave;
|
||||
}
|
||||
if (err) {
|
||||
if (is.fn(callback)) {
|
||||
@ -81,9 +80,9 @@ function toFile (fileOut, callback) {
|
||||
|
||||
/**
|
||||
* Write output to a Buffer.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.
|
||||
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
||||
*
|
||||
* If no explicit format is set, the output format will match the input image, except GIF and SVG input which become PNG output.
|
||||
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
||||
*
|
||||
* By default all metadata will be removed, which includes EXIF-based orientation.
|
||||
* See {@link withMetadata} for control over this.
|
||||
@ -412,7 +411,7 @@ function png (options) {
|
||||
const colours = options.colours || options.colors;
|
||||
if (is.defined(colours)) {
|
||||
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||
this.options.pngBitdepth = 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
|
||||
this.options.pngBitdepth = bitdepthFromColourCount(colours);
|
||||
} else {
|
||||
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||
}
|
||||
@ -495,13 +494,40 @@ function webp (options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use these GIF options for output image.
|
||||
* Use these GIF options for the output image.
|
||||
*
|
||||
* Requires libvips compiled with support for ImageMagick or GraphicsMagick.
|
||||
* The prebuilt binaries do not include this - see
|
||||
* {@link https://sharp.pixelplumbing.com/install#custom-libvips installing a custom libvips}.
|
||||
* The first entry in the palette is reserved for transparency.
|
||||
*
|
||||
* @example
|
||||
* // Convert PNG to GIF
|
||||
* await sharp(pngBuffer)
|
||||
* .gif()
|
||||
* .toBuffer());
|
||||
*
|
||||
* @example
|
||||
* // Convert animated WebP to animated GIF
|
||||
* await sharp('animated.webp', { animated: true })
|
||||
* .toFile('animated.gif');
|
||||
*
|
||||
* @example
|
||||
* // Create 128x128, non-dithered thumbnail of an animated GIF
|
||||
* const { pages } = await sharp('animated.gif').metadata();
|
||||
* const gif = await sharp('animated.gif', { animated: true })
|
||||
* .resize({
|
||||
* width: 128,
|
||||
* height: 128 * pages
|
||||
* })
|
||||
* .gif({
|
||||
* pageHeight: 128,
|
||||
* dither: 0
|
||||
* })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options] - output options
|
||||
* @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
|
||||
* @param {number} [options.colors=256] - alternative spelling of `options.colours`
|
||||
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
|
||||
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
|
||||
* @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)
|
||||
@ -509,10 +535,30 @@ function webp (options) {
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
function gif (options) {
|
||||
if (!this.constructor.format.magick.output.buffer) {
|
||||
throw errMagickSave;
|
||||
if (is.object(options)) {
|
||||
const colours = options.colours || options.colors;
|
||||
if (is.defined(colours)) {
|
||||
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||
this.options.gifBitdepth = bitdepthFromColourCount(colours);
|
||||
} else {
|
||||
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.effort)) {
|
||||
if (is.number(options.effort) && is.inRange(options.effort, 1, 10)) {
|
||||
this.options.gifEffort = options.effort;
|
||||
} else {
|
||||
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.dither)) {
|
||||
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
||||
this.options.gifDither = options.dither;
|
||||
} else {
|
||||
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
||||
}
|
||||
}
|
||||
}
|
||||
trySetAnimationOptions(options, this.options);
|
||||
return this._updateFormatOut('gif', options);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||
"version": "0.29.3",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
|
@ -762,9 +762,6 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
baton->width = image.width();
|
||||
baton->height = image.height();
|
||||
|
||||
bool const supportsGifOutput = vips_type_find("VipsOperation", "magicksave") != 0 &&
|
||||
vips_type_find("VipsOperation", "magicksave_buffer") != 0;
|
||||
|
||||
image = sharp::SetAnimationProperties(
|
||||
image,
|
||||
baton->pageHeight,
|
||||
@ -817,8 +814,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips_area_unref(area);
|
||||
baton->formatOut = "jp2";
|
||||
} else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
|
||||
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
||||
inputImageType == sharp::ImageType::SVG))) {
|
||||
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
||||
// Write PNG to buffer
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
||||
@ -853,14 +849,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
vips_area_unref(area);
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "gif" ||
|
||||
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
||||
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF)) {
|
||||
// Write GIF to buffer
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.magicksave_buffer(VImage::option()
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.gifsave_buffer(VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("optimize_gif_frames", TRUE)
|
||||
->set("optimize_gif_transparency", TRUE)
|
||||
->set("format", "gif")));
|
||||
->set("bitdepth", baton->gifBitdepth)
|
||||
->set("effort", baton->gifEffort)
|
||||
->set("dither", baton->gifDither)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
area->free_fn = nullptr;
|
||||
@ -987,8 +983,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("tile_width", baton->jp2TileWidth));
|
||||
baton->formatOut = "jp2";
|
||||
} else if (baton->formatOut == "png" || (mightMatchInput && isPng) || (willMatchInput &&
|
||||
(inputImageType == sharp::ImageType::PNG || (inputImageType == sharp::ImageType::GIF && !supportsGifOutput) ||
|
||||
inputImageType == sharp::ImageType::SVG))) {
|
||||
(inputImageType == sharp::ImageType::PNG || inputImageType == sharp::ImageType::SVG))) {
|
||||
// Write PNG to file
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
||||
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||
@ -1015,14 +1010,14 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("alpha_q", baton->webpAlphaQuality));
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
||||
(willMatchInput && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
||||
(willMatchInput && inputImageType == sharp::ImageType::GIF)) {
|
||||
// Write GIF to file
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
||||
image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||
image.gifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("optimize_gif_frames", TRUE)
|
||||
->set("optimize_gif_transparency", TRUE)
|
||||
->set("format", "gif"));
|
||||
->set("bitdepth", baton->gifBitdepth)
|
||||
->set("effort", baton->gifEffort)
|
||||
->set("dither", baton->gifDither));
|
||||
baton->formatOut = "gif";
|
||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||
(willMatchInput && inputImageType == sharp::ImageType::TIFF)) {
|
||||
@ -1488,6 +1483,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
||||
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
||||
baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
|
||||
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
||||
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||
baton->tiffQuality = sharp::AttrAsUint32(options, "tiffQuality");
|
||||
baton->tiffPyramid = sharp::AttrAsBool(options, "tiffPyramid");
|
||||
baton->tiffBitdepth = sharp::AttrAsUint32(options, "tiffBitdepth");
|
||||
|
@ -160,6 +160,9 @@ struct PipelineBaton {
|
||||
bool webpLossless;
|
||||
bool webpSmartSubsample;
|
||||
int webpReductionEffort;
|
||||
int gifBitdepth;
|
||||
int gifEffort;
|
||||
double gifDither;
|
||||
int tiffQuality;
|
||||
VipsForeignTiffCompression tiffCompression;
|
||||
VipsForeignTiffPredictor tiffPredictor;
|
||||
|
@ -8,7 +8,7 @@ fi
|
||||
curl -s -o ./test/leak/libvips.supp https://raw.githubusercontent.com/libvips/libvips/master/suppressions/valgrind.supp
|
||||
|
||||
for test in ./test/unit/*.js; do
|
||||
G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind \
|
||||
G_SLICE=always-malloc G_DEBUG=gc-friendly VIPS_LEAK=1 valgrind \
|
||||
--suppressions=test/leak/libvips.supp \
|
||||
--suppressions=test/leak/sharp.supp \
|
||||
--gen-suppressions=yes \
|
||||
|
@ -225,14 +225,10 @@
|
||||
fun:FcInitLoadConfigAndFonts
|
||||
}
|
||||
{
|
||||
leak_fontconfig_doContent
|
||||
leak_fontconfig_XML_ParseBuffer
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
...
|
||||
fun:doContent
|
||||
fun:doProlog
|
||||
fun:prologInitProcessor
|
||||
fun:XML_ParseBuffer
|
||||
obj:*/libfontconfig.so.*
|
||||
}
|
||||
|
107
test/unit/gif.js
107
test/unit/gif.js
@ -42,7 +42,7 @@ describe('GIF input', () => {
|
||||
.then(({ data, info }) => {
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual(data.length, info.size);
|
||||
assert.strictEqual(sharp.format.magick.input.buffer ? 'gif' : 'png', info.format);
|
||||
assert.strictEqual('gif', info.format);
|
||||
assert.strictEqual(80, info.width);
|
||||
assert.strictEqual(80, info.height);
|
||||
assert.strictEqual(4, info.channels);
|
||||
@ -55,28 +55,30 @@ describe('GIF input', () => {
|
||||
.then(({ data, info }) => {
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual(data.length, info.size);
|
||||
assert.strictEqual(sharp.format.magick.input.buffer ? 'gif' : 'png', info.format);
|
||||
assert.strictEqual('gif', info.format);
|
||||
assert.strictEqual(80, info.width);
|
||||
assert.strictEqual(2400, info.height);
|
||||
assert.strictEqual(4, info.channels);
|
||||
})
|
||||
);
|
||||
|
||||
if (!sharp.format.magick.output.buffer) {
|
||||
it('GIF buffer output should fail due to missing ImageMagick', () => {
|
||||
assert.throws(
|
||||
() => sharp().gif(),
|
||||
/GIF output requires libvips with support for ImageMagick/
|
||||
);
|
||||
});
|
||||
it('GIF with reduced colours, no dither, low effort reduces file size', async () => {
|
||||
const original = await sharp(fixtures.inputJpg)
|
||||
.resize(120, 80)
|
||||
.gif()
|
||||
.toBuffer();
|
||||
|
||||
it('GIF file output should fail due to missing ImageMagick', () => {
|
||||
assert.rejects(
|
||||
async () => await sharp().toFile('test.gif'),
|
||||
/GIF output requires libvips with support for ImageMagick/
|
||||
);
|
||||
});
|
||||
}
|
||||
const reduced = await sharp(fixtures.inputJpg)
|
||||
.resize(120, 80)
|
||||
.gif({
|
||||
colours: 128,
|
||||
dither: 0,
|
||||
effort: 1
|
||||
})
|
||||
.toBuffer();
|
||||
|
||||
assert.strictEqual(true, reduced.length < original.length);
|
||||
});
|
||||
|
||||
it('invalid pageHeight throws', () => {
|
||||
assert.throws(() => {
|
||||
@ -88,7 +90,6 @@ describe('GIF input', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ loop: -1 });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().gif({ loop: 65536 });
|
||||
});
|
||||
@ -98,41 +99,59 @@ describe('GIF input', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ delay: [-1] });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().gif({ delay: [65536] });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid colour throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ colours: 1 });
|
||||
});
|
||||
assert.throws(() => {
|
||||
sharp().gif({ colours: 'fail' });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid effort throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ effort: 0 });
|
||||
});
|
||||
assert.throws(() => {
|
||||
sharp().gif({ effort: 'fail' });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid dither throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ dither: 1.1 });
|
||||
});
|
||||
assert.throws(() => {
|
||||
sharp().gif({ effort: 'fail' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with streams when only animated is set', function (done) {
|
||||
if (sharp.format.magick.output.buffer) {
|
||||
fs.createReadStream(fixtures.inputGifAnimated)
|
||||
.pipe(sharp({ animated: true }))
|
||||
.gif()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('gif', info.format);
|
||||
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
fs.createReadStream(fixtures.inputGifAnimated)
|
||||
.pipe(sharp({ animated: true }))
|
||||
.gif()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('gif', info.format);
|
||||
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with streams when only pages is set', function (done) {
|
||||
if (sharp.format.magick.output.buffer) {
|
||||
fs.createReadStream(fixtures.inputGifAnimated)
|
||||
.pipe(sharp({ pages: -1 }))
|
||||
.gif()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('gif', info.format);
|
||||
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
fs.createReadStream(fixtures.inputGifAnimated)
|
||||
.pipe(sharp({ pages: -1 }))
|
||||
.gif()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
assert.strictEqual('gif', info.format);
|
||||
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -561,19 +561,6 @@ describe('Input/output', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Autoconvert GIF input to PNG output', function (done) {
|
||||
sharp(fixtures.inputGif)
|
||||
.resize(320, 80)
|
||||
.toFile(outputZoinks, function (err, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, info.size > 0);
|
||||
assert.strictEqual(sharp.format.magick.input.buffer ? 'gif' : 'png', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(80, info.height);
|
||||
rimraf(outputZoinks, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Force JPEG format for PNG input', function (done) {
|
||||
sharp(fixtures.inputPng)
|
||||
.resize(320, 80)
|
||||
|
Loading…
x
Reference in New Issue
Block a user