From 103ec0d58f1c6a940a617a156e4d9f3cd832233a Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Tue, 17 Nov 2020 14:32:05 +0000 Subject: [PATCH] Upgrade to libvips 8.10.5, AVIF support in prebuilt binaries Remove experimental status from HEIF, changing defaults to prefer royalty-free AV1 over patent-encumbered HEVC --- .travis.yml | 16 +-- docs/README.md | 6 +- docs/api-output.md | 39 +++++-- docs/changelog.md | 10 ++ docs/install.md | 11 +- docs/search-index.json | 2 +- lib/constructor.js | 5 +- lib/output.js | 45 +++++--- package.json | 9 +- src/common.h | 6 +- src/pipeline.cc | 8 +- src/pipeline.h | 6 +- src/sharp.cc | 1 + ...frame12924_yuv420_10bpc_bt2020_pq_q50.avif | Bin 0 -> 17264 bytes test/fixtures/expected/tint-cmyk.jpg | Bin 26307 -> 26333 bytes test/fixtures/index.js | 1 + test/unit/avif.js | 84 ++++++++++++++ test/unit/heif.js | 105 ++++++++---------- test/unit/io.js | 1 - test/unit/stats.js | 24 ++-- 20 files changed, 254 insertions(+), 125 deletions(-) create mode 100644 test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif create mode 100644 test/unit/avif.js diff --git a/.travis.yml b/.travis.yml index 92a106e1..07d22418 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,43 +45,43 @@ jobs: install: sudo docker exec sharp bash -c "npm install --build-from-source --unsafe-perm" script: sudo docker exec sharp bash -c "npm test" - - name: "Linux x64 (Alpine 3.9, musl 1.1.20) - Node.js 10" + - name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 10" os: linux dist: bionic language: shell before_install: - - sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10.17.0-alpine3.9 # https://github.com/nodejs/docker-node/issues/1158 + - sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10-alpine3.11 - sudo docker exec sharp apk add build-base git python2 --update-cache install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm" script: sudo docker exec sharp sh -c "npm test" - - name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 12" + - name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 12" os: linux dist: bionic language: shell before_install: - - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine + - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12-alpine3.11 - sudo docker exec sharp apk add build-base git python2 --update-cache install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm" script: sudo docker exec sharp sh -c "npm test" - - name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 14" + - name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 14" os: linux dist: bionic language: shell before_install: - - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14.0-alpine + - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14-alpine3.11 - sudo docker exec sharp apk add build-base git python2 --update-cache install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm" script: sudo docker exec sharp sh -c "npm test" - - name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 15" + - name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 15" os: linux dist: bionic language: shell before_install: - sudo chown 0.0 ${PWD} - - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:15.0-alpine + - sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:15-alpine3.11 - sudo docker exec sharp apk add build-base git python2 --update-cache install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm" script: sudo docker exec sharp sh -c "npm test" diff --git a/docs/README.md b/docs/README.md index c4c1b15e..bf2e812a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 and WebP images of varying dimensions. +smaller, web-friendly JPEG, PNG, AVIF and WebP 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, TIFF, GIF and SVG images. +This module supports reading JPEG, PNG, WebP, AVIF, TIFF, GIF and SVG images. -Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data. +Output images can be in JPEG, PNG, WebP, AVIF and TIFF formats as well as uncompressed raw pixel data. Streams, Buffer objects and the filesystem can be used for input and output. diff --git a/docs/api-output.md b/docs/api-output.md index aa3ce439..4d0df918 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -299,23 +299,40 @@ sharp('input.svg') Returns **Sharp** -## heif +## avif -Use these HEIF options for output image. - -Support for HEIF (HEIC/AVIF) is experimental. -Do not use this in production systems. - -Requires a custom, globally-installed libvips compiled with support for libheif. - -Most versions of libheif support only the patent-encumbered HEVC compression format. +Use these AVIF options for output image. ### Parameters - `options` **[Object][6]?** output options - - `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`) - - `options.compression` **[boolean][7]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`) + - `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`) - `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`) + - `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`) + + +- Throws **[Error][4]** Invalid options + +Returns **Sharp** + +**Meta** + +- **since**: 0.27.0 + +## heif + +Use these HEIF options for output image. + +Support for patent-encumbered HEIC images requires the use of a +globally-installed libvips compiled with support for libheif, libde265 and x265. + +### Parameters + +- `options` **[Object][6]?** output options + - `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`) + - `options.compression` **[boolean][7]** compression format: av1, hevc (optional, default `'av1'`) + - `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`) + - `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`) - Throws **[Error][4]** Invalid options diff --git a/docs/changelog.md b/docs/changelog.md index 965a1e38..9f43c773 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,15 @@ # Changelog +## v0.27 - *avif* + +Requires libvips v8.10.5 + +### v0.27.0 - TBD + +* Add support for AVIF to prebuilt binaries. + +* Remove experimental status from `heif` output, defaults are now AVIF-centric. + ## v0.26 - *zoom* Requires libvips v8.10.0 diff --git a/docs/install.md b/docs/install.md index 525ee470..d744871c 100644 --- a/docs/install.md +++ b/docs/install.md @@ -20,21 +20,24 @@ Node.js v10+ on the most common platforms: * macOS x64 (>= 10.13) * Linux x64 (glibc >= 2.17, musl >= 1.1.24) * Linux ARM64 (glibc >= 2.29) -* Windows +* Windows x64 +* Windows x86 -A 7-8MB tarball containing libvips and its most commonly used dependencies +A ~9MB 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, TIFF, GIF (input) and SVG (input) image formats. +JPEG, PNG, WebP, AVIF, TIFF, GIF (input) and SVG (input) image formats. The following platforms have prebuilt libvips but not sharp: * Linux ARMv6 * Linux ARMv7 (glibc >= 2.28) +* Windows ARM64 The following platforms require compilation of both libvips and sharp from source: +* macOS ARM64 * Linux x86 * Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6) * Linux ARM64 (glibc <= 2.28, musl) @@ -69,7 +72,7 @@ The use of a globally-installed libvips is unsupported on Windows. This module will be compiled from source at `npm install` time when: * a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this), -* prebuilt binaries do not exist for the current platform and Node.js version, or +* prebuilt sharp binaries do not exist for the current platform, or * when the `npm install --build-from-source` flag is used. Building from source requires: diff --git a/docs/search-index.json b/docs/search-index.json index eaf9527d..e1f498bb 100644 --- a/docs/search-index.json +++ b/docs/search-index.json @@ -1 +1 @@ -[{"t":"Prerequisites","d":"Node.js v10.16.0","k":"prerequisites node","l":"/install#prerequisites"},{"t":"Prebuilt binaries","d":"Ready-compiled sharp and libvips binaries are provided for use with Node.js v10.16.0 on the most common platforms macOS x64 10.13 Linux x64","k":"prebuilt binaries ready compiled sharp libvips node common platforms macos linux","l":"/install#prebuilt-binaries"},{"t":"Common problems","d":"The architecture and platform of Node.js used for npm install must be the same as the architecture and platform of Node.js used at runtime.","k":"common problems architecture platform node npm install same runtime","l":"/install#common-problems"},{"t":"Custom libvips","d":"To use a custom, globally-installed version of libvips instead of the provided binaries, make sure it is at least the version listed under c","k":"custom libvips globally installed version instead binaries make sure least listed under","l":"/install#custom-libvips"},{"t":"Building from source","d":"This module will be compiled from source at npm install time when a globally-installed libvips is detected set the SHARP_IGNORE_GLOBAL_LIBVI","k":"building source module compiled npm install time globally installed libvips detected sharpignoregloballibvi","l":"/install#building-from-source"},{"t":"Custom prebuilt binaries","d":"This is an advanced approach that most people will not require. To install the prebuilt sharp binaries from a custom URL, set the sharp_bina","k":"custom prebuilt binaries advanced approach people require install sharp url sharpbina","l":"/install#custom-prebuilt-binaries"},{"t":"Chinese mirror","d":"Alibaba provide a mirror site based in China containing binaries for both sharp and libvips. To use this either set the following configurat","k":"chinese mirror alibaba site based china binaries both sharp libvips following configurat","l":"/install#chinese-mirror"},{"t":"FreeBSD","d":"The vips package must be installed before npm install is run. sh pkg install -y pkgconf vips sh cd /usr/ports/graphics/vips/ make install cl","k":"freebsd vips package installed npm install run pkg pkgconf usr ports graphics make","l":"/install#freebsd"},{"t":"Heroku","d":"Add the jemalloc buildpack to reduce the effects of memory fragmentation. Set NODE_MODULES_CACHE","k":"heroku add jemalloc buildpack reduce effects memory fragmentation nodemodulescache","l":"/install#heroku"},{"t":"AWS Lambda","d":"Set the Lambda runtime to nodejs12.x. The binaries in the node_modules directory of the deployment package must be for the Linux x64 platfor","k":"aws lambda runtime nodejs binaries nodemodules directory deployment package linux platfor","l":"/install#aws-lambda"},{"t":"Worker threads","d":"The main thread must call requiresharp before worker threads are created to ensure shared libraries remain loaded in memory until after all","k":"worker threads main thread call requiresharp created ensure shared libraries remain loaded memory until","l":"/install#worker-threads"},{"t":"Sharp","d":"Constructor factory to create an instance of sharp, to which further methods are chained.","k":"sharp constructor factory create instance further methods chained","l":"/api-constructor#sharp"},{"t":"clone","d":"Take a snapshot of the Sharp instance, returning a new instance. Cloned instances inherit the input of their parent instance. This allows mu","k":"clone snapshot sharp instance returning new cloned instances inherit input parent allows","l":"/api-constructor#clone"},{"t":"metadata","d":"Fast access to uncached image metadata without decoding any compressed image data. A Promise is returned when callback is not provided.","k":"metadata fast access uncached decoding compressed data promise returned callback","l":"/api-input#metadata"},{"t":"stats","d":"Access to pixel-derived image statistics for every channel in the image. A Promise is returned when callback is not provided.","k":"stats access pixel derived statistics every channel promise returned callback","l":"/api-input#stats"},{"t":"toFile","d":"Write output image data to a file.","k":"tofile write output data file","l":"/api-output#tofile"},{"t":"toBuffer","d":"Write output to a Buffer. JPEG, PNG, WebP, TIFF and RAW output are supported.","k":"tobuffer write output buffer jpeg png webp tiff raw","l":"/api-output#tobuffer"},{"t":"withMetadata","d":"Include all metadata EXIF, XMP, IPTC from the input image in the output image. This will also convert to and add a web-friendly sRGB ICC pro","k":"withmetadata include metadata exif xmp iptc input output also convert add web friendly srgb icc pro","l":"/api-output#withmetadata"},{"t":"toFormat","d":"Force output to a given format.","k":"toformat force output format","l":"/api-output#toformat"},{"t":"jpeg","d":"Use these JPEG options for output image.","k":"jpeg options output","l":"/api-output#jpeg"},{"t":"png","d":"Use these PNG options for output image.","k":"png options output","l":"/api-output#png"},{"t":"webp","d":"Use these WebP options for output image.","k":"webp options output","l":"/api-output#webp"},{"t":"gif","d":"Use these GIF options for output image.","k":"gif options output","l":"/api-output#gif"},{"t":"tiff","d":"Use these TIFF options for output image.","k":"tiff options output","l":"/api-output#tiff"},{"t":"heif","d":"Use these HEIF options for output image.","k":"heif options output","l":"/api-output#heif"},{"t":"raw","d":"Force output to be raw, uncompressed, 8-bit unsigned integer unit8 pixel data. Pixel ordering is left-to-right, top-to-bottom, without paddi","k":"raw force output uncompressed bit unsigned integer unit pixel data ordering left right top bottom paddi","l":"/api-output#raw"},{"t":"tile","d":"Use tile-based deep zoom image pyramid output. Set the format and options for tile images via the toFormat, jpeg, png or webp functions. Use","k":"tile based deep zoom pyramid output format options images via toformat jpeg png webp functions","l":"/api-output#tile"},{"t":"resize","d":"Resize image to width, height or width x height.","k":"resize width height","l":"/api-resize#resize"},{"t":"extend","d":"Extends/pads the edges of the image with the provided background colour. This operation will always occur after resizing and extraction, if","k":"extend extends pads edges background colour operation resizing extraction","l":"/api-resize#extend"},{"t":"extract","d":"Extract/crop a region of the image.","k":"extract crop region","l":"/api-resize#extract"},{"t":"trim","d":"Trim boring pixels from all edges that contain values similar to the top-left pixel. Images consisting entirely of a single colour will calc","k":"trim boring pixels edges contain similar top left pixel images consisting entirely single colour calc","l":"/api-resize#trim"},{"t":"composite","d":"Composite images over the processed resized, extracted etc. image.","k":"composite images processed resized extracted","l":"/api-composite#composite"},{"t":"rotate","d":"Rotate the output image by either an explicit angle or auto-orient based on the EXIF Orientation tag.","k":"rotate output explicit angle auto orient based exif orientation tag","l":"/api-operation#rotate"},{"t":"flip","d":"Flip the image about the vertical Y axis. This always occurs after rotation, if any. The use of flip implies the removal of the EXIF Orienta","k":"flip vertical axis rotation implies removal exif orienta","l":"/api-operation#flip"},{"t":"flop","d":"Flop the image about the horizontal X axis. This always occurs after rotation, if any. The use of flop implies the removal of the EXIF Orien","k":"flop horizontal axis rotation implies removal exif orien","l":"/api-operation#flop"},{"t":"affine","d":"Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.","k":"affine transform operation resizing extraction rotation","l":"/api-operation#affine"},{"t":"sharpen","d":"Sharpen the image. When used without parameters, performs a fast, mild sharpen of the output image. When a sigma is provided, performs a slo","k":"sharpen parameters fast mild output sigma slo","l":"/api-operation#sharpen"},{"t":"median","d":"Apply median filter. When used without parameters the default window is 3x3.","k":"median apply filter parameters window","l":"/api-operation#median"},{"t":"blur","d":"Blur the image. When used without parameters, performs a fast, mild blur of the output image. When a sigma is provided, performs a slower, m","k":"blur parameters fast mild output sigma slower","l":"/api-operation#blur"},{"t":"flatten","d":"Merge alpha transparency channel, if any, with a background.","k":"flatten merge alpha transparency channel background","l":"/api-operation#flatten"},{"t":"gamma","d":"Apply a gamma correction by reducing the encoding darken pre-resize at a factor of 1/gamma then increasing the encoding brighten post-resize","k":"gamma apply correction reducing encoding darken pre resize factor then increasing brighten post","l":"/api-operation#gamma"},{"t":"negate","d":"Produce the negative of the image.","k":"negate produce negative","l":"/api-operation#negate"},{"t":"normalise","d":"Enhance output image contrast by stretching its luminance to cover the full dynamic range.","k":"normalise enhance output contrast stretching luminance cover full dynamic range","l":"/api-operation#normalise"},{"t":"normalize","d":"Alternative spelling of normalise.","k":"normalize normalise","l":"/api-operation#normalize"},{"t":"convolve","d":"Convolve the image with the specified kernel.","k":"convolve specified kernel","l":"/api-operation#convolve"},{"t":"threshold","d":"Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.","k":"threshold pixel greather than equal otherwise","l":"/api-operation#threshold"},{"t":"boolean","d":"Perform a bitwise boolean operation with operand image.","k":"boolean bitwise operation operand","l":"/api-operation#boolean"},{"t":"linear","d":"Apply the linear formula a input b to the image levels adjustment","k":"linear apply formula input levels adjustment","l":"/api-operation#linear"},{"t":"recomb","d":"Recomb the image with the specified matrix.","k":"recomb specified matrix","l":"/api-operation#recomb"},{"t":"modulate","d":"Transforms the image using brightness, saturation and hue rotation.","k":"modulate transforms brightness saturation hue rotation","l":"/api-operation#modulate"},{"t":"removeAlpha","d":"Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.","k":"removealpha remove alpha channel","l":"/api-channel#removealpha"},{"t":"ensureAlpha","d":"Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.","k":"ensurealpha ensure alpha channel missing added fully opaque","l":"/api-channel#ensurealpha"},{"t":"extractChannel","d":"Extract a single channel from a multi-channel image.","k":"extractchannel extract single channel multi","l":"/api-channel#extractchannel"},{"t":"joinChannel","d":"Join one or more channels to the image. The meaning of the added channels depends on the output colourspace, set with toColourspace. By defa","k":"joinchannel join one channels meaning added depends output colourspace tocolourspace defa","l":"/api-channel#joinchannel"},{"t":"bandbool","d":"Perform a bitwise boolean operation on all input image channels bands to produce a single channel output image.","k":"bandbool bitwise boolean operation input channels bands produce single channel output","l":"/api-channel#bandbool"},{"t":"tint","d":"Tint the image using the provided chroma while preserving the image luminance. An alpha channel may be present and will be unchanged by the","k":"tint chroma preserving luminance alpha channel present unchanged","l":"/api-colour#tint"},{"t":"greyscale","d":"Convert to 8-bit greyscale 256 shades of grey. This is a linear operation. If the input image is in a non-linear colour space such as sRGB,","k":"greyscale convert bit shades grey linear operation input colour space such srgb","l":"/api-colour#greyscale"},{"t":"grayscale","d":"Alternative spelling of greyscale.","k":"grayscale greyscale","l":"/api-colour#grayscale"},{"t":"toColourspace","d":"Set the output colourspace. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.","k":"tocolourspace output colourspace web friendly srgb additional channels interpreted alpha","l":"/api-colour#tocolourspace"},{"t":"toColorspace","d":"Alternative spelling of toColourspace.","k":"tocolorspace tocolourspace","l":"/api-colour#tocolorspace"},{"t":"format","d":"An Object containing nested boolean values representing the available input and output formats/methods.","k":"format object nested boolean representing available input output formats methods","l":"/api-utility#format"},{"t":"interpolators","d":"An Object containing the available interpolators and their proper values","k":"interpolators object available proper","l":"/api-utility#interpolators"},{"t":"versions","d":"An Object containing the version numbers of libvips and its dependencies.","k":"versions object version numbers libvips dependencies","l":"/api-utility#versions"},{"t":"cache","d":"Gets or, when options are provided, sets the limits of _libvips_ operation cache. Existing entries in the cache will be trimmed after any ch","k":"cache options limits libvips operation existing entries trimmed","l":"/api-utility#cache"},{"t":"concurrency","d":"Gets or, when a concurrency is provided, sets the number of threads _libvips_ should create to process each image. The default value is the","k":"concurrency number threads libvips create process","l":"/api-utility#concurrency"},{"t":"queue","d":"An EventEmitter that emits a change event when a task is either","k":"queue eventemitter emits change event task","l":"/api-utility#queue"},{"t":"counters","d":"Provides access to internal task counters.","k":"counters provides access internal task","l":"/api-utility#counters"},{"t":"simd","d":"Get and set use of SIMD vector unit instructions. Requires libvips to have been compiled with liborc support.","k":"simd vector unit instructions requires libvips compiled liborc","l":"/api-utility#simd"}] \ No newline at end of file +[{"t":"Prerequisites","d":"Node.js v10","k":"prerequisites node","l":"/install#prerequisites"},{"t":"Prebuilt binaries","d":"Ready-compiled sharp and libvips binaries are provided for use with Node.js v10 on the most common platforms macOS x64 10.13 Linux x64 glibc","k":"prebuilt binaries ready compiled sharp libvips node common platforms macos linux glibc","l":"/install#prebuilt-binaries"},{"t":"Common problems","d":"The architecture and platform of Node.js used for npm install must be the same as the architecture and platform of Node.js used at runtime.","k":"common problems architecture platform node npm install same runtime","l":"/install#common-problems"},{"t":"Custom libvips","d":"To use a custom, globally-installed version of libvips instead of the provided binaries, make sure it is at least the version listed under c","k":"custom libvips globally installed version instead binaries make sure least listed under","l":"/install#custom-libvips"},{"t":"Building from source","d":"This module will be compiled from source at npm install time when a globally-installed libvips is detected set the SHARP_IGNORE_GLOBAL_LIBVI","k":"building source module compiled npm install time globally installed libvips detected sharpignoregloballibvi","l":"/install#building-from-source"},{"t":"Custom prebuilt binaries","d":"This is an advanced approach that most people will not require. To install the prebuilt sharp binaries from a custom URL, set the sharp_bina","k":"custom prebuilt binaries advanced approach people require install sharp url sharpbina","l":"/install#custom-prebuilt-binaries"},{"t":"Chinese mirror","d":"Alibaba provide a mirror site based in China containing binaries for both sharp and libvips. To use this either set the following configurat","k":"chinese mirror alibaba site based china binaries both sharp libvips following configurat","l":"/install#chinese-mirror"},{"t":"FreeBSD","d":"The vips package must be installed before npm install is run. sh pkg install -y pkgconf vips sh cd /usr/ports/graphics/vips/ make install cl","k":"freebsd vips package installed npm install run pkg pkgconf usr ports graphics make","l":"/install#freebsd"},{"t":"Heroku","d":"Add the jemalloc buildpack to reduce the effects of memory fragmentation. Set NODE_MODULES_CACHE","k":"heroku add jemalloc buildpack reduce effects memory fragmentation nodemodulescache","l":"/install#heroku"},{"t":"AWS Lambda","d":"Set the Lambda runtime to nodejs12.x. The binaries in the node_modules directory of the deployment package must be for the Linux x64 platfor","k":"aws lambda runtime nodejs binaries nodemodules directory deployment package linux platfor","l":"/install#aws-lambda"},{"t":"Worker threads","d":"The main thread must call requiresharp before worker threads are created to ensure shared libraries remain loaded in memory until after all","k":"worker threads main thread call requiresharp created ensure shared libraries remain loaded memory until","l":"/install#worker-threads"},{"t":"Sharp","d":"Constructor factory to create an instance of sharp, to which further methods are chained.","k":"sharp constructor factory create instance further methods chained","l":"/api-constructor#sharp"},{"t":"clone","d":"Take a snapshot of the Sharp instance, returning a new instance. Cloned instances inherit the input of their parent instance. This allows mu","k":"clone snapshot sharp instance returning new cloned instances inherit input parent allows","l":"/api-constructor#clone"},{"t":"metadata","d":"Fast access to uncached image metadata without decoding any compressed image data. A Promise is returned when callback is not provided.","k":"metadata fast access uncached decoding compressed data promise returned callback","l":"/api-input#metadata"},{"t":"stats","d":"Access to pixel-derived image statistics for every channel in the image. A Promise is returned when callback is not provided.","k":"stats access pixel derived statistics every channel promise returned callback","l":"/api-input#stats"},{"t":"toFile","d":"Write output image data to a file.","k":"tofile write output data file","l":"/api-output#tofile"},{"t":"toBuffer","d":"Write output to a Buffer. JPEG, PNG, WebP, TIFF and RAW output are supported.","k":"tobuffer write output buffer jpeg png webp tiff raw","l":"/api-output#tobuffer"},{"t":"withMetadata","d":"Include all metadata EXIF, XMP, IPTC from the input image in the output image. This will also convert to and add a web-friendly sRGB ICC pro","k":"withmetadata include metadata exif xmp iptc input output also convert add web friendly srgb icc pro","l":"/api-output#withmetadata"},{"t":"toFormat","d":"Force output to a given format.","k":"toformat force output format","l":"/api-output#toformat"},{"t":"jpeg","d":"Use these JPEG options for output image.","k":"jpeg options output","l":"/api-output#jpeg"},{"t":"png","d":"Use these PNG options for output image.","k":"png options output","l":"/api-output#png"},{"t":"webp","d":"Use these WebP options for output image.","k":"webp options output","l":"/api-output#webp"},{"t":"gif","d":"Use these GIF options for output image.","k":"gif options output","l":"/api-output#gif"},{"t":"tiff","d":"Use these TIFF options for output image.","k":"tiff options output","l":"/api-output#tiff"},{"t":"avif","d":"Use these AVIF options for output image.","k":"avif options output","l":"/api-output#avif"},{"t":"heif","d":"Use these HEIF options for output image.","k":"heif options output","l":"/api-output#heif"},{"t":"raw","d":"Force output to be raw, uncompressed, 8-bit unsigned integer unit8 pixel data. Pixel ordering is left-to-right, top-to-bottom, without paddi","k":"raw force output uncompressed bit unsigned integer unit pixel data ordering left right top bottom paddi","l":"/api-output#raw"},{"t":"tile","d":"Use tile-based deep zoom image pyramid output. Set the format and options for tile images via the toFormat, jpeg, png or webp functions. Use","k":"tile based deep zoom pyramid output format options images via toformat jpeg png webp functions","l":"/api-output#tile"},{"t":"resize","d":"Resize image to width, height or width x height.","k":"resize width height","l":"/api-resize#resize"},{"t":"extend","d":"Extends/pads the edges of the image with the provided background colour. This operation will always occur after resizing and extraction, if","k":"extend extends pads edges background colour operation resizing extraction","l":"/api-resize#extend"},{"t":"extract","d":"Extract/crop a region of the image.","k":"extract crop region","l":"/api-resize#extract"},{"t":"trim","d":"Trim boring pixels from all edges that contain values similar to the top-left pixel. Images consisting entirely of a single colour will calc","k":"trim boring pixels edges contain similar top left pixel images consisting entirely single colour calc","l":"/api-resize#trim"},{"t":"composite","d":"Composite images over the processed resized, extracted etc. image.","k":"composite images processed resized extracted","l":"/api-composite#composite"},{"t":"rotate","d":"Rotate the output image by either an explicit angle or auto-orient based on the EXIF Orientation tag.","k":"rotate output explicit angle auto orient based exif orientation tag","l":"/api-operation#rotate"},{"t":"flip","d":"Flip the image about the vertical Y axis. This always occurs after rotation, if any. The use of flip implies the removal of the EXIF Orienta","k":"flip vertical axis rotation implies removal exif orienta","l":"/api-operation#flip"},{"t":"flop","d":"Flop the image about the horizontal X axis. This always occurs after rotation, if any. The use of flop implies the removal of the EXIF Orien","k":"flop horizontal axis rotation implies removal exif orien","l":"/api-operation#flop"},{"t":"affine","d":"Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.","k":"affine transform operation resizing extraction rotation","l":"/api-operation#affine"},{"t":"sharpen","d":"Sharpen the image. When used without parameters, performs a fast, mild sharpen of the output image. When a sigma is provided, performs a slo","k":"sharpen parameters fast mild output sigma slo","l":"/api-operation#sharpen"},{"t":"median","d":"Apply median filter. When used without parameters the default window is 3x3.","k":"median apply filter parameters window","l":"/api-operation#median"},{"t":"blur","d":"Blur the image. When used without parameters, performs a fast, mild blur of the output image. When a sigma is provided, performs a slower, m","k":"blur parameters fast mild output sigma slower","l":"/api-operation#blur"},{"t":"flatten","d":"Merge alpha transparency channel, if any, with a background.","k":"flatten merge alpha transparency channel background","l":"/api-operation#flatten"},{"t":"gamma","d":"Apply a gamma correction by reducing the encoding darken pre-resize at a factor of 1/gamma then increasing the encoding brighten post-resize","k":"gamma apply correction reducing encoding darken pre resize factor then increasing brighten post","l":"/api-operation#gamma"},{"t":"negate","d":"Produce the negative of the image.","k":"negate produce negative","l":"/api-operation#negate"},{"t":"normalise","d":"Enhance output image contrast by stretching its luminance to cover the full dynamic range.","k":"normalise enhance output contrast stretching luminance cover full dynamic range","l":"/api-operation#normalise"},{"t":"normalize","d":"Alternative spelling of normalise.","k":"normalize normalise","l":"/api-operation#normalize"},{"t":"convolve","d":"Convolve the image with the specified kernel.","k":"convolve specified kernel","l":"/api-operation#convolve"},{"t":"threshold","d":"Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.","k":"threshold pixel greather than equal otherwise","l":"/api-operation#threshold"},{"t":"boolean","d":"Perform a bitwise boolean operation with operand image.","k":"boolean bitwise operation operand","l":"/api-operation#boolean"},{"t":"linear","d":"Apply the linear formula a input b to the image levels adjustment","k":"linear apply formula input levels adjustment","l":"/api-operation#linear"},{"t":"recomb","d":"Recomb the image with the specified matrix.","k":"recomb specified matrix","l":"/api-operation#recomb"},{"t":"modulate","d":"Transforms the image using brightness, saturation and hue rotation.","k":"modulate transforms brightness saturation hue rotation","l":"/api-operation#modulate"},{"t":"removeAlpha","d":"Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.","k":"removealpha remove alpha channel","l":"/api-channel#removealpha"},{"t":"ensureAlpha","d":"Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.","k":"ensurealpha ensure alpha channel missing added fully opaque","l":"/api-channel#ensurealpha"},{"t":"extractChannel","d":"Extract a single channel from a multi-channel image.","k":"extractchannel extract single channel multi","l":"/api-channel#extractchannel"},{"t":"joinChannel","d":"Join one or more channels to the image. The meaning of the added channels depends on the output colourspace, set with toColourspace. By defa","k":"joinchannel join one channels meaning added depends output colourspace tocolourspace defa","l":"/api-channel#joinchannel"},{"t":"bandbool","d":"Perform a bitwise boolean operation on all input image channels bands to produce a single channel output image.","k":"bandbool bitwise boolean operation input channels bands produce single channel output","l":"/api-channel#bandbool"},{"t":"tint","d":"Tint the image using the provided chroma while preserving the image luminance. An alpha channel may be present and will be unchanged by the","k":"tint chroma preserving luminance alpha channel present unchanged","l":"/api-colour#tint"},{"t":"greyscale","d":"Convert to 8-bit greyscale 256 shades of grey. This is a linear operation. If the input image is in a non-linear colour space such as sRGB,","k":"greyscale convert bit shades grey linear operation input colour space such srgb","l":"/api-colour#greyscale"},{"t":"grayscale","d":"Alternative spelling of greyscale.","k":"grayscale greyscale","l":"/api-colour#grayscale"},{"t":"toColourspace","d":"Set the output colourspace. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.","k":"tocolourspace output colourspace web friendly srgb additional channels interpreted alpha","l":"/api-colour#tocolourspace"},{"t":"toColorspace","d":"Alternative spelling of toColourspace.","k":"tocolorspace tocolourspace","l":"/api-colour#tocolorspace"},{"t":"format","d":"An Object containing nested boolean values representing the available input and output formats/methods.","k":"format object nested boolean representing available input output formats methods","l":"/api-utility#format"},{"t":"interpolators","d":"An Object containing the available interpolators and their proper values","k":"interpolators object available proper","l":"/api-utility#interpolators"},{"t":"versions","d":"An Object containing the version numbers of libvips and its dependencies.","k":"versions object version numbers libvips dependencies","l":"/api-utility#versions"},{"t":"cache","d":"Gets or, when options are provided, sets the limits of _libvips_ operation cache. Existing entries in the cache will be trimmed after any ch","k":"cache options limits libvips operation existing entries trimmed","l":"/api-utility#cache"},{"t":"concurrency","d":"Gets or, when a concurrency is provided, sets the number of threads _libvips_ should create to process each image. The default value is the","k":"concurrency number threads libvips create process","l":"/api-utility#concurrency"},{"t":"queue","d":"An EventEmitter that emits a change event when a task is either","k":"queue eventemitter emits change event task","l":"/api-utility#queue"},{"t":"counters","d":"Provides access to internal task counters.","k":"counters provides access internal task","l":"/api-utility#counters"},{"t":"simd","d":"Get and set use of SIMD vector unit instructions. Requires libvips to have been compiled with liborc support.","k":"simd vector unit instructions requires libvips compiled liborc","l":"/api-utility#simd"}] \ No newline at end of file diff --git a/lib/constructor.js b/lib/constructor.js index 547fdef9..22e702d8 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -233,9 +233,10 @@ const Sharp = function (input, options) { tiffTileWidth: 256, tiffXres: 1.0, tiffYres: 1.0, - heifQuality: 80, + heifQuality: 50, heifLossless: false, - heifCompression: 'hevc', + heifCompression: 'av1', + heifSpeed: 5, tileSize: 256, tileOverlap: 0, tileContainer: 'fs', diff --git a/lib/output.js b/lib/output.js index 3553ae9f..222ae3e4 100644 --- a/lib/output.js +++ b/lib/output.js @@ -6,6 +6,7 @@ const sharp = require('../build/Release/sharp.node'); const formats = new Map([ ['heic', 'heif'], ['heif', 'heif'], + ['avif', 'avif'], ['jpeg', 'jpeg'], ['jpg', 'jpeg'], ['png', 'png'], @@ -556,29 +557,39 @@ function tiff (options) { return this._updateFormatOut('tiff', options); } +/** + * Use these AVIF options for output image. + * + * @since 0.27.0 + * + * @param {Object} [options] - output options + * @param {number} [options.quality=50] - quality, integer 1-100 + * @param {boolean} [options.lossless=false] - use lossless compression + * @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) + * @returns {Sharp} + * @throws {Error} Invalid options + */ +function avif (options) { + return this.heif({ ...options, compression: 'av1' }); +} + /** * Use these HEIF options for output image. * - * Support for HEIF (HEIC/AVIF) is experimental. - * Do not use this in production systems. - * - * Requires a custom, globally-installed libvips compiled with support for libheif. - * - * Most versions of libheif support only the patent-encumbered HEVC compression format. + * Support for patent-encumbered HEIC images requires the use of a + * globally-installed libvips compiled with support for libheif, libde265 and x265. * * @since 0.23.0 * * @param {Object} [options] - output options - * @param {number} [options.quality=80] - quality, integer 1-100 - * @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1 + * @param {number} [options.quality=50] - quality, integer 1-100 + * @param {boolean} [options.compression='av1'] - compression format: av1, hevc * @param {boolean} [options.lossless=false] - use lossless compression + * @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) * @returns {Sharp} * @throws {Error} Invalid options */ function heif (options) { - if (!this.constructor.format.heif.output.buffer) { - throw new Error('The heif operation requires libvips to have been installed with support for libheif'); - } if (is.object(options)) { if (is.defined(options.quality)) { if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) { @@ -595,10 +606,17 @@ function heif (options) { } } if (is.defined(options.compression)) { - if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) { + if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) { this.options.heifCompression = options.compression; } else { - throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression); + throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression); + } + } + if (is.defined(options.speed)) { + if (is.integer(options.speed) && is.inRange(options.speed, 0, 8)) { + this.options.heifSpeed = options.speed; + } else { + throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed); } } } @@ -891,6 +909,7 @@ module.exports = function (Sharp) { png, webp, tiff, + avif, heif, gif, raw, diff --git a/package.json b/package.json index c4951a30..001014a1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sharp", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images", - "version": "0.26.3", + "version": "0.27.0-alpha1", "author": "Lovell Fuller ", "homepage": "https://github.com/lovell/sharp", "contributors": [ @@ -100,6 +100,7 @@ "jpeg", "png", "webp", + "avif", "tiff", "gif", "svg", @@ -116,10 +117,10 @@ "array-flatten": "^3.0.0", "color": "^3.1.3", "detect-libc": "^1.0.3", - "node-addon-api": "^3.0.2", + "node-addon-api": "^3.1.0", "npmlog": "^4.1.2", "prebuild-install": "^6.0.0", - "semver": "^7.3.2", + "semver": "^7.3.4", "simple-get": "^4.0.0", "tar-fs": "^2.1.1", "tunnel-agent": "^0.6.0" @@ -141,7 +142,7 @@ }, "license": "Apache-2.0", "config": { - "libvips": "8.10.0", + "libvips": "8.10.5-alpha2", "runtime": "napi", "target": 3 }, diff --git a/src/common.h b/src/common.h index ffc54afc..c0dbca71 100644 --- a/src/common.h +++ b/src/common.h @@ -24,8 +24,10 @@ // Verify platform and compiler compatibility -#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10)) -#error "libvips version 8.10.0+ is required - please see https://sharp.pixelplumbing.com/install" +#if (VIPS_MAJOR_VERSION < 8) || \ + (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \ + (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 5) +#error "libvips version 8.10.5+ is required - please see https://sharp.pixelplumbing.com/install" #endif #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) diff --git a/src/pipeline.cc b/src/pipeline.cc index 883ebbe2..55af804c 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -837,6 +837,7 @@ class PipelineWorker : public Napi::AsyncWorker { ->set("strip", !baton->withMetadata) ->set("compression", baton->heifCompression) ->set("Q", baton->heifQuality) + ->set("speed", baton->heifSpeed) ->set("lossless", baton->heifLossless))); baton->bufferOut = static_cast(area->data); baton->bufferOutLength = area->length; @@ -885,7 +886,7 @@ class PipelineWorker : public Napi::AsyncWorker { bool const isV = sharp::IsV(baton->fileOut); bool const mightMatchInput = baton->formatOut == "input"; bool const willMatchInput = mightMatchInput && - !(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV); + !(isJpeg || isPng || isWebp || isGif || isTiff || isHeif || isDz || isDzZip || isV); if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) || (willMatchInput && inputImageType == sharp::ImageType::JPEG)) { @@ -966,13 +967,11 @@ class PipelineWorker : public Napi::AsyncWorker { } else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) || (willMatchInput && inputImageType == sharp::ImageType::HEIF)) { // Write HEIF to file - if (sharp::IsAvif(baton->fileOut)) { - baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1; - } image.heifsave(const_cast(baton->fileOut.data()), VImage::option() ->set("strip", !baton->withMetadata) ->set("Q", baton->heifQuality) ->set("compression", baton->heifCompression) + ->set("speed", baton->heifSpeed) ->set("lossless", baton->heifLossless)); baton->formatOut = "heif"; } else if (baton->formatOut == "dz" || isDz || isDzZip) { @@ -1395,6 +1394,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) { baton->heifCompression = static_cast( vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION, sharp::AttrAsStr(options, "heifCompression").data())); + baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed"); // Animated output if (sharp::HasAttr(options, "pageHeight")) { diff --git a/src/pipeline.h b/src/pipeline.h index bc6fe5c7..1ef5b05e 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -159,6 +159,7 @@ struct PipelineBaton { double tiffYres; int heifQuality; VipsForeignHeifCompression heifCompression; + int heifSpeed; bool heifLossless; std::string err; bool withMetadata; @@ -276,8 +277,9 @@ struct PipelineBaton { tiffTileWidth(256), tiffXres(1.0), tiffYres(1.0), - heifQuality(80), - heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_HEVC), + heifQuality(50), + heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1), + heifSpeed(5), heifLossless(false), withMetadata(false), withMetadataOrientation(-1), diff --git a/src/sharp.cc b/src/sharp.cc index ae15648b..04c8a577 100644 --- a/src/sharp.cc +++ b/src/sharp.cc @@ -22,6 +22,7 @@ #include "stats.h" static void* sharp_vips_init(void*) { + g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE); vips_init("sharp"); return nullptr; } diff --git a/test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif b/test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif new file mode 100644 index 0000000000000000000000000000000000000000..487d801352215fd2ae3ed8ebec5b2f7f6bb675bd GIT binary patch literal 17264 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

y4 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�hKhH^xXcqO~^c1_Fe|}oj?1rtnij}X|ri* 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 WmA(+h4_4)ye%REvxvXxka{t*#KoS7} delta 26062 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 Z`D@DG2xAAUa?QVNYFpgaH&;1-|Jf{NFyH_H diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 28d0293e..aa8d9393 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -109,6 +109,7 @@ module.exports = { inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg inputSvgSmallViewBox: getPath('circle.svg'), inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg + inputAvif: getPath('cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif'), // CC by-nc-nd https://github.com/AOMediaCodec/av1-avif/tree/master/testFiles/Netflix inputJPGBig: getPath('flowers.jpeg'), diff --git a/test/unit/avif.js b/test/unit/avif.js new file mode 100644 index 00000000..859598d8 --- /dev/null +++ b/test/unit/avif.js @@ -0,0 +1,84 @@ +'use strict'; + +const assert = require('assert'); + +const sharp = require('../../'); +const { inputAvif, inputJpg } = require('../fixtures'); + +describe('AVIF', () => { + it('called without options does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().avif(); + }); + }); + + it('can passthrough AVIF', async () => { + const data = await sharp(inputAvif) + .resize(32) + .toBuffer(); + const metadata = await sharp(data) + .metadata(); + const { size, ...metadataWithoutSize } = metadata; + assert.deepStrictEqual(metadataWithoutSize, { + channels: 3, + depth: 'uchar', + format: 'heif', + hasAlpha: false, + hasProfile: false, + height: 12, + isProgressive: false, + pageHeight: 12, + pagePrimary: 0, + pages: 1, + space: 'srgb', + width: 32 + }); + }); + + it('can convert AVIF to JPEG', async () => { + const data = await sharp(inputAvif) + .resize(32) + .jpeg() + .toBuffer(); + const metadata = await sharp(data) + .metadata(); + const { size, ...metadataWithoutSize } = metadata; + assert.deepStrictEqual(metadataWithoutSize, { + channels: 3, + chromaSubsampling: '4:2:0', + density: 72, + depth: 'uchar', + format: 'jpeg', + hasAlpha: false, + hasProfile: false, + height: 13, + isProgressive: false, + space: 'srgb', + width: 32 + }); + }); + + it('can convert JPEG to AVIF', async () => { + const data = await sharp(inputJpg) + .resize(32) + .avif() + .toBuffer(); + const metadata = await sharp(data) + .metadata(); + const { size, ...metadataWithoutSize } = metadata; + assert.deepStrictEqual(metadataWithoutSize, { + channels: 3, + depth: 'uchar', + format: 'heif', + hasAlpha: false, + hasProfile: false, + height: 26, + isProgressive: false, + pageHeight: 26, + pagePrimary: 0, + pages: 1, + space: 'srgb', + width: 32 + }); + }); +}); diff --git a/test/unit/heif.js b/test/unit/heif.js index 60e6ab2b..c67b89b8 100644 --- a/test/unit/heif.js +++ b/test/unit/heif.js @@ -4,76 +4,65 @@ const assert = require('assert'); const sharp = require('../../'); -const formatHeifOutputBuffer = sharp.format.heif.output.buffer; - -describe('HEIF (experimental)', () => { - describe('Stubbed without support for HEIF', () => { - before(() => { - sharp.format.heif.output.buffer = false; - }); - after(() => { - sharp.format.heif.output.buffer = formatHeifOutputBuffer; - }); - - it('should throw an error', () => { - assert.throws(() => { - sharp().heif(); - }); +describe('HEIF', () => { + it('called without options does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().heif(); }); }); - - describe('Stubbed with support for HEIF', () => { - before(() => { - sharp.format.heif.output.buffer = true; + it('valid quality does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().heif({ quality: 80 }); }); - after(() => { - sharp.format.heif.output.buffer = formatHeifOutputBuffer; + }); + it('invalid quality should throw an error', () => { + assert.throws(() => { + sharp().heif({ quality: 101 }); }); - - it('called without options does not throw an error', () => { - assert.doesNotThrow(() => { - sharp().heif(); - }); + }); + it('non-numeric quality should throw an error', () => { + assert.throws(() => { + sharp().heif({ quality: 'fail' }); }); - it('valid quality does not throw an error', () => { - assert.doesNotThrow(() => { - sharp().heif({ quality: 50 }); - }); + }); + it('valid lossless does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().heif({ lossless: true }); }); - it('invalid quality should throw an error', () => { - assert.throws(() => { - sharp().heif({ quality: 101 }); - }); + }); + it('non-boolean lossless should throw an error', () => { + assert.throws(() => { + sharp().heif({ lossless: 'fail' }); }); - it('non-numeric quality should throw an error', () => { - assert.throws(() => { - sharp().heif({ quality: 'fail' }); - }); + }); + it('valid compression does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().heif({ compression: 'hevc' }); }); - it('valid lossless does not throw an error', () => { - assert.doesNotThrow(() => { - sharp().heif({ lossless: true }); - }); + }); + it('unknown compression should throw an error', () => { + assert.throws(() => { + sharp().heif({ compression: 'fail' }); }); - it('non-boolean lossless should throw an error', () => { - assert.throws(() => { - sharp().heif({ lossless: 'fail' }); - }); + }); + it('invalid compression should throw an error', () => { + assert.throws(() => { + sharp().heif({ compression: 1 }); }); - it('valid compression does not throw an error', () => { - assert.doesNotThrow(() => { - sharp().heif({ compression: 'avc' }); - }); + }); + it('valid speed does not throw an error', () => { + assert.doesNotThrow(() => { + sharp().heif({ speed: 6 }); }); - it('unknown compression should throw an error', () => { - assert.throws(() => { - sharp().heif({ compression: 'fail' }); - }); + }); + it('out of range speed should throw an error', () => { + assert.throws(() => { + sharp().heif({ speed: 9 }); }); - it('invalid compression should throw an error', () => { - assert.throws(() => { - sharp().heif({ compression: 1 }); - }); + }); + it('invalid speed should throw an error', () => { + assert.throws(() => { + sharp().heif({ compression: 'fail' }); }); }); }); diff --git a/test/unit/io.js b/test/unit/io.js index a0d798a6..eb723e40 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -302,7 +302,6 @@ describe('Input/output', function () { }); it('Fail when input is empty Buffer', function (done) { - if (sharp.format.magick.input.buffer) return this.skip(); // can be removed with libvips 8.10.1+ sharp(Buffer.alloc(0)).toBuffer().then(function () { assert(false); done(); diff --git a/test/unit/stats.js b/test/unit/stats.js index 0b828e6c..342b27dc 100644 --- a/test/unit/stats.js +++ b/test/unit/stats.js @@ -279,10 +279,10 @@ describe('Image Stats', function () { // red channel assert.strictEqual(0, stats.channels[0].min); assert.strictEqual(true, isInRange(stats.channels[0].max, 254, 255)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 82506996)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11213984832)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 104.36947963892487)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.379896254993135)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 83291370)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11379783198)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 105.36169496842616)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.39412151419967)); assert.strictEqual(true, isInteger(stats.channels[0].minX)); assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024)); assert.strictEqual(true, isInteger(stats.channels[0].minY)); @@ -295,10 +295,10 @@ describe('Image Stats', function () { // green channel assert.strictEqual(0, stats.channels[1].min); assert.strictEqual(true, isInRange(stats.channels[1].max, 254, 255)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120089056)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20533721114)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 151.90993361398964)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.83370206587037)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120877425)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20774687595)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 152.9072025279307)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.84143349689916)); assert.strictEqual(true, isInteger(stats.channels[1].minX)); assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024)); assert.strictEqual(true, isInteger(stats.channels[1].minY)); @@ -311,10 +311,10 @@ describe('Image Stats', function () { // blue channel assert.strictEqual(0, stats.channels[2].min); assert.strictEqual(true, isInRange(stats.channels[2].max, 254, 255)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138153653)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28172033081)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 174.76123932359133)); - assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.38276338513747)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138938859)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28449125593)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 175.75450711423252)); + assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.39929031070358)); assert.strictEqual(true, isInteger(stats.channels[2].minX)); assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024)); assert.strictEqual(true, isInteger(stats.channels[2].minY));