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 00000000..487d8013 Binary files /dev/null and b/test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif differ diff --git a/test/fixtures/expected/tint-cmyk.jpg b/test/fixtures/expected/tint-cmyk.jpg index 715898a7..436bc667 100644 Binary files a/test/fixtures/expected/tint-cmyk.jpg and b/test/fixtures/expected/tint-cmyk.jpg differ 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));