mirror of
https://github.com/lovell/sharp.git
synced 2025-07-14 04:40:13 +02:00
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
This commit is contained in:
parent
a0d89ed514
commit
103ec0d58f
16
.travis.yml
16
.travis.yml
@ -45,43 +45,43 @@ jobs:
|
|||||||
install: sudo docker exec sharp bash -c "npm install --build-from-source --unsafe-perm"
|
install: sudo docker exec sharp bash -c "npm install --build-from-source --unsafe-perm"
|
||||||
script: sudo docker exec sharp bash -c "npm test"
|
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
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
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
|
- 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"
|
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
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
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
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
|
- 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"
|
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
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
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
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
|
- 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"
|
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
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
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- sudo chown 0.0 ${PWD}
|
- 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
|
- 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"
|
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The typical use case for this high speed Node.js module
|
The typical use case for this high speed Node.js module
|
||||||
is to convert large images in common formats to
|
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
|
Resizing an image is typically 4x-5x faster than using the
|
||||||
quickest ImageMagick and GraphicsMagick settings
|
quickest ImageMagick and GraphicsMagick settings
|
||||||
@ -21,9 +21,9 @@ do not require any additional install or runtime dependencies.
|
|||||||
|
|
||||||
### Formats
|
### 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.
|
Streams, Buffer objects and the filesystem can be used for input and output.
|
||||||
|
|
||||||
|
@ -299,23 +299,40 @@ sharp('input.svg')
|
|||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
## heif
|
## avif
|
||||||
|
|
||||||
Use these HEIF options for output image.
|
Use these AVIF 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.
|
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
- `options` **[Object][6]?** output options
|
- `options` **[Object][6]?** output options
|
||||||
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
|
||||||
- `options.compression` **[boolean][7]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`)
|
|
||||||
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
|
- `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
|
- Throws **[Error][4]** Invalid options
|
||||||
|
@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# 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*
|
## v0.26 - *zoom*
|
||||||
|
|
||||||
Requires libvips v8.10.0
|
Requires libvips v8.10.0
|
||||||
|
@ -20,21 +20,24 @@ Node.js v10+ on the most common platforms:
|
|||||||
* macOS x64 (>= 10.13)
|
* macOS x64 (>= 10.13)
|
||||||
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
|
||||||
* Linux ARM64 (glibc >= 2.29)
|
* 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`.
|
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
|
||||||
|
|
||||||
This provides support for the
|
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:
|
The following platforms have prebuilt libvips but not sharp:
|
||||||
|
|
||||||
* Linux ARMv6
|
* Linux ARMv6
|
||||||
* Linux ARMv7 (glibc >= 2.28)
|
* Linux ARMv7 (glibc >= 2.28)
|
||||||
|
* Windows ARM64
|
||||||
|
|
||||||
The following platforms require compilation of both libvips and sharp from source:
|
The following platforms require compilation of both libvips and sharp from source:
|
||||||
|
|
||||||
|
* macOS ARM64
|
||||||
* Linux x86
|
* Linux x86
|
||||||
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
|
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
|
||||||
* Linux ARM64 (glibc <= 2.28, musl)
|
* 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:
|
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),
|
* 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.
|
* when the `npm install --build-from-source` flag is used.
|
||||||
|
|
||||||
Building from source requires:
|
Building from source requires:
|
||||||
|
File diff suppressed because one or more lines are too long
@ -233,9 +233,10 @@ const Sharp = function (input, options) {
|
|||||||
tiffTileWidth: 256,
|
tiffTileWidth: 256,
|
||||||
tiffXres: 1.0,
|
tiffXres: 1.0,
|
||||||
tiffYres: 1.0,
|
tiffYres: 1.0,
|
||||||
heifQuality: 80,
|
heifQuality: 50,
|
||||||
heifLossless: false,
|
heifLossless: false,
|
||||||
heifCompression: 'hevc',
|
heifCompression: 'av1',
|
||||||
|
heifSpeed: 5,
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
tileOverlap: 0,
|
tileOverlap: 0,
|
||||||
tileContainer: 'fs',
|
tileContainer: 'fs',
|
||||||
|
@ -6,6 +6,7 @@ const sharp = require('../build/Release/sharp.node');
|
|||||||
const formats = new Map([
|
const formats = new Map([
|
||||||
['heic', 'heif'],
|
['heic', 'heif'],
|
||||||
['heif', 'heif'],
|
['heif', 'heif'],
|
||||||
|
['avif', 'avif'],
|
||||||
['jpeg', 'jpeg'],
|
['jpeg', 'jpeg'],
|
||||||
['jpg', 'jpeg'],
|
['jpg', 'jpeg'],
|
||||||
['png', 'png'],
|
['png', 'png'],
|
||||||
@ -556,29 +557,39 @@ function tiff (options) {
|
|||||||
return this._updateFormatOut('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.
|
* Use these HEIF options for output image.
|
||||||
*
|
*
|
||||||
* Support for HEIF (HEIC/AVIF) is experimental.
|
* Support for patent-encumbered HEIC images requires the use of a
|
||||||
* Do not use this in production systems.
|
* globally-installed libvips compiled with support for libheif, libde265 and x265.
|
||||||
*
|
|
||||||
* Requires a custom, globally-installed libvips compiled with support for libheif.
|
|
||||||
*
|
|
||||||
* Most versions of libheif support only the patent-encumbered HEVC compression format.
|
|
||||||
*
|
*
|
||||||
* @since 0.23.0
|
* @since 0.23.0
|
||||||
*
|
*
|
||||||
* @param {Object} [options] - output options
|
* @param {Object} [options] - output options
|
||||||
* @param {number} [options.quality=80] - quality, integer 1-100
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
||||||
* @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
|
* @param {boolean} [options.compression='av1'] - compression format: av1, hevc
|
||||||
* @param {boolean} [options.lossless=false] - use lossless compression
|
* @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}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
function heif (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.object(options)) {
|
||||||
if (is.defined(options.quality)) {
|
if (is.defined(options.quality)) {
|
||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
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.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;
|
this.options.heifCompression = options.compression;
|
||||||
} else {
|
} 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,
|
png,
|
||||||
webp,
|
webp,
|
||||||
tiff,
|
tiff,
|
||||||
|
avif,
|
||||||
heif,
|
heif,
|
||||||
gif,
|
gif,
|
||||||
raw,
|
raw,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
"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 <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://github.com/lovell/sharp",
|
"homepage": "https://github.com/lovell/sharp",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@ -100,6 +100,7 @@
|
|||||||
"jpeg",
|
"jpeg",
|
||||||
"png",
|
"png",
|
||||||
"webp",
|
"webp",
|
||||||
|
"avif",
|
||||||
"tiff",
|
"tiff",
|
||||||
"gif",
|
"gif",
|
||||||
"svg",
|
"svg",
|
||||||
@ -116,10 +117,10 @@
|
|||||||
"array-flatten": "^3.0.0",
|
"array-flatten": "^3.0.0",
|
||||||
"color": "^3.1.3",
|
"color": "^3.1.3",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"node-addon-api": "^3.0.2",
|
"node-addon-api": "^3.1.0",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^6.0.0",
|
"prebuild-install": "^6.0.0",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.4",
|
||||||
"simple-get": "^4.0.0",
|
"simple-get": "^4.0.0",
|
||||||
"tar-fs": "^2.1.1",
|
"tar-fs": "^2.1.1",
|
||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
@ -141,7 +142,7 @@
|
|||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.10.0",
|
"libvips": "8.10.5-alpha2",
|
||||||
"runtime": "napi",
|
"runtime": "napi",
|
||||||
"target": 3
|
"target": 3
|
||||||
},
|
},
|
||||||
|
@ -24,8 +24,10 @@
|
|||||||
|
|
||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10))
|
#if (VIPS_MAJOR_VERSION < 8) || \
|
||||||
#error "libvips version 8.10.0+ is required - please see https://sharp.pixelplumbing.com/install"
|
(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
|
#endif
|
||||||
|
|
||||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||||
|
@ -837,6 +837,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("compression", baton->heifCompression)
|
->set("compression", baton->heifCompression)
|
||||||
->set("Q", baton->heifQuality)
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("speed", baton->heifSpeed)
|
||||||
->set("lossless", baton->heifLossless)));
|
->set("lossless", baton->heifLossless)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@ -885,7 +886,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
bool const isV = sharp::IsV(baton->fileOut);
|
bool const isV = sharp::IsV(baton->fileOut);
|
||||||
bool const mightMatchInput = baton->formatOut == "input";
|
bool const mightMatchInput = baton->formatOut == "input";
|
||||||
bool const willMatchInput = mightMatchInput &&
|
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) ||
|
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
|
||||||
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
|
||||||
@ -966,13 +967,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||||
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
||||||
// Write HEIF to file
|
// Write HEIF to file
|
||||||
if (sharp::IsAvif(baton->fileOut)) {
|
|
||||||
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
|
||||||
}
|
|
||||||
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("Q", baton->heifQuality)
|
->set("Q", baton->heifQuality)
|
||||||
->set("compression", baton->heifCompression)
|
->set("compression", baton->heifCompression)
|
||||||
|
->set("speed", baton->heifSpeed)
|
||||||
->set("lossless", baton->heifLossless));
|
->set("lossless", baton->heifLossless));
|
||||||
baton->formatOut = "heif";
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||||
@ -1395,6 +1394,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
||||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
||||||
sharp::AttrAsStr(options, "heifCompression").data()));
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
||||||
|
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
||||||
|
|
||||||
// Animated output
|
// Animated output
|
||||||
if (sharp::HasAttr(options, "pageHeight")) {
|
if (sharp::HasAttr(options, "pageHeight")) {
|
||||||
|
@ -159,6 +159,7 @@ struct PipelineBaton {
|
|||||||
double tiffYres;
|
double tiffYres;
|
||||||
int heifQuality;
|
int heifQuality;
|
||||||
VipsForeignHeifCompression heifCompression;
|
VipsForeignHeifCompression heifCompression;
|
||||||
|
int heifSpeed;
|
||||||
bool heifLossless;
|
bool heifLossless;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
@ -276,8 +277,9 @@ struct PipelineBaton {
|
|||||||
tiffTileWidth(256),
|
tiffTileWidth(256),
|
||||||
tiffXres(1.0),
|
tiffXres(1.0),
|
||||||
tiffYres(1.0),
|
tiffYres(1.0),
|
||||||
heifQuality(80),
|
heifQuality(50),
|
||||||
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_HEVC),
|
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
||||||
|
heifSpeed(5),
|
||||||
heifLossless(false),
|
heifLossless(false),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
static void* sharp_vips_init(void*) {
|
static void* sharp_vips_init(void*) {
|
||||||
|
g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE);
|
||||||
vips_init("sharp");
|
vips_init("sharp");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
BIN
test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif
vendored
Normal file
BIN
test/fixtures/cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
test/fixtures/expected/tint-cmyk.jpg
vendored
BIN
test/fixtures/expected/tint-cmyk.jpg
vendored
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@ -109,6 +109,7 @@ module.exports = {
|
|||||||
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
|
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
|
||||||
inputSvgSmallViewBox: getPath('circle.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
|
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'),
|
inputJPGBig: getPath('flowers.jpeg'),
|
||||||
|
|
||||||
|
84
test/unit/avif.js
Normal file
84
test/unit/avif.js
Normal file
@ -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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -4,76 +4,65 @@ const assert = require('assert');
|
|||||||
|
|
||||||
const sharp = require('../../');
|
const sharp = require('../../');
|
||||||
|
|
||||||
const formatHeifOutputBuffer = sharp.format.heif.output.buffer;
|
describe('HEIF', () => {
|
||||||
|
it('called without options does not throw an error', () => {
|
||||||
describe('HEIF (experimental)', () => {
|
assert.doesNotThrow(() => {
|
||||||
describe('Stubbed without support for HEIF', () => {
|
sharp().heif();
|
||||||
before(() => {
|
|
||||||
sharp.format.heif.output.buffer = false;
|
|
||||||
});
|
|
||||||
after(() => {
|
|
||||||
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error', () => {
|
|
||||||
assert.throws(() => {
|
|
||||||
sharp().heif();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('valid quality does not throw an error', () => {
|
||||||
describe('Stubbed with support for HEIF', () => {
|
assert.doesNotThrow(() => {
|
||||||
before(() => {
|
sharp().heif({ quality: 80 });
|
||||||
sharp.format.heif.output.buffer = true;
|
|
||||||
});
|
});
|
||||||
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', () => {
|
it('non-numeric quality should throw an error', () => {
|
||||||
assert.doesNotThrow(() => {
|
assert.throws(() => {
|
||||||
sharp().heif();
|
sharp().heif({ quality: 'fail' });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
it('valid quality does not throw an error', () => {
|
});
|
||||||
assert.doesNotThrow(() => {
|
it('valid lossless does not throw an error', () => {
|
||||||
sharp().heif({ quality: 50 });
|
assert.doesNotThrow(() => {
|
||||||
});
|
sharp().heif({ lossless: true });
|
||||||
});
|
});
|
||||||
it('invalid quality should throw an error', () => {
|
});
|
||||||
assert.throws(() => {
|
it('non-boolean lossless should throw an error', () => {
|
||||||
sharp().heif({ quality: 101 });
|
assert.throws(() => {
|
||||||
});
|
sharp().heif({ lossless: 'fail' });
|
||||||
});
|
});
|
||||||
it('non-numeric quality should throw an error', () => {
|
});
|
||||||
assert.throws(() => {
|
it('valid compression does not throw an error', () => {
|
||||||
sharp().heif({ quality: 'fail' });
|
assert.doesNotThrow(() => {
|
||||||
});
|
sharp().heif({ compression: 'hevc' });
|
||||||
});
|
});
|
||||||
it('valid lossless does not throw an error', () => {
|
});
|
||||||
assert.doesNotThrow(() => {
|
it('unknown compression should throw an error', () => {
|
||||||
sharp().heif({ lossless: true });
|
assert.throws(() => {
|
||||||
});
|
sharp().heif({ compression: 'fail' });
|
||||||
});
|
});
|
||||||
it('non-boolean lossless should throw an error', () => {
|
});
|
||||||
assert.throws(() => {
|
it('invalid compression should throw an error', () => {
|
||||||
sharp().heif({ lossless: 'fail' });
|
assert.throws(() => {
|
||||||
});
|
sharp().heif({ compression: 1 });
|
||||||
});
|
});
|
||||||
it('valid compression does not throw an error', () => {
|
});
|
||||||
assert.doesNotThrow(() => {
|
it('valid speed does not throw an error', () => {
|
||||||
sharp().heif({ compression: 'avc' });
|
assert.doesNotThrow(() => {
|
||||||
});
|
sharp().heif({ speed: 6 });
|
||||||
});
|
});
|
||||||
it('unknown compression should throw an error', () => {
|
});
|
||||||
assert.throws(() => {
|
it('out of range speed should throw an error', () => {
|
||||||
sharp().heif({ compression: 'fail' });
|
assert.throws(() => {
|
||||||
});
|
sharp().heif({ speed: 9 });
|
||||||
});
|
});
|
||||||
it('invalid compression should throw an error', () => {
|
});
|
||||||
assert.throws(() => {
|
it('invalid speed should throw an error', () => {
|
||||||
sharp().heif({ compression: 1 });
|
assert.throws(() => {
|
||||||
});
|
sharp().heif({ compression: 'fail' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -302,7 +302,6 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Fail when input is empty Buffer', function (done) {
|
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 () {
|
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
||||||
assert(false);
|
assert(false);
|
||||||
done();
|
done();
|
||||||
|
@ -279,10 +279,10 @@ describe('Image Stats', function () {
|
|||||||
// red channel
|
// red channel
|
||||||
assert.strictEqual(0, stats.channels[0].min);
|
assert.strictEqual(0, stats.channels[0].min);
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0].max, 254, 255));
|
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].sum, 83291370));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11213984832));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11379783198));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 104.36947963892487));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 105.36169496842616));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.379896254993135));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.39412151419967));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
assert.strictEqual(true, isInteger(stats.channels[0].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
assert.strictEqual(true, isInteger(stats.channels[0].minY));
|
||||||
@ -295,10 +295,10 @@ describe('Image Stats', function () {
|
|||||||
// green channel
|
// green channel
|
||||||
assert.strictEqual(0, stats.channels[1].min);
|
assert.strictEqual(0, stats.channels[1].min);
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].max, 254, 255));
|
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].sum, 120877425));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20533721114));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20774687595));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 151.90993361398964));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 152.9072025279307));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.83370206587037));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.84143349689916));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
assert.strictEqual(true, isInteger(stats.channels[1].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
assert.strictEqual(true, isInteger(stats.channels[1].minY));
|
||||||
@ -311,10 +311,10 @@ describe('Image Stats', function () {
|
|||||||
// blue channel
|
// blue channel
|
||||||
assert.strictEqual(0, stats.channels[2].min);
|
assert.strictEqual(0, stats.channels[2].min);
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2].max, 254, 255));
|
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].sum, 138938859));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28172033081));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28449125593));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 174.76123932359133));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 175.75450711423252));
|
||||||
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.38276338513747));
|
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.39929031070358));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
assert.strictEqual(true, isInteger(stats.channels[2].minX));
|
||||||
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024));
|
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024));
|
||||||
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
assert.strictEqual(true, isInteger(stats.channels[2].minY));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user