diff --git a/docs/api-output.md b/docs/api-output.md index 63a2cfea..391daf66 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -374,6 +374,8 @@ Use these AVIF options for output image. Whilst it is possible to create AVIF images smaller than 16x16 pixels, most web browsers do not display these properly. +AVIF image sequences are not supported. + ### Parameters * `options` **[Object][6]?** output options diff --git a/docs/changelog.md b/docs/changelog.md index e92ed326..1cb34fbd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,9 @@ Requires libvips v8.11.3 * Ensure images with P3 profiles retain full gamut. [#2862](https://github.com/lovell/sharp/issues/2862) +* Remove unsupported animation properties from AVIF output. + [#2870](https://github.com/lovell/sharp/issues/2870) + ### v0.29.0 - 17th August 2021 * Drop support for Node.js 10, now requires Node.js >= 12.13.0. diff --git a/lib/output.js b/lib/output.js index e0b2610c..80277a3d 100644 --- a/lib/output.js +++ b/lib/output.js @@ -656,6 +656,8 @@ function tiff (options) { * Whilst it is possible to create AVIF images smaller than 16x16 pixels, * most web browsers do not display these properly. * + * AVIF image sequences are not supported. + * * @since 0.27.0 * * @param {Object} [options] - output options diff --git a/src/common.cc b/src/common.cc index 5d2d6255..17e4634e 100644 --- a/src/common.cc +++ b/src/common.cc @@ -510,6 +510,17 @@ namespace sharp { return copy; } + /* + Remove animation properties from image. + */ + VImage RemoveAnimationProperties(VImage image) { + VImage copy = image.copy(); + copy.remove(VIPS_META_PAGE_HEIGHT); + copy.remove("delay"); + copy.remove("loop"); + return copy; + } + /* Does this image have a non-default density? */ diff --git a/src/common.h b/src/common.h index a59bcee4..93d09eb8 100644 --- a/src/common.h +++ b/src/common.h @@ -208,6 +208,11 @@ namespace sharp { */ VImage SetAnimationProperties(VImage image, int pageHeight, std::vector delay, int loop); + /* + Remove animation properties from image. + */ + VImage RemoveAnimationProperties(VImage image); + /* Does this image have a non-default density? */ diff --git a/src/pipeline.cc b/src/pipeline.cc index 981ced05..2a6fb69d 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -872,6 +872,7 @@ class PipelineWorker : public Napi::AsyncWorker { } else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) { // Write HEIF to buffer + image = sharp::RemoveAnimationProperties(image); VipsArea *area = reinterpret_cast(image.heifsave_buffer(VImage::option() ->set("strip", !baton->withMetadata) ->set("Q", baton->heifQuality) @@ -1012,6 +1013,7 @@ class PipelineWorker : public Napi::AsyncWorker { } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) || (willMatchInput && inputImageType == sharp::ImageType::HEIF)) { // Write HEIF to file + image = sharp::RemoveAnimationProperties(image); image.heifsave(const_cast(baton->fileOut.data()), VImage::option() ->set("strip", !baton->withMetadata) ->set("Q", baton->heifQuality) diff --git a/test/unit/avif.js b/test/unit/avif.js index 37ba7301..cd699149 100644 --- a/test/unit/avif.js +++ b/test/unit/avif.js @@ -3,7 +3,7 @@ const assert = require('assert'); const sharp = require('../../'); -const { inputAvif, inputJpg } = require('../fixtures'); +const { inputAvif, inputJpg, inputGifAnimated } = require('../fixtures'); describe('AVIF', () => { it('called without options does not throw an error', () => { @@ -81,4 +81,29 @@ describe('AVIF', () => { width: 32 }); }); + + it('can convert animated GIF to non-animated AVIF', async () => { + const data = await sharp(inputGifAnimated, { animated: true }) + .resize(10) + .avif({ speed: 8 }) + .toBuffer(); + const metadata = await sharp(data) + .metadata(); + const { size, ...metadataWithoutSize } = metadata; + assert.deepStrictEqual(metadataWithoutSize, { + channels: 4, + compression: 'av1', + depth: 'uchar', + format: 'heif', + hasAlpha: true, + hasProfile: false, + height: 300, + isProgressive: false, + pageHeight: 300, + pagePrimary: 0, + pages: 1, + space: 'srgb', + width: 10 + }); + }); });