mirror of
https://github.com/lovell/sharp.git
synced 2025-12-19 07:15:08 +01:00
Add support for animated WebP and GIF (via magick) (#2012)
This commit is contained in:
BIN
test/fixtures/animated-loop-3.webp
vendored
Normal file
BIN
test/fixtures/animated-loop-3.webp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
2
test/fixtures/index.js
vendored
2
test/fixtures/index.js
vendored
@@ -93,6 +93,8 @@ module.exports = {
|
||||
|
||||
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
||||
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||
inputWebPAnimated: getPath('rotating-squares.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||
inputWebPAnimatedLoop3: getPath('animated-loop-3.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
||||
inputTiffMultipage: getPath('G31D_MULTI.TIF'), // gm convert G31D.TIF -resize 50% G31D_2.TIF ; tiffcp G31D.TIF G31D_2.TIF G31D_MULTI.TIF
|
||||
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
||||
|
||||
BIN
test/fixtures/rotating-squares.webp
vendored
Normal file
BIN
test/fixtures/rotating-squares.webp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
@@ -61,4 +61,41 @@ describe('GIF input', () => {
|
||||
assert.strictEqual(4, info.channels);
|
||||
})
|
||||
);
|
||||
|
||||
if (!sharp.format.magick.input.buffer) {
|
||||
it('Animated GIF output should fail due to missing ImageMagick', () =>
|
||||
assert.rejects(() =>
|
||||
sharp(fixtures.inputGifAnimated, { pages: -1 })
|
||||
.gif({ loop: 2, delay: [...Array(10).fill(100)], pageHeight: 10 })
|
||||
.toBuffer(),
|
||||
/VipsOperation: class "magicksave_buffer" not found/
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
it('invalid pageHeight throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ pageHeight: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid loop throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ loop: -1 });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().gif({ loop: 65536 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid delay throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().gif({ delay: [-1] });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().gif({ delay: [65536] });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,6 +192,54 @@ describe('Image metadata', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Animated WebP', () =>
|
||||
sharp(fixtures.inputWebPAnimated)
|
||||
.metadata()
|
||||
.then(({
|
||||
format, width, height, space, channels, depth,
|
||||
isProgressive, pages, pageHeight, loop, delay,
|
||||
hasProfile, hasAlpha
|
||||
}) => {
|
||||
assert.strictEqual(format, 'webp');
|
||||
assert.strictEqual(width, 80);
|
||||
assert.strictEqual(height, 80);
|
||||
assert.strictEqual(space, 'srgb');
|
||||
assert.strictEqual(channels, 4);
|
||||
assert.strictEqual(depth, 'uchar');
|
||||
assert.strictEqual(isProgressive, false);
|
||||
assert.strictEqual(pages, 9);
|
||||
assert.strictEqual(pageHeight, 80);
|
||||
assert.strictEqual(loop, 0);
|
||||
assert.deepStrictEqual(delay, [120, 120, 90, 120, 120, 90, 120, 90, 30]);
|
||||
assert.strictEqual(hasProfile, false);
|
||||
assert.strictEqual(hasAlpha, true);
|
||||
})
|
||||
);
|
||||
|
||||
it('Animated WebP with limited looping', () =>
|
||||
sharp(fixtures.inputWebPAnimatedLoop3)
|
||||
.metadata()
|
||||
.then(({
|
||||
format, width, height, space, channels, depth,
|
||||
isProgressive, pages, pageHeight, loop, delay,
|
||||
hasProfile, hasAlpha
|
||||
}) => {
|
||||
assert.strictEqual(format, 'webp');
|
||||
assert.strictEqual(width, 370);
|
||||
assert.strictEqual(height, 285);
|
||||
assert.strictEqual(space, 'srgb');
|
||||
assert.strictEqual(channels, 4);
|
||||
assert.strictEqual(depth, 'uchar');
|
||||
assert.strictEqual(isProgressive, false);
|
||||
assert.strictEqual(pages, 10);
|
||||
assert.strictEqual(pageHeight, 285);
|
||||
assert.strictEqual(loop, 3);
|
||||
assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]);
|
||||
assert.strictEqual(hasProfile, false);
|
||||
assert.strictEqual(hasAlpha, true);
|
||||
})
|
||||
);
|
||||
|
||||
it('GIF via giflib', function (done) {
|
||||
sharp(fixtures.inputGif).metadata(function (err, metadata) {
|
||||
if (err) throw err;
|
||||
|
||||
@@ -125,4 +125,63 @@ describe('WebP', function () {
|
||||
sharp().webp({ reductionEffort: -1 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid pageHeight throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ pageHeight: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid loop throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ loop: -1 });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().webp({ loop: 65536 });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid delay throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ delay: [-1] });
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
sharp().webp({ delay: [65536] });
|
||||
});
|
||||
});
|
||||
|
||||
it('should double the number of frames with default delay', async () => {
|
||||
const original = await sharp(fixtures.inputWebPAnimated, { pages: -1 }).metadata();
|
||||
const updated = await sharp(fixtures.inputWebPAnimated, { pages: -1 })
|
||||
.webp({ pageHeight: original.pageHeight / 2 })
|
||||
.toBuffer()
|
||||
.then(data => sharp(data, { pages: -1 }).metadata());
|
||||
|
||||
assert.strictEqual(updated.pages, original.pages * 2);
|
||||
assert.strictEqual(updated.pageHeight, original.pageHeight / 2);
|
||||
assert.deepStrictEqual(updated.delay, [...original.delay, ...Array(9).fill(120)]);
|
||||
});
|
||||
|
||||
it('should limit animation loop', async () => {
|
||||
const updated = await sharp(fixtures.inputWebPAnimated, { pages: -1 })
|
||||
.webp({ loop: 3 })
|
||||
.toBuffer()
|
||||
.then(data => sharp(data, { pages: -1 }).metadata());
|
||||
|
||||
assert.strictEqual(updated.loop, 3);
|
||||
});
|
||||
|
||||
it('should change delay between frames', async () => {
|
||||
const original = await sharp(fixtures.inputWebPAnimated, { pages: -1 }).metadata();
|
||||
|
||||
const expectedDelay = [...Array(original.pages).fill(40)];
|
||||
const updated = await sharp(fixtures.inputWebPAnimated, { pages: -1 })
|
||||
.webp({ delay: expectedDelay })
|
||||
.toBuffer()
|
||||
.then(data => sharp(data, { pages: -1 }).metadata());
|
||||
|
||||
assert.deepStrictEqual(updated.delay, expectedDelay);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user